Files
UnrealEngineUWP/Engine/Source/Editor/BlueprintGraph/Private/K2Node_MathExpression.cpp

2832 lines
95 KiB
C++
Raw Normal View History

// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
#include "K2Node_MathExpression.h"
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3209340) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3209340 on 2016/11/23 by Ben.Marsh Convert UE4 codebase to an "include what you use" model - where every header just includes the dependencies it needs, rather than every source file including large monolithic headers like Engine.h and UnrealEd.h. Measured full rebuild times around 2x faster using XGE on Windows, and improvements of 25% or more for incremental builds and full rebuilds on most other platforms. * Every header now includes everything it needs to compile. * There's a CoreMinimal.h header that gets you a set of ubiquitous types from Core (eg. FString, FName, TArray, FVector, etc...). Most headers now include this first. * There's a CoreTypes.h header that sets up primitive UE4 types and build macros (int32, PLATFORM_WIN64, etc...). All headers in Core include this first, as does CoreMinimal.h. * Every .cpp file includes its matching .h file first. * This helps validate that each header is including everything it needs to compile. * No engine code includes a monolithic header such as Engine.h or UnrealEd.h any more. * You will get a warning if you try to include one of these from the engine. They still exist for compatibility with game projects and do not produce warnings when included there. * There have only been minor changes to our internal games down to accommodate these changes. The intent is for this to be as seamless as possible. * No engine code explicitly includes a precompiled header any more. * We still use PCHs, but they're force-included on the compiler command line by UnrealBuildTool instead. This lets us tune what they contain without breaking any existing include dependencies. * PCHs are generated by a tool to get a statistical amount of coverage for the source files using it, and I've seeded the new shared PCHs to contain any header included by > 15% of source files. Tool used to generate this transform is at Engine\Source\Programs\IncludeTool. [CL 3209342 by Ben Marsh in Main branch]
2016-11-23 15:48:37 -05:00
#include "UObject/UnrealType.h"
#include "UObject/UObjectHash.h"
#include "UObject/UObjectIterator.h"
#include "Engine/MemberReference.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "EdGraphSchema_K2.h"
#include "EdGraphSchema_K2_Actions.h"
#include "K2Node_CallFunction.h"
#include "K2Node_MacroInstance.h"
#include "K2Node_VariableGet.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/Kismet2NameValidators.h"
#include "EdGraphUtilities.h"
#include "BasicTokenParser.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "DiffResults.h"
#include "MathExpressionHandler.h"
#include "BlueprintNodeSpawner.h"
#define LOCTEXT_NAMESPACE "K2Node"
/*******************************************************************************
* Static Helpers
*******************************************************************************/
/**
* Helper function for deleting all the nodes from a specified graph. Does not
* delete any tunnel in/out nodes (to preserve the tunnel).
*
* @param Graph The graph you want to delete nodes in.
*/
static void DeleteGeneratedNodesInGraph(UEdGraph* Graph)
{
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(Graph);
for (int32 NodeIndex = 0; NodeIndex < Graph->Nodes.Num(); )
{
UEdGraphNode* Node = Graph->Nodes[NodeIndex];
if (ExactCast<UK2Node_Tunnel>(Node) != NULL)
{
++NodeIndex;
}
else
{
FBlueprintEditorUtils::RemoveNode(Blueprint, Node, true);
}
}
}
/**
* If the specified type is a "byte" type, then this will modify it to
* an "int". Helps when trying to match function signatures.
*
* @param InOutType The type you want to attempt to promote.
* @return True if the type was modified, false if not.
*/
static bool PromoteByteToInt(FEdGraphPinType& InOutType)
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if (InOutType.PinCategory == Schema->PC_Byte)
{
InOutType.PinCategory = Schema->PC_Int;
InOutType.PinSubCategoryObject = NULL;
return true;
}
return false;
}
/**
* If the specified type is a "int" type, then this will modify it to
* a "float". Helps when trying to match function signatures.
*
* @param InOutType The type you want to attempt to promote.
* @return True if the type was modified, false if not.
*/
static bool PromoteIntToFloat(FEdGraphPinType& InOutType)
{
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
if (InOutType.PinCategory == Schema->PC_Int)
{
InOutType.PinCategory = Schema->PC_Float;
InOutType.PinSubCategoryObject = NULL;
return true;
}
return false;
}
/**
* Sets or clears the error on a specific node. If the ErrorText is empty, then
* it resets the error state on the node. If actual error text is supplied,
* then the node is flagged as having an error, and the string is appended to
* the node's error message.
*
* @param Node The node you wish to modify.
* @param ErrorText The text you want to append to the node's error message (empty if you want to clear any errors).
*/
static void SetNodeError(UEdGraphNode* Node, FText const& ErrorText)
{
if (ErrorText.IsEmpty())
{
Node->ErrorMsg.Empty();
Node->ErrorType = EMessageSeverity::Info;
Node->bHasCompilerMessage = false;
}
else if (Node->bHasCompilerMessage)
{
Node->ErrorMsg += TEXT("\n") + ErrorText.ToString();
Node->ErrorType = EMessageSeverity::Error;
}
else
{
Node->ErrorMsg = ErrorText.ToString();
Node->ErrorType = EMessageSeverity::Error;
Node->bHasCompilerMessage = true;
}
}
/*******************************************************************************
* FExpressionVisitor
*******************************************************************************/
/**
* This is the base class used for IFExpressionNode tree traversal (set up to
* handle different node types... new node types should have a Visit() method
* added for them).
*/
class FExpressionVisitor
{
public:
/**
* FExpressionNodes determine when a traverser (FExpressionVisitor) has access
* to the node. There are a couple hook points, allowing the traverser to pick
* either a pre-order or post-order tree traversal. These values let the
* FExpressionVisitor where we are in the tree search.
*/
enum EVisitPhase
{
VISIT_Pre, // the node being visited has yet to visit its children (and will next, starting with the left)
VISIT_Post, // the node being visited has finished visiting its children (and is about to return up, to its parent)
VISIT_Leaf // the node being visited is a leaf (no children will be visited)
};
/**
* Intended to be overridden by sub-classes, for special handling of
* explicit node types (new node types should have one added for them).
*
* @param Node The current node in the tree's traversal.
* @param Phase Where we currently are in traversing the specified node (before children vs. after)
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Visit(class IFExpressionNode& Node, EVisitPhase Phase) { return VisitUnhandled(Node, Phase); }
virtual bool Visit(class FTokenWrapperNode& Node, EVisitPhase Phase) { return VisitUnhandled((class IFExpressionNode&)Node, Phase); }
virtual bool Visit(class FBinaryOperator& Node, EVisitPhase Phase) { return VisitUnhandled((class IFExpressionNode&)Node, Phase); }
virtual bool Visit(class FUnaryOperator& Node, EVisitPhase Phase) { return VisitUnhandled((class IFExpressionNode&)Node, Phase); }
virtual bool Visit(class FConditionalOperator& Node, EVisitPhase Phase) { return VisitUnhandled((class IFExpressionNode&)Node, Phase); }
virtual bool Visit(class FExpressionList& Node, EVisitPhase Phase) { return VisitUnhandled((class IFExpressionNode&)Node, Phase); }
virtual bool Visit(class FFunctionExpression& Node, EVisitPhase Phase) { return VisitUnhandled((class IFExpressionNode&)Node, Phase); }
protected:
/**
* Called by all the base Visit() methods, a good point for sub-classes to
* hook into for handling EVERY IFExpressionNode type (unless they override
* a Visit method)
*
* @param Node The current node in the tree's traversal.
* @param Phase Where we currently are in traversing the specified node (before children vs. after)
* @return True to continue traversing the tree, false to abort.
*/
virtual bool VisitUnhandled(class IFExpressionNode& Node, EVisitPhase Phase)
{
// if we end up here, then the subclass decided not to handle the
// specific node type, and therefore doesn't care about it
return true;
}
};
/*******************************************************************************
* IFExpressionNode Types
*******************************************************************************/
/**
* Base class for all expression-tree nodes that are generated from parsing an
* expression string. Represents either a single value/variable, or an operation
* on other IFExpressionNode(s).
*/
class IFExpressionNode
{
public:
/**
* Entry point for traversing the expression-tree, should either pass the
* Visitor along to sub child nodes (in the case of a branch node), or
* simply let the Visitor "visit" the leaf node.
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Accept(FExpressionVisitor& Visitor) = 0;
/**
* For debug purposes, intended to help visualize what this node represents
* (for reconstructing a pseudo expression).
*/
virtual FString ToString() const = 0;
/**
* Variable Guid's are stored in the internal expression and must be
* converted back to their name when showing the expression in the node's title
*/
virtual FString ToDisplayString(UBlueprint* InBlueprint) const { return ToString(); };
protected:
IFExpressionNode() {}
};
/**
* Leaf node for the expression-tree. Encapsulates either a literal constant
* (FBasicToken::TOKEN_Const), or a variable identifier (FBasicToken::TOKEN_Identifier).
*/
class FTokenWrapperNode : public IFExpressionNode
{
public:
FTokenWrapperNode(FBasicToken InToken)
: Token(InToken)
{
}
virtual ~FTokenWrapperNode() {}
/**
* Gives the FExpressionVisitor access to this node (lets it "visit" this,
* in tree traversal terms).
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Accept(FExpressionVisitor& Visitor) override
{
return Visitor.Visit(*this, FExpressionVisitor::VISIT_Leaf);
}
/** For debug purposes, constructs a textual representation of this expression */
virtual FString ToString() const override
{
if (Token.TokenType == FBasicToken::TOKEN_Identifier || Token.TokenType == FBasicToken::TOKEN_Guid)
{
return FString::Printf(TEXT("%s"), Token.Identifier);
}
else if (Token.TokenType == FBasicToken::TOKEN_Const)
{
return FString::Printf(TEXT("%s"), *Token.GetConstantValue());
}
else
{
return FString::Printf(TEXT("(UnexpectedTokenType)%s"), Token.Identifier);
}
}
virtual FString ToDisplayString(UBlueprint* InBlueprint) const
{
if (Token.TokenType == FBasicToken::TOKEN_Guid)
{
FGuid VariableGuid;
if(FGuid::Parse(FString(Token.Identifier), VariableGuid))
{
FName VariableName = FBlueprintEditorUtils::FindMemberVariableNameByGuid(InBlueprint, VariableGuid);
if (VariableName.IsNone())
{
VariableName = FBlueprintEditorUtils::FindLocalVariableNameByGuid(InBlueprint, VariableGuid);
}
return VariableName.ToString();
}
}
return ToString();
}
public:
/** The base token which this leaf node represents */
FBasicToken Token;
};
/**
* Branch node that represents a binary operation, where its children are the
* left and right operands:
*
* <operator>
* / \
* <left-expression> <right-expression>
*/
class FBinaryOperator : public IFExpressionNode
{
public:
FBinaryOperator(const FString& InOperator, TSharedRef<IFExpressionNode> InLHS, TSharedRef<IFExpressionNode> InRHS)
: Operator(InOperator)
, LHS(InLHS)
, RHS(InRHS)
{
}
virtual ~FBinaryOperator() {}
/**
* Gives the FExpressionVisitor access to this node, and passes it along to
* traverse the children.
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Accept(FExpressionVisitor& Visitor) override
{
bool bAbort = !Visitor.Visit(*this, FExpressionVisitor::VISIT_Pre);
if (bAbort || !LHS->Accept(Visitor) || !RHS->Accept(Visitor))
{
return false;
}
return Visitor.Visit(*this, FExpressionVisitor::VISIT_Post);
}
/** For debug purposes, constructs a textual representation of this expression */
virtual FString ToString() const override
{
const FString LeftStr = LHS->ToString();
const FString RightStr = RHS->ToString();
return FString::Printf(TEXT("(%s %s %s)"), *LeftStr, *Operator, *RightStr);
}
virtual FString ToDisplayString(UBlueprint* InBlueprint) const
{
const FString LeftStr = LHS->ToDisplayString(InBlueprint);
const FString RightStr = RHS->ToDisplayString(InBlueprint);
return FString::Printf(TEXT("(%s %s %s)"), *LeftStr, *Operator, *RightStr);
}
public:
FString Operator;
TSharedRef<IFExpressionNode> LHS;
TSharedRef<IFExpressionNode> RHS;
};
/**
* Branch node that represents a unary (prefix) operation, where its child is
* the right operand:
*
* <unary-operator>
* \
* <operand-expression>
*/
class FUnaryOperator : public IFExpressionNode
{
public:
FUnaryOperator(const FString& InOperator, TSharedRef<IFExpressionNode> InRHS)
: Operator(InOperator)
, RHS(InRHS)
{
}
virtual ~FUnaryOperator() {}
/**
* Gives the FExpressionVisitor access to this node, and passes it along to
* traverse its child.
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Accept(FExpressionVisitor& Visitor) override
{
bool bAbort = !Visitor.Visit(*this, FExpressionVisitor::VISIT_Pre);
if (bAbort || !RHS->Accept(Visitor))
{
return false;
}
return Visitor.Visit(*this, FExpressionVisitor::VISIT_Post);
}
/** For debug purposes, constructs a textual representation of this expression */
virtual FString ToString() const override
{
const FString RightStr = RHS->ToString();
return FString::Printf(TEXT("(%s%s)"), *Operator, *RightStr);
}
virtual FString ToDisplayString(UBlueprint* InBlueprint) const
{
const FString RightStr = RHS->ToDisplayString(InBlueprint);
return FString::Printf(TEXT("(%s%s)"), *Operator, *RightStr);
}
public:
FString Operator;
TSharedRef<IFExpressionNode> RHS;
};
/**
* Branch node that represents a ternary conditional (if-then-else) operation
* (c ? a : b), where its children are the condition, the "then" expression,
* and the "else" expression:
*
* <conditional-operator>
* / | \
* <condition> <then-exression> <else-expression>
*/
class FConditionalOperator : public IFExpressionNode
{
public:
FConditionalOperator(TSharedRef<IFExpressionNode> InCondition, TSharedRef<IFExpressionNode> InTruePart, TSharedRef<IFExpressionNode> InFalsePart)
: Condition(InCondition)
, TruePart(InTruePart)
, FalsePart(InFalsePart)
{
}
virtual ~FConditionalOperator() {}
/**
* Gives the FExpressionVisitor access to this node, and passes it along to
* traverse the children.
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Accept(FExpressionVisitor& Visitor) override
{
bool bAbort = !Visitor.Visit(*this, FExpressionVisitor::VISIT_Pre);
// @TODO: what about the Condition?
if (bAbort || !TruePart->Accept(Visitor) || !FalsePart->Accept(Visitor))
{
return false;
}
return Visitor.Visit(*this, FExpressionVisitor::VISIT_Post);
}
/** For debug purposes, constructs a textual representation of this expression */
virtual FString ToString() const override
{
const FString ConditionStr = Condition->ToString();
const FString TrueStr = TruePart->ToString();
const FString FalseStr = FalsePart->ToString();
return FString::Printf(TEXT("(%s ? %s : %s)"), *ConditionStr, *TrueStr, *FalseStr);
}
virtual FString ToDisplayString(UBlueprint* InBlueprint) const
{
const FString ConditionStr = Condition->ToDisplayString(InBlueprint);
const FString TrueStr = TruePart->ToDisplayString(InBlueprint);
const FString FalseStr = FalsePart->ToDisplayString(InBlueprint);
return FString::Printf(TEXT("(%s ? %s : %s)"), *ConditionStr, *TrueStr, *FalseStr);
}
public:
TSharedRef<IFExpressionNode> Condition;
TSharedRef<IFExpressionNode> TruePart;
TSharedRef<IFExpressionNode> FalsePart;
};
/**
* Branch node that represents an n-dimentional list of sub-expressions (like
* for vector parameter lists, etc.). Each child is a separate sub-expression:
*
* <list-node>
* / | \
* <sub-expression0> | <sub-expression2>
* |
* <sub-expression1>
*/
class FExpressionList : public IFExpressionNode
{
public:
/**
* Gives the FExpressionVisitor access to this node, and passes it along to
* traverse all children.
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Accept(FExpressionVisitor& Visitor) override
{
bool bAbort = !Visitor.Visit(*this, FExpressionVisitor::VISIT_Pre);
for (TSharedRef<IFExpressionNode> Child : Children)
{
if (bAbort || !Child->Accept(Visitor))
{
return false;
}
}
return Visitor.Visit(*this, FExpressionVisitor::VISIT_Post);
}
/** For debug purposes, constructs a textual representation of this expression */
virtual FString ToString() const override
{
FString AsString("(");
if (Children.Num() > 0)
{
for (TSharedRef<IFExpressionNode> Child : Children)
{
AsString += Child->ToString();
if (Child == Children.Last())
{
AsString += ")";
}
else
{
AsString += ", ";
}
}
}
else
{
AsString += ")";
}
return AsString;
}
virtual FString ToDisplayString(UBlueprint* InBlueprint) const
{
FString AsString("(");
if (Children.Num() > 0)
{
for (TSharedRef<IFExpressionNode> Child : Children)
{
AsString += Child->ToDisplayString(InBlueprint);
if (Child == Children.Last())
{
AsString += ")";
}
else
{
AsString += ", ";
}
}
}
else
{
AsString += ")";
}
return AsString;
}
virtual ~FExpressionList() {}
public:
TArray< TSharedRef<IFExpressionNode> > Children;
};
/**
* Branch node that represents some function (like sin(), cos(), etc.), could
* also represent some structure (conceptually the constructor), like vector,
* rotator, etc. Its child is a single FExpressionList (which wraps all the params).
*/
class FFunctionExpression : public IFExpressionNode
{
public:
FFunctionExpression(FString const& InFuncName, TSharedRef<FExpressionList> InParamList)
: FuncName(InFuncName)
, ParamList(InParamList)
{
}
virtual ~FFunctionExpression() {}
/**
* Gives the FExpressionVisitor access to this node, and passes it along to
* traverse its child.
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool Accept(FExpressionVisitor& Visitor) override
{
bool bAbort = !Visitor.Visit(*this, FExpressionVisitor::VISIT_Pre);
if (bAbort || !ParamList->Accept(Visitor))
{
return false;
}
return Visitor.Visit(*this, FExpressionVisitor::VISIT_Post);
}
/** For debug purposes, constructs a textual representation of this expression */
virtual FString ToString() const override
{
FString const ParamsString = ParamList->ToString();
return FString::Printf(TEXT("(%s%s)"), *FuncName, *ParamsString);
}
virtual FString ToDisplayString(UBlueprint* InBlueprint) const
{
FString const ParamsString = ParamList->ToDisplayString(InBlueprint);
return FString::Printf(TEXT("(%s%s)"), *FuncName, *ParamsString);
}
public:
FString FuncName;
TSharedRef<FExpressionList> ParamList;
};
/*******************************************************************************
* FLayoutVisitor
*******************************************************************************/
/**
* This class is utilized to help layout math expression nodes by traversing the
* expression tree and cataloging each expression node's depth. From the tree's
* depth we can determine the width of the the graph (an where to place each K2 node):
*
* _
* | [_]---[_]
* | /
* height [_]-- [_]--[_]---[_]
* | \ /
* |_ [_]---[_]
*
* ^-------depth/width-------^
*/
class FLayoutVisitor : public FExpressionVisitor
{
public:
/** Tracks the horizontal (depth) placement for each expression node encountered */
TMap<IFExpressionNode*, int32> DepthChart;
/** Tracks the vertical (height) placement for each expression node encountered */
TMap<IFExpressionNode*, int32> HeightChart;
/** Tracks the total height (value) at each depth (key) */
TMap<int32, int32> DepthHeightLookup;
/** */
FLayoutVisitor()
: CurrentDepth(0)
, MaximumDepth(0)
{
}
/**
* Retrieves the total depth (or graph width) of the previously traversed
* expression tree.
*/
int32 GetMaximumDepth() const
{
return MaximumDepth;
}
/**
* Resets this tree visitor so that it can accurately parse another
* expression tree (else the results would stack up).
*/
void Clear()
{
CurrentDepth = 0;
MaximumDepth = 0;
DepthChart.Empty();
HeightChart.Empty();
DepthHeightLookup.Empty();
}
private:
/**
* From the FExpressionVisitor interface, a generic choke point for visiting
* all expression nodes.
*
* @return True to continue traversing the tree, false to abort.
*/
virtual bool VisitUnhandled(class IFExpressionNode& Node, EVisitPhase Phase) override
{
if (Phase == FExpressionVisitor::VISIT_Pre)
{
++CurrentDepth;
MaximumDepth = FMath::Max(CurrentDepth, MaximumDepth);
}
else
{
if (Phase == FExpressionVisitor::VISIT_Post)
{
--CurrentDepth;
}
// else leaf
// CurrentHeight represents how many nodes have already been placed
// at this depth
int32& CurrentHeight = DepthHeightLookup.FindOrAdd(CurrentDepth);
DepthChart.Add(&Node, CurrentDepth);
HeightChart.Add(&Node, CurrentHeight);
// since we just placed another node at this depth, increase the
// height count
++CurrentHeight;
}
// let the tree traversal continue! don't abort it!
return true;
}
private:
int32 CurrentDepth;
int32 MaximumDepth;
};
/*******************************************************************************
* FOperatorTable
*******************************************************************************/
/**
* This class acts as a lookup table for mapping operator strings (like "+",
* "*", etc.) to corresponding functions that can be turned into blueprint
* nodes. It builds itself (so users don't have to add mappings themselves).
*/
class FOperatorTable
{
public:
FOperatorTable() { Rebuild(); }
/**
* Checks to see if there are any functions associated with the specified
* operator.
*/
bool Contains(FString const& Operator) const
{
return LookupTable.Contains(Operator);
}
/**
* Attempts to lookup a function matching the supplied signature (where
* 'Operator' identifies the function's name and 'InputTypeList' defines
* the desired parameters). If one can't be found, it attempts to find a
* match by promoting the input types (like from int to float, etc.)
*
* @param Operator The operator you want to find a function for.
* @param InputTypeList A list of parameter types you want to feed the function.
* @return A pointer to the matching function (if one was found), otherwise nullptr.
*/
UFunction* FindMatchingFunction(FString const& Operator, TArray<FEdGraphPinType> const& InputTypeList) const
{
// make a local copy of the desired input types so that we can promote
// those types as needed
TArray<FEdGraphPinType> ParamTypeList = InputTypeList;
// try to find the function
UFunction* MatchingFunc = FindFunctionInternal(Operator, ParamTypeList);
// if we didn't find a function that matches the supplied function
// signature, then try to promote the parameters (like from int to
// float), and see if we can lookup a function with those types
for (int32 promoterIndex = 0; (promoterIndex < OrderedTypePromoters.Num()) && (MatchingFunc == NULL); ++promoterIndex)
{
FTypePromoter const& PromotionOperator = OrderedTypePromoters[promoterIndex];
// Apply the promotion operator to any values that match
bool bMadeChanges = false;
for (FEdGraphPinType& ParamType : ParamTypeList)
{
bMadeChanges |= PromotionOperator.Execute(ParamType);
}
// since we've promoted some of the params, attempt to find the
// function again (maybe there's one that matches these param types)
if (bMadeChanges)
{
MatchingFunc = FindFunctionInternal(Operator, ParamTypeList);
// if we found a function to match this time around, no need to
// continue with
if (MatchingFunc != NULL)
{
break;
}
}
}
return MatchingFunc;
}
/**
* Flags the specified function as one associated with the supplied
* operator.
*/
void Add(FString const& Operator, UFunction* OperatorFunc)
{
LookupTable.FindOrAdd(Operator).Add(OperatorFunc);
}
/**
* Rebuilds the lookup table, mapping operator strings (like "+" or "*") to
* associated functions (searches through function libraries for operator
* functions).
*/
void Rebuild()
{
LookupTable.Empty();
OrderedTypePromoters.Empty();
// run through all blueprint function libraries and build up a list of
// functions that have good operator info
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
{
UClass* TestClass = *ClassIt;
if (TestClass->IsChildOf(UBlueprintFunctionLibrary::StaticClass()) && (!TestClass->HasAnyClassFlags(CLASS_Abstract)))
{
for (TFieldIterator<UFunction> FuncIt(TestClass, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
{
UFunction* TestFunction = *FuncIt;
if (!TestFunction->HasAnyFunctionFlags(FUNC_BlueprintPure) || (TestFunction->GetReturnProperty() == nullptr))
{
continue;
}
FString FunctionName = TestFunction->GetName();
TArray<FString> const& OperatorAliases = GetOperatorAliases(FunctionName);
// if there are aliases, use those instead of the function's standard name
if (OperatorAliases.Num() > 0)
{
for (FString const& Alias : OperatorAliases)
{
Add(Alias, TestFunction);
}
}
else
{
if (TestFunction->HasMetaData(FBlueprintMetadata::MD_CompactNodeTitle))
{
FunctionName = TestFunction->GetMetaData(FBlueprintMetadata::MD_CompactNodeTitle);
}
else if (TestFunction->HasMetaData(FBlueprintMetadata::MD_DisplayName))
{
FunctionName = TestFunction->GetMetaData(FBlueprintMetadata::MD_DisplayName);
}
Add(FunctionName, TestFunction);
}
}
}
}
FTypePromoter ByteToIntPromoter;
ByteToIntPromoter.BindStatic(&PromoteByteToInt);
OrderedTypePromoters.Add(ByteToIntPromoter);
FTypePromoter IntToFloatPromoter;
IntToFloatPromoter.BindStatic(&PromoteIntToFloat);
OrderedTypePromoters.Add(IntToFloatPromoter);
}
private:
/**
* Attempts to lookup a function matching the supplied signature (where
* 'Operator' identifies the function's name and 'InputTypeList' defines
* the desired parameters into that function).
*
* @param Operator The operator you want to find a function for.
* @param InputTypeList A list of parameter types you want to feed the function.
* @return A pointer to the matching function (if one was found), otherwise nullptr.
*/
UFunction* FindFunctionInternal(FString const& Operator, TArray<FEdGraphPinType> const& InputTypeList) const
{
UFunction* MatchedFunction = nullptr;
FFunctionsList const* OperatorFunctions = LookupTable.Find(Operator);
if (OperatorFunctions != nullptr)
{
UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>();
for (UFunction* TestFunction : *OperatorFunctions)
{
int32 ArgumentIndex = 0;
for (TFieldIterator<UProperty> PropIt(TestFunction); PropIt && (PropIt->PropertyFlags & CPF_Parm); ++PropIt)
{
UProperty* Param = *PropIt;
if (!Param->HasAnyPropertyFlags(CPF_ReturnParm))
{
if (ArgumentIndex < InputTypeList.Num())
{
FEdGraphPinType ParamType;
if (K2Schema->ConvertPropertyToPinType(Param, /*out*/ParamType))
{
FEdGraphPinType const& TypeToMatch = InputTypeList[ArgumentIndex];
if (!K2Schema->ArePinTypesCompatible(TypeToMatch, ParamType))
{
break; // type mismatch
}
}
else
{
break; // function has a non-K2 type as a parameter
}
}
else
{
break; // ran out of arguments; no match
}
++ArgumentIndex;
}
}
if (ArgumentIndex == InputTypeList.Num())
{
// success!
MatchedFunction = TestFunction;
break;
}
}
}
return MatchedFunction;
}
/**
* Here we overwrite and map multiplies names to specific functions (for
* example "MultiplyMultiply_FloatFloat" and "^2" are not the sort of names
* we'd expect a user to input in a mathematical expression). We can
* replace a function name with a single value, or a series of values
* (could setup "asin" and "arcsin" both as aliases for the ASin() method).
*
* @param FunctionName The raw name of the function you're looking to replace (not the friendly name)
* @return A reference to the array of aliases for the specified function (an empty array if none were found).
*/
static TArray<FString> const& GetOperatorAliases(FString const& FunctionName)
{
#define FUNC_ALIASES_BEGIN(FuncName) \
if (FunctionName == FString(TEXT(FuncName))) \
{ \
static TArray<FString> AliasTable; \
if (AliasTable.Num() == 0) \
{
#define ADD_ALIAS(AliasStr) \
AliasTable.Add(TEXT(AliasStr));
#define FUNC_ALIASES_END \
} \
return AliasTable; \
}
FUNC_ALIASES_BEGIN("BooleanAND")
ADD_ALIAS("&&")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("BooleanOR")
ADD_ALIAS("||")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("BooleanXOR")
ADD_ALIAS("^")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("Not_PreBool")
ADD_ALIAS("!")
FUNC_ALIASES_END
// keep the compact node title of "^2" from being the required key
FUNC_ALIASES_BEGIN("Square")
ADD_ALIAS("SQUARE")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("FClamp")
ADD_ALIAS("CLAMP")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("MultiplyMultiply_FloatFloat")
ADD_ALIAS("POWER")
ADD_ALIAS("POW")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("ASin")
// have to add "ASin" back, because this overwrites the function's
// name and we still want it as a viable option
ADD_ALIAS("ASIN")
ADD_ALIAS("ARCSIN")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("ACos")
ADD_ALIAS("ACOS")
ADD_ALIAS("ARCCOS")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("ATan")
ADD_ALIAS("ATAN")
ADD_ALIAS("ARCTAN")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("MakeVector")
ADD_ALIAS("VECTOR")
ADD_ALIAS("VEC")
ADD_ALIAS("VECT")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("MakeVector2D")
ADD_ALIAS("VECTOR2D")
ADD_ALIAS("VEC2D")
ADD_ALIAS("VECT2D")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("MakeRotator")
ADD_ALIAS("ROTATOR")
ADD_ALIAS("ROT")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("MakeTransform")
ADD_ALIAS("TRANSFORM")
ADD_ALIAS("XFORM")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("MakeColor")
ADD_ALIAS("COLOR")
ADD_ALIAS("LINEARCOLOR")
ADD_ALIAS("COLOUR") // long live the empire!
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("RandomFloat")
ADD_ALIAS("RandomFloat")
ADD_ALIAS("RAND")
ADD_ALIAS("RANDOM")
FUNC_ALIASES_END
Copying //UE4/Dev-Blueprints to //UE4/Dev-Main (Source: //UE4/Dev-Blueprints @ 3235800) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3194900 on 2016/11/11 by Ryan.Rauschkolb Fixed issue where Reroute node pins weren't mirroring data properly. #jira UE-33717 Change 3195081 on 2016/11/11 by Dan.Oconnor This @todo was addressed Change 3196368 on 2016/11/14 by Maciej.Mroz Results of FBlueprintNativeCodeGenModule::IsTargetedForReplacement are cashed - optimization (cooking time). Change 3196369 on 2016/11/14 by Maciej.Mroz CompileDisplaysBinaryBackend, CompileDisplaysTextBackend and bDisplaysLayout features (in [Kismet] in Engine.ini) are disabled in commandlets. They slow down BP compilation. Change 3196398 on 2016/11/14 by Ben.Cosh Reworked the tracking of latent and expansion event tracking in the blueprint compiler for use with the blueprint profiler. #Jira - UE-37364 - Crash: PIE with instrumented PlayerPawn_Generic added to AITestbed scene #Proj BlueprintProfiler, KismetCompiler. BlueprintGraph, Engine Change 3196410 on 2016/11/14 by Maciej.Mroz Fixed crash in UK2Node_Knot::PropagatePinTypeFromInput Change 3196852 on 2016/11/14 by Maciej.Mroz Fixed static analysis warning. Change 3196874 on 2016/11/14 by Maciej.Mroz #jira UE-37778 (the issue was already fixed, but it was reintroduced, when EDL support was added). ObjectImport->XObject is not filled prematurelly, so CreateExport is properly called each dynamic class. Change 3197469 on 2016/11/14 by Dan.Oconnor Fix for being able to make Sets and Maps of user defined structs that contained unhashable types (e.g. Rotator) Change 3197703 on 2016/11/14 by Dan.Oconnor Updated documentation comment to reflect current behavior Change 3198167 on 2016/11/15 by Maciej.Mroz Merged 3196582 from Dev-Core UE4 - Changed a check to a warning related to detaching linekrs twice. Seen in nativized BP version of platformer game. Change 3198433 on 2016/11/15 by Ryan.Rauschkolb Fixed Copy/pasting variable nodes hides them from a reference search #UE-31606 Change 3198811 on 2016/11/15 by Maciej.Mroz Fixed Knot node - it will use/propagate the type from input connection, if it's possible (intstead of the type from output connection). Change 3198866 on 2016/11/15 by Maciej.Mroz #jira UE-38578 Fixed infinite loading of DynamicClass in EDL. Change 3199045 on 2016/11/15 by Phillip.Kavan [UE-27402] Fix attached Actor-based Blueprint instance root component relative transform values after reconstruction. change summary: - modified FActorComponentInstanceData's ctor to exclude relative transform properties when caching root component values. #jira UE-27402 Change 3200703 on 2016/11/16 by Mike.Beach Marking the ease node explicitly as pure, which makes it so we can prune it from graphs when it is unused. #jira UE-38453 Change 3201115 on 2016/11/16 by Maciej.Mroz Nativization + EDL: "Dynamic" objects are processed by FAsyncPackage::PostLoadDeferredObjects, so the EInternalObjectFlags::AsyncLoading flag is properly cleared. Change 3201749 on 2016/11/17 by Maciej.Mroz In EDL a package containig a dynamic class has PKG_CompiledIn flag (the same like without EDL). Change 3202577 on 2016/11/17 by Mike.Beach Accounting for a change in our intermediate node mapping - the old list no longer maps expanded nodes to macro instances (instead it maps to the corresponding node in the macro graph), so we had to use a new mapping meant for this. #jira UE-35609 Change 3204803 on 2016/11/18 by Phillip.Kavan [UE-38607] Implicitly turn on Blueprint class nativization for dependencies. change summary: - added a UBlueprint::PostEditChangeProperty() override method to handle this. #jira UE-38607 Change 3204812 on 2016/11/18 by Phillip.Kavan [UE-38580] Implicitly turn on the "nativize" project setting when enabling nativize for any Blueprint class. change summary: - modified UBlueprint::PostEditChangeProperty() to update project packaging settings if necessary #jira UE-38580 Change 3204818 on 2016/11/18 by Phillip.Kavan [UE-38725] Interface class dependencies that are not enabled for nativization will now raise an error during nativized cooks. change summary: - modified FBlueprintNativeCodeGenModule::IsTargetedForReplacement() to check interface class dependencies in addition to parent class dependencies. #jira UE-38725 Change 3204963 on 2016/11/18 by Dan.Oconnor Create a transaction when using UBlueprintFunctionNodeSpawner* directly #jira UE-36439 Change 3206510 on 2016/11/21 by Mike.Beach Adding math-expression aliases for dot and cross functions ("dot" and "cross" respectively). #jira UEBP-71 Change 3206547 on 2016/11/21 by Mike.Beach Exposing GetReflectionVector() to Blueprints. #jira UEBP-70 Change 3206658 on 2016/11/21 by Dan.Oconnor Fix for compile error, digging out authorative class from trash class. Mirror of 3206638 from Odin #jira None Change 3207579 on 2016/11/22 by Mike.Beach No longer enforcing the requirement that game UFunctions have to have a category (making writing of game code easier). #jira UE-18093 Change 3207956 on 2016/11/22 by Phillip.Kavan [UE-38690] Fix a regression in which nested scene component subobjects would no longer be registered after construction of an instance-added component in IWCE. change summary: - modified the IWCE path in SSCSEditor::AddNewComponent() to ensure that any components added as a result of instancing the newly-added component are also registered. - modified AActor::ExecuteConstruction() to ensure that non-scene, native nested component subobjects that might be created as a result of SCS execution are also registered (previously it was only considering non-scene components that were explicitly created by the SCS, or that inherited the creation method of the instance that created it). #jira UE-38690 Change 3208217 on 2016/11/22 by Mike.Beach Modified fix (originally from Ryan.Rauschkolb, CL 3186023): Fixed unable to set struct variable name if name includes space #jira UE-28435 Change 3208347 on 2016/11/22 by Mike.Beach Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints) Change 3208688 on 2016/11/23 by Ben.Cosh Made a minor change that forces debugging references to the PIE world when the play in editor world is changed. This is intended to better handle mutliple game instance/world debugging scenarios. #Jira UE-26386 - Can't hit breakpoints in blueprints for level script for server instances #Proj Engine, UnrealEd Change 3208712 on 2016/11/23 by Ben.Cosh Improved handling of unwired transform struct terminal expression's in the blueprint VM compiler to remove an errant warning. #Jira UE-32401 - "ImportText: Missing opening parenthesis" message, when a function returns Transform #Proj KismetCompiler Change 3209457 on 2016/11/23 by Phillip.Kavan [UE-30479] Fix inability to edit the ISMC instance array on Actor instances when the ISMC is inherited from a Blueprint class. change summary: - added PPF_ForceTaggedSerialization as a means to override the CPF_SkipSerialization flag when explicit serialization of the property value is needed - modified UProperty::ShouldSerializeValue() to check for and handle the PPF_ForceTaggedSerialization flag when the CPF_SkipSerialization flag is present - modified UActorComponent::DetermineUCSModifiedProperties() to add the PPF_ForceTaggedSerialization flag to the custom FArchive impl - modified FActorComponentInstanceData::FActorComponentInstanceData() to add the PPF_ForceTaggedSerialization flag to the custom FObjectWriter impl - modified FActorComponentInstanceData::ApplyToComponent() to add the PPF_ForceTaggedSerialization flag to the custom FObjectReader impl #jira UE-30479 Change 3209758 on 2016/11/24 by Maciej.Mroz #jira UE-38979 Nativization: fixed error when a BP implements a native interface. FBlueprintNativeCodeGenModule::IsTargetedForReplacement will return "DontReplace" for native class. Change 3210376 on 2016/11/25 by Maciej.Mroz #jira UE-39028 Fixed FBlueprintNativeCodeGenModule::FindReplacedNameAndOuter Components in nativized BPCG SCS have replaced outer object and name while cooking. Change 3210936 on 2016/11/28 by Phillip.Kavan Minor revision to try and avoid a potentially expensive Contains() call when possible. Change 3211527 on 2016/11/28 by Maciej.Mroz Fixed map of names cooked in packages in nativized build. Change 3211969 on 2016/11/28 by Mike.Beach Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints) Change 3212328 on 2016/11/28 by Dan.Oconnor Enum, pointer and arithmetic specializations for THasGetTypeHash, as VC doesn't detect them properly. TIsEnum moved to its own header. Submitted on behalf of steve.robb Change 3212398 on 2016/11/28 by Dan.Oconnor Build fix, this function is part of another change Change 3212442 on 2016/11/28 by Dan.Oconnor UHT now supports structs in TMap and TSet, misc. fixes to PropertyStruct's PropertyFlags detecting whether the struct type is hashable (all HasGetTypeHash flags are now computed from THasGetTypeHash). Various fixes for generating TMap/TSet code from blueprints Change 3212578 on 2016/11/28 by Dan.Oconnor Rename RegisterClass to avoid collsion with RegistClass macro in generated code Change 3213668 on 2016/11/29 by Dan.Oconnor Fix for missing CDO when instatiating some subobjects in nativized BPs #jira UE-34980 Change 3213737 on 2016/11/29 by Dan.Oconnor Added GetTypeHash implementation to UEnumProperty #jira UE-39091 Change 3215225 on 2016/11/30 by Maciej.Mroz Bunch of changes required for nativized Orion to work with EDL. - ClientOnly, ServerOnly and EditorOnly assets are properly distinguished and handled - Introduced FCompilerNativizationOptions - fixed inproper references to BP instead of BPGC - fixed generated delegate name - hack for DefaultRootNode UE-39168 - improved NativeCodeGenrationTool - various minor improvements Change 3216363 on 2016/11/30 by Dan.Oconnor Better fix for discrepency between UUserDefinedEnum::GetEnumText and UEnum::GetEnumText. Without meta data we could not look up display names, so I'm writing out the display names into a function in the BP cpp backend. This function could be generated by UHT if we wanted to correct this odd behavior for native enums #jira UE-27735 Change 3217168 on 2016/12/01 by Maciej.Mroz #jira UE-35390 Nativization: Fixed compilation warning C4458: declaration of 'CurrentState' hides class member Disabled warning C4996 (deprecated) in nativized code. Change 3217320 on 2016/12/01 by Phillip.Kavan [UE-38652] Selecting Blueprint assets for nativization is now integrated into the project's configuration. change summary: - added EProjectPackagingBlueprintNativizationMethod - deprecated the 'bNativizeBlueprintAssets' and 'bNativizeOnlySelectedBlueprints' flags in favor of a new 'BlueprintNativizationMethod' config property in UProjectPackagingSettings - added a new 'NativizeBlueprintAssets' config property to UProjectPackagingSettings to track/store the list of Blueprints to be nativized when the exclusive method (whitelist) is selected - added a PostInitProperties() override to UProjectPackagingSettings; implemented to migrate from the deprecated config properties to the new method enum type - updated FMainFrameActionCallbacks::PackageProject() to migrate to checking the enum type - updated FBlueprintNativeCodeGenModule::IsTargetedForReplacement() to migrate to checking the enum type - modified UProjectPackagingSettings::CanEditChange() to enable editing of the nativization whitelist only when the "exclusive" method is active - modified UProjectPackagingSettings::PostEditChangeProperty() to add a new case for handling changes to the whitelist; changes are propagated to any Blueprint assets that are loaded - added new Add/RemoveBlueprintAssetFromNativizationList() APIs to UProjectPackagingSettings for assisting with adding/removing Blueprint assets to/from the exclusive list (whitelist) - deprecated the 'bNativize' flag in UBlueprint in favor of a new transient 'bSelectedForNativization' flag that is no longer serialized; this now caches whether or not the asset is present in the whitelist in the project config - modified UBlueprint::Serialize() to both migrate from the deprecated flag to the project config/transient flag on load, as well as to propagate the value of the transient flag back to the project config on save. this means that if the user changes the value of the transient flag through the Details view, that change won't be reflected back to the project config until the Blueprint is actually saved (saving the value to the config rather than serializing to the asset) - modified UBlueprint::PostEditChangeProperty() to remove code that was previously updating the project configuration at edit time. this functionality has been relocated to Serialize() (save time) instead. notes: - also completes UE-38636 (task: consolidate config options into a single drop-down) #jira UE-38652 Change 3218124 on 2016/12/01 by Dan.Oconnor CPF_HasGetValueTypeHash flag was not set on native UEnumProperties #jira UE-39181 Change 3218168 on 2016/12/01 by Phillip.Kavan Fix local var name that shadows a function param (CIS fix). Change 3219117 on 2016/12/02 by Maciej.Mroz #jira UE-39241 "warning C4458: declaration of XXX hides class member" In Nativized Code Nativization: Fixed compilation warning C4458: when local function variable hides a class variable. Names of local variables in funvtions have prefix "bpfv__". Change 3219201 on 2016/12/02 by Mike.Beach Keeping the "Select All Input Nodes" option from infinitely recurssing by blocking on nodes it has already selected. #jira UE-38988 Change 3219247 on 2016/12/02 by Mike.Beach Fixing CIS shadow variable warning from my last check in (CL 3219201). Change 3219332 on 2016/12/02 by Maciej.Mroz #jira HeaderParser: "private:" specifier is lost in FGameplayTag::TagName Workaround for UE-38231 Change 3219381 on 2016/12/02 by Mike.Beach Accounting for cyclic compile issues in cast-node's validate function, making it check the authoratative class instead of the current type. Also down grading some of the warnings to notes (suggesting the users don't need the cast). #jira UE-39272 Change 3220224 on 2016/12/02 by Dan.Oconnor Reduce font size for compact nodes Change 3220476 on 2016/12/03 by Maciej.Mroz #jira UE-35390 Better fix for UE-35390 Disabled deprecation warnings in nativized code. Change 3221637 on 2016/12/05 by Maciej.Mroz #jira UEBP-245 Nativization: Forced ImportCreation while InitialLoad for DynamicClasses. Change 3222306 on 2016/12/05 by Dan.Oconnor Support for default values of TMap and TSet local variables #jira UE-39239 Change 3222383 on 2016/12/05 by Dan.Oconnor Fixed bug in Blueprint TMap function for getting values out of a TMap Change 3222427 on 2016/12/05 by Mike.Beach Preventing ChildActorTemplate fixups from occuring on component load, when they may instead be a placeholder object (a byproduct of deferred Blueprint loading - a guard against cyclic load problems). #jira UE-39323 Change 3222679 on 2016/12/05 by Dan.Oconnor Remove unused code. Had no sideeffects, other than potential for leak when struct with non-trivial dtor was allocated here. Change 3222719 on 2016/12/05 by Dan.Oconnor Earlier detection of invalid native map/set properties. These generate a compile error if any TMap/TSet functions are used, but will slip by undetected if not used. Working on static assert to catch them. #jira UE-39338 Change 3224375 on 2016/12/06 by Dan.Oconnor Add tags for testing of array diffing Change 3224507 on 2016/12/07 by Phillip.Kavan [UE-39055] Fix a crash caused by an object name collision that could occur when loading some older Blueprint assets. change summary: - added UInheritableComponentHandler::FixComponentTemplateName() - modified UInheritableComponentHandler::PostLoad() to check for and correct stale records that cause a collision with the corrected name - added a UInheritableComponentHandler::Serialize() override to ensure that UsingCustomVersion() is called for the asset containing the ICH (already happening in UBlueprint::Serialize(), but added for consistency with other usage) - modified USimpleConstructionScript::Serialize() to ensure that UsingCustomVersion() is called for the asset containing the SCS (same reason as above) #jira UE-39055 Change 3225572 on 2016/12/07 by Samuel.Proctor Test assets for TSet/TMap testing. Includes new native class for testing containers. #rb none Change 3225577 on 2016/12/07 by Samuel.Proctor New test map for Array, TSet and Tmap testing. Change 3226281 on 2016/12/07 by Dan.Oconnor Container test asset, needs to be in p4 for diff tool tests. Change 3226345 on 2016/12/07 by Dan.Oconnor Another revision of test data Change 3228496 on 2016/12/09 by Ben.Cosh This change adds extra information to component template arrays so that the component class can be determined in builds that strip out objects of certain class types such as the editor dedicated server build. #Jira UE-38842 - "LogBlueprint:Error: [Compiler BP_Skybox_World_RandomTrees_01] Error Can't connect pins ReturnValue and Target" after entering a lobby in a synced server #Proj KismetCompiler, BlueprintGraph, UnrealEd, Core, Engine, Kismet, BlueprintCompilerCppBackend Change 3230120 on 2016/12/09 by Dan.Oconnor Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints) Change 3230700 on 2016/12/12 by Samuel.Proctor Removing some array test properties from container test class that were not needed. Also updated struct element to better reflect testing purpose. #rb none Change 3230926 on 2016/12/12 by Samuel.Proctor Missed a file on previous container test native QA asset checkin. #rb none Change 3231246 on 2016/12/12 by Dan.Oconnor PR #3003: New Feature: In-editor diff of arrays and structs now highlights diff. (Contributed by CA-ADuran). I've added TSet and TMap support as well. Change 3231311 on 2016/12/12 by Dan.Oconnor Handle class load failure #jira UE-39480 Change 3231387 on 2016/12/12 by Dan.Oconnor Shadow variable fixes Change 3231501 on 2016/12/12 by Dan.Oconnor More shadow fixes Change 3231584 on 2016/12/12 by Maciej.Mroz #jira UE-39636 Replaced obsolate macro usage. #fyi Dan.Oconnor Change 3231685 on 2016/12/12 by Mike.Beach PR #3032: Fixed category for IsValidIndex (Contributed by elFarto) #jira UE-39660 Change 3231689 on 2016/12/12 by Maciej.Mroz Nativization: Fixed Dependency list generation. Change 3231765 on 2016/12/12 by Phillip.Kavan [UE-38903] Auto-enable exclusive Blueprint nativization only after explicitly selecting the first asset. change summary: - fixed up the auto-enable logic on save in UBlueprint::Serialize() #jira UE-38903 #fyi Maciej.Mroz Change 3231837 on 2016/12/12 by Dan.Oconnor Restore hack to keep objects referenced by bytecode alive while in editor #jira UE-38486 Change 3232085 on 2016/12/13 by Phillip.Kavan Compile fix. Change 3232435 on 2016/12/13 by Ben.Cosh Fix for a bug introduced in CL 3228496 that caused component templates to fail to be identified by name and resulted in blueprint compilation issues for add component nodes. #Jira UE-39623 - Unknown template referenced by Add Component Node #Proj BlueprintGraph, Engine Change 3232437 on 2016/12/13 by Maciej.Mroz #jira UE-33021 Remove an obsolete warning. Change 3232564 on 2016/12/13 by Ben.Cosh This adds extra component template renaming propagation and checking for the blueprint generated class and blueprint skeleton class. #Jira UE-39623 - Unknown template referenced by Add Component Node #Proj BlueprintGraph - Implementing a bit of review feedback and some safety checking. Change 3232598 on 2016/12/13 by Maciej.Mroz Nativization: stati functions cannot be const, so no workaound (_Inner function) specyfic to const functions is necessary #jira UE-39518 Change 3232601 on 2016/12/13 by Phillip.Kavan [UE-38975] Warn on BuildCookRun or a standalone cook when the -nativizeAssets flag is omitted from the command line for a nativization-enabled project. change summary: - added 'bWarnIfPackagedWithoutNativizationFlag' to UProjectPackagingSettings (default = true) - modified UCookOnTheFlyServer::StartCookByTheBook() to check for the presence of the -nativizeAssets flag and emit a warning for unexpected omission from the command line - modified UAT to include a warning for the BuildCookRun command when -build is specified with the same unexpected omission of the -nativizeAssets flag on the UAT command line #jira UE-38975 Change 3232749 on 2016/12/13 by Mike.Beach Moving Blueprint nativization out of the experimental category. #jira UE-39358 Change 3233043 on 2016/12/13 by Dan.Oconnor Various fixes for TSet/TMap nativization issues #jira UE-39634 Change 3233086 on 2016/12/13 by Dan.Oconnor Advanced Containers (TSet/TMap) no longer experimental Change 3233175 on 2016/12/13 by Mike.Beach Whitelising "Packaging" as an acceptable BP settings category (follow up to CL 3232749). #jira UE-39358 Change 3233182 on 2016/12/13 by Mike.Beach Exposing the editor setting "Show Action Menu Item Signatures" through the Blueprint Developer menu (for ease of access). Change 3233662 on 2016/12/13 by Phillip.Kavan [UE-39722] Fix a typo that led to a UAT runtime failure. #jira UE-39722 Change 3233710 on 2016/12/13 by Dan.Oconnor Clang template useage fix #jira UE-39742 Change 3233895 on 2016/12/13 by Dan.Oconnor Several fixes to crashes that occur when you delete a blueprint asset when its children are not loaded. #jira UE-39558 Change 3234443 on 2016/12/14 by Phillip.Kavan [UE-39354] Fix script VM crash on assignment to TSet/TMap variables. change summary: - modified FScriptBuilderBase::EmitDestinationExpression() to consider TSet/TMap value types in addition to TArray terms #jira UE-39354 Change 3234581 on 2016/12/14 by Mike.Beach Backing out fix for UE-38842 (CL 3228496/3232435/3232564) - mapping UBlueprintGeneratedClass's ComponentTemplates array to a new format was causing issues with deferred dependency loading during serialization (trying to extract type information from a placeholder object). We're opting for a smaller/simpler solution to UE-38842, which will be to store the component information on the node itself (not with the templates). #jira UE-39707 Change 3234729 on 2016/12/14 by Mike.Beach Making it so AddComponent nodes now track the component (class) type that they represent (in case the template cannot be spawned, like in -server w/ client-only components). #jira UE-38842 Change 3234805 on 2016/12/14 by Mike.Beach Fixing CIS shadowed variable warning. Change 3234830 on 2016/12/14 by Nick.Atamas Added extra debugging mechanisms to help track down duplicate item issues with TableViews. Change 3235075 on 2016/12/14 by Mike.Beach Creating a helper to better manage nested scope blocks added in generated code - on close, clears out cached local accessor variables that were added, so we don't use one that was declared inside the nested scope. #jira UE-39769 Change 3235213 on 2016/12/14 by Phillip.Kavan [UE-39790] Fix UAT compile issue after latest merge from Main. change summary: - migrated the BuildCookRun command's usage of the (deprecated) ConfigCacheIni to the new ConfigHierarchy API #jira UE-39790 Change 3235384 on 2016/12/14 by Mike.Beach Defaulting to excluding data-only Blueprints from nativization. Change 3235675 on 2016/12/14 by Nick.Atamas Hopefully fixed build. Added OnEnteredBadState delegate that lets users add arbitrary logging info when the List/Tree enters a bad state. Change 3235761 on 2016/12/14 by Mike.Beach Hopefully resolving CIS mac/ps4 build failures in Dev-BP for 4.15 integration. #jira UE-39800 Change 3235800 on 2016/12/14 by Mike.Beach More hopeful CIS mac/ps4 fixes for 4.15 integration. #jira UE-39800 [CL 3236017 by Mike Beach in Main branch]
2016-12-14 22:10:20 -05:00
FUNC_ALIASES_BEGIN("Dot_VectorVector")
ADD_ALIAS("Dot")
FUNC_ALIASES_END
FUNC_ALIASES_BEGIN("Cross_VectorVector")
ADD_ALIAS("Cross")
FUNC_ALIASES_END
// if none of the above aliases returned, then we don't have any for
// this function (use its regular name)
static TArray<FString> NoAliases;
return NoAliases;
#undef FUNC_ALIASES_END
#undef ADD_ALIAS
#undef FUNC_ALIASES_END
}
private:
/**
* A single operator can have multiple functions associated with it; usually
* for handling different types (int*int, vs. int*vector), hence this array.
*/
typedef TArray<UFunction*> FFunctionsList;
/**
* A lookup table, mapping operator strings (like "+", "*", etc.) to a list
* of associated functions.
*/
TMap<FString, FFunctionsList> LookupTable;
/**
* When looking to match parameters, there are some implicit conversions we
* can make to try and find a match (like converting from int to float).
* This holds an ordered list of delegates that will try and promote the
* supplied types.
*/
DECLARE_DELEGATE_RetVal_OneParam(bool, FTypePromoter, FEdGraphPinType&);
TArray<FTypePromoter> OrderedTypePromoters;
};
/*******************************************************************************
* FCodeGenFragments
*******************************************************************************/
/**
* FCodeGenFragments facilitate the making of pin connections/defaults. When
* turning an expression tree into a network of UK2Nodes, you traverse the tree,
* working backwards from the expression's result node. This means that when you
* spawn a UK2Node, you don't have the node (or literals) that should be plugged
* into it, that is why these fragments are created (to track the associated
* UK2Node/literal, and provide an easy interface for connecting it later with
* other fragments/nodes).
*/
class FCodeGenFragment
{
public:
FCodeGenFragment(FEdGraphPinType InType)
: FragmentType(InType)
{
}
/**
* Takes the input to some other fragment, and plugs the result of this one
* into it.
*
* @param InputPin Either an input into some parent expression, or the final output for the entire math expression.
* @return True is the connection was made, otherwise false.
*/
virtual bool ConnectToInput(UEdGraphPin* InputPin, FCompilerResultsLog& MessageLog) = 0;
/**
* As it stands, all the math nodes/literals that can be generated have a
* singular output (hence why we have a basic "connect this fragment to an
* input" function). This message retrieves that output type.
*
* @return The pin type of this fragment's output.
*/
FEdGraphPinType const& GetOutputType() const
{
return FragmentType;
}
protected:
/**
* Utility method for sub-classes to use when attempting a connection
* between two pins. Tries to connect two pins, verifying the type/etc, and
* reporting a failure if there is one.
*
* @param OutputPin The output pin (probably from this fragment).
* @param InputPin The input pin (probably from some other fragment).
* @return True if the connection was made, false if the pins weren't compatible.
*/
bool SafeConnectPins(UEdGraphPin* OutputPin, UEdGraphPin* InputPin, FCompilerResultsLog& MessageLog)
{
const UEdGraphSchema* Schema = InputPin->GetSchema();
bool bSuccess = Schema->TryCreateConnection(OutputPin, InputPin);
if (!bSuccess)
{
MessageLog.Error(*LOCTEXT("PinsNotCompatible", "Output pin '@@ 'is not compatible with input: '@@'").ToString(),
OutputPin, InputPin);
}
return bSuccess;
}
private:
/** All fragments have a singular output type, this is considered the fragment's type as a whole. */
FEdGraphPinType FragmentType;
};
/**
* If the user uses a variable name that already exists in the blueprint, then
* we use that instead of adding an extra input. This fragment wraps a
* VariableGet node that was generated in that scenario.
*/
class FCodeGenFragment_VariableGet : public FCodeGenFragment
{
public:
FCodeGenFragment_VariableGet(UK2Node_VariableGet* InNode, FEdGraphPinType const& InType)
: FCodeGenFragment(InType)
, GeneratedNode(InNode)
{
check(GeneratedNode != nullptr);
}
virtual ~FCodeGenFragment_VariableGet() {}
/// Begin FCodeGenFragment Interface
virtual bool ConnectToInput(UEdGraphPin* InputPin, FCompilerResultsLog& MessageLog) override
{
bool bSuccess = false;
if (UEdGraphPin* VariablePin = GeneratedNode->FindPin(GeneratedNode->VariableReference.GetMemberName().ToString()))
{
bSuccess = SafeConnectPins(VariablePin, InputPin, MessageLog);
}
else
{
FText ErrorText = FText::Format(LOCTEXT("NoVariablePin", "Failed to find the '{0}' pin for: '@@'"),
FText::FromName(GeneratedNode->VariableReference.GetMemberName()));
MessageLog.Error(*ErrorText.ToString(), GeneratedNode);
}
return bSuccess;
}
/// End FCodeGenFragment Interface
private:
UK2Node_VariableGet* GeneratedNode;
};
/**
* All operators in the mathematical expression correspond to library functions,
* which in turn generate CallFunction nodes. This fragment wraps one of those
* operation nodes and connects it with the given input (when prompted to).
*/
class FCodeGenFragment_FuntionCall : public FCodeGenFragment
{
public:
FCodeGenFragment_FuntionCall(UK2Node_CallFunction* InNode, FEdGraphPinType const& InType)
: FCodeGenFragment(InType)
, GeneratedNode(InNode)
{
check(GeneratedNode != nullptr);
}
virtual ~FCodeGenFragment_FuntionCall() {}
/// Begin FCodeGenFragment Interface
virtual bool ConnectToInput(UEdGraphPin* InputPin, FCompilerResultsLog& MessageLog) override
{
bool bSuccess = false;
if (UEdGraphPin* ResultPin = GeneratedNode->GetReturnValuePin())
{
bSuccess = SafeConnectPins(ResultPin, InputPin, MessageLog);
}
else
{
MessageLog.Error(*LOCTEXT("NoRetValPin", "Failed to find an output pin for: '@@'").ToString(),
GeneratedNode);
}
return bSuccess;
}
/// End FCodeGenFragment Interface
private:
UK2Node_CallFunction* GeneratedNode;
};
/**
* This fragment doesn't have a corresponding UK2Node, instead it represents a
* constant value that should be entered into another node's input field. When
* "connected", it modifies the connecting pin's DefaultValue.
*/
class FCodeGenFragment_Literal : public FCodeGenFragment
{
public:
FCodeGenFragment_Literal(FString const& LiteralVal, FEdGraphPinType const& ResultType)
: FCodeGenFragment(ResultType)
, DefaultValue(LiteralVal)
{}
virtual ~FCodeGenFragment_Literal() {}
/// Begin FCodeGenFragment Interface
virtual bool ConnectToInput(UEdGraphPin* InputPin, FCompilerResultsLog& MessageLog) override
{
UEdGraphSchema_K2 const* K2Schema = Cast<UEdGraphSchema_K2>(InputPin->GetSchema());
bool bSuccess = true;//K2Schema->ArePinTypesCompatible(GetOutputType(), InputPin->PinType);
if (bSuccess)
{
InputPin->DefaultValue = DefaultValue;
}
else
{
FText ErrorText = FText::Format(LOCTEXT("LiteralNotCompatible", "Literal type ({0}) is incompatible with pin: '@@'"),
FText::FromString(GetOutputType().PinCategory));
MessageLog.Error(*ErrorText.ToString(), InputPin);
}
return bSuccess;
}
/// End FCodeGenFragment Interface
private:
FString DefaultValue;
};
/**
* This fragment corresponds to an input pin that was added to the
* MathExpression node. Input pins are generated when the user enters variable
* names (like "x", or "y"... ones that aren't variables on the blueprint).
*/
class FCodeGenFragment_InputPin : public FCodeGenFragment
{
public:
FCodeGenFragment_InputPin(UEdGraphPin* InTunnelInputPin)
: FCodeGenFragment(InTunnelInputPin->PinType)
, TunnelInputPin(InTunnelInputPin)
{
}
virtual ~FCodeGenFragment_InputPin() {}
/// Begin FCodeGenFragment Interface
virtual bool ConnectToInput(UEdGraphPin* InputPin, FCompilerResultsLog& MessageLog) override
{
return SafeConnectPins(TunnelInputPin, InputPin, MessageLog);
}
/// End FCodeGenFragment Interface
private:
UEdGraphPin* TunnelInputPin;
};
/*******************************************************************************
* FMathGraphGenerator
*******************************************************************************/
/**
* Takes the root of an expression tree and instantiates blueprint nodes/pins
* for the specified UK2Node_MathExpression (which is a tunnel node, similar to
* how collapsed composite nodes work).
*/
class FMathGraphGenerator : public FExpressionVisitor
{
public:
FMathGraphGenerator(UK2Node_MathExpression* InNode)
: CompilingNode(InNode)
, TargetBlueprint(FBlueprintEditorUtils::FindBlueprintForGraphChecked(InNode->BoundGraph))
, ActiveMessageLog(nullptr)
{
}
/**
* Takes an expression tree and converts expression nodes into UK2Nodes,
* connecting them, and adding them under the math expression node that
* this was instantiated with.
*
* @param ExpressionRoot The root of the expression tree that you want converted into a UK2Node network.
*/
bool GenerateCode(TSharedRef<IFExpressionNode> ExpressionRoot, FCompilerResultsLog& MessageLog)
{
ActiveMessageLog = &MessageLog;
// want to track if we generated any errors from this pass, so we need to know how many we started with
int32 StartingErrorCount = MessageLog.NumErrors;
InputPinNames.Empty();
LayoutMapper.Clear();
// map the depth/height of expression tree (so we can position nodes prettily)
ExpressionRoot->Accept(LayoutMapper);
// reset the bounds tracking, so we can adjust it as we spawn nodes
GraphXBounds.X = +LayoutMapper.GetMaximumDepth();
GraphXBounds.Y = -LayoutMapper.GetMaximumDepth();
// traverse the expression tree, spawning nodes as we go along
ExpressionRoot->Accept(*this);
UK2Node_Tunnel* EntryNode = CompilingNode->GetEntryNode();
UK2Node_Tunnel* ExitNode = CompilingNode->GetExitNode();
TSharedPtr<FCodeGenFragment> RootFragment = CompiledFragments.FindRef(&ExpressionRoot.Get());
if (RootFragment.IsValid())
{
// connect the final node of the expression with the math-node's output
UEdGraphPin* ReturnPin = ExitNode->CreateUserDefinedPin(TEXT("ReturnValue"), RootFragment->GetOutputType(), EGPD_Input);
if (!RootFragment->ConnectToInput(ReturnPin, MessageLog))
{
MessageLog.Error(*LOCTEXT("ResultConnectError", "Failed to connect the generated nodes with expression's result pin: '@@'").ToString(),
ReturnPin);
}
}
else
{
MessageLog.Error(*LOCTEXT("NoGraphGenerated", "No root node generated from the expression: '@@'").ToString(),
CompilingNode);
}
// position the entry and exit nodes somewhere sane
{
const FVector2D EntryPos = GetNodePosition(GraphXBounds.X - 1, 0);
EntryNode->NodePosX = EntryPos.X;
EntryNode->NodePosY = EntryPos.Y;
const FVector2D ExitPos = GetNodePosition(GraphXBounds.Y + 1, 0);
ExitNode->NodePosX = ExitPos.X;
ExitNode->NodePosY = ExitPos.Y;
}
bool bHasErrors = ((MessageLog.NumErrors - StartingErrorCount) > 0);
ActiveMessageLog = nullptr;
return !bHasErrors;
}
/**
* When the node gen is over, we need to clear any old pins that weren't
* reused. This query method helps in identifying those that were utilized.
*
* @return True if the pin's name was used in the most recent expression, false if not.
*/
bool IsPinInUse(TSharedPtr<FUserPinInfo> PinInfo)
{
return (InputPinNames.Find(PinInfo->PinName) != INDEX_NONE);
}
/**
* Overloaded, part of the FExpressionVisitor interface; attempts to
* generate either a vaiable-get node, an input pin, or a literal fragment
* from the supplied FTokenWrapperNode (all depends on the token's type).
*
* @return True to continue travesing the expression tree, false to stop.
*/
virtual bool Visit(FTokenWrapperNode& ExpressionNode, EVisitPhase Phase) override
{
check(ActiveMessageLog != nullptr);
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
if (ExpressionNode.Token.TokenType == FBasicToken::TOKEN_Identifier || ExpressionNode.Token.TokenType == FBasicToken::TOKEN_Guid)
{
FString const VariableIdentifier = ExpressionNode.Token.Identifier;
// first we try to match up variables with existing variable properties on the blueprint
FMemberReference VariableReference;
FString VariableName;
FGuid VariableGuid;
if (ExpressionNode.Token.TokenType == FBasicToken::TOKEN_Guid && FGuid::Parse(VariableIdentifier, VariableGuid))
{
// First look the variable up as a Member variable
FName VariableFName;
VariableFName = FBlueprintEditorUtils::FindMemberVariableNameByGuid(TargetBlueprint, VariableGuid);
// If the variable was not found, look it up as a local variable
if (VariableFName.IsNone())
{
VariableFName = FBlueprintEditorUtils::FindLocalVariableNameByGuid(TargetBlueprint, VariableGuid);
VariableReference.SetLocalMember(VariableFName, CompilingNode->GetGraph()->GetName(), VariableGuid);
}
else
{
VariableReference.SetSelfMember(VariableFName);
}
VariableName = VariableFName.ToString();
}
else
{
VariableName = VariableIdentifier;
// First look the variable up as a Member variable
VariableGuid = FBlueprintEditorUtils::FindMemberVariableGuidByName(TargetBlueprint, FName(*VariableName));
// If the variable was not found, look it up as a local variable
if (!VariableGuid.IsValid())
{
VariableGuid = FBlueprintEditorUtils::FindLocalVariableGuidByName(TargetBlueprint, CompilingNode->GetGraph(), FName(*VariableName));
if (VariableGuid.IsValid())
{
VariableReference.SetLocalMember(FName(*VariableName), CompilingNode->GetGraph()->GetName(), VariableGuid);
}
}
else
{
VariableReference.SetSelfMember(FName(*VariableName));
}
// If we found a valid Guid, change the expression's identifier to be the Guid
if (VariableGuid.IsValid())
{
FCString::Strncpy(ExpressionNode.Token.Identifier, *VariableGuid.ToString(EGuidFormats::DigitsWithHyphensInBraces), NAME_SIZE);
ExpressionNode.Token.TokenType = FBasicToken::TOKEN_Guid;
}
}
if (UProperty* VariableProperty = VariableReference.ResolveMember<UProperty>(TargetBlueprint))
{
TSharedPtr<FCodeGenFragment_VariableGet> VariableGetFragment = GeneratePropertyFragment(ExpressionNode, VariableProperty, VariableReference, *ActiveMessageLog);
if (VariableGetFragment.IsValid())
{
CompiledFragments.Add(&ExpressionNode, VariableGetFragment);
}
}
// if a variable-get couldn't be created for it, it needs to be an input to the math node
else if(ExpressionNode.Token.TokenType != FBasicToken::TOKEN_Guid)
{
CompiledFragments.Add(&ExpressionNode, GenerateInputPinFragment(VariableIdentifier));
}
}
else if (ExpressionNode.Token.TokenType == FBasicToken::TOKEN_Const)
{
CompiledFragments.Add(&ExpressionNode, GenerateLiteralFragment(ExpressionNode.Token, *ActiveMessageLog));
}
else // TOKEN_Symbol
{
FText ErrorText = FText::Format(LOCTEXT("UhandledTokenType", "Unhandled token '{0}' in expression: '@@'"),
FText::FromString(ExpressionNode.Token.Identifier));
ActiveMessageLog->Error(*ErrorText.ToString(), CompilingNode);
}
// keep traversing the expression tree... we should handle cascading
// errors that result from ones incurred here, gathering them all as we
// go, presenting them to the user later
return true;
}
/**
* Overloaded, part of the FExpressionVisitor interface... On VISIT_Post,
* attempts to generate a UK2Node_CallFunction node for the specified
* FBinaryOperator.
*
* @return True to continue traversing the expression tree, false to stop.
*/
virtual bool Visit(FBinaryOperator& ExpressionNode, EVisitPhase Phase) override
{
check(ActiveMessageLog != nullptr);
// we only care about the "Post" visit, after the operands fragments have been generated
if (Phase == FExpressionVisitor::VISIT_Post)
{
TSharedPtr<FCodeGenFragment> LHS = CompiledFragments.FindRef(&(ExpressionNode.LHS.Get()));
TSharedPtr<FCodeGenFragment> RHS = CompiledFragments.FindRef(&(ExpressionNode.RHS.Get()));
TArray< TSharedPtr<FCodeGenFragment> > ArgumentList;
ArgumentList.Add(LHS);
ArgumentList.Add(RHS);
TSharedPtr<FCodeGenFragment_FuntionCall> FunctionFragment = GenerateFunctionFragment(ExpressionNode, ExpressionNode.Operator, ArgumentList, *ActiveMessageLog);
if (FunctionFragment.IsValid())
{
CompiledFragments.Add(&ExpressionNode, FunctionFragment);
}
}
// keep traversing the expression tree... we should handle cascading
// errors that result from ones incurred here, gathering them all as we
// go, presenting them to the user later
return true;
}
/**
* Does nothing (but had to prevent this expression node from being flagged
* as "unhandled"). Expression lists are handled by whatever expression
* they're contained within.
*
* @return Always true, it is expected that cascading errors are handled (and all should be logged).
*/
virtual bool Visit(FExpressionList& ExpressionNode, EVisitPhase Phase) override
{
// no fragments are generated from a list node, it mostly acts as a link
// from a parent node to some set of sub-expressions
// keep traversing the expression tree... if there are any errors,
// they'll be caught in the children nodes (or maybe in the parent)
return true;
}
/**
* Overloaded, part of the FExpressionVisitor interface... On VISIT_Post,
* attempts to generate a UK2Node_CallFunction node for the specified
* FFunctionExpression.
*
* @return True to continue traversing the expression tree, false to stop.
*/
virtual bool Visit(FFunctionExpression& ExpressionNode, EVisitPhase Phase) override
{
check(ActiveMessageLog != nullptr);
// we only care about the "Post" visit, after the function's parameter fragments have been generated
if (Phase == FExpressionVisitor::VISIT_Post)
{
TArray< TSharedPtr<FCodeGenFragment> > ArgumentList;
for (TSharedRef<IFExpressionNode> Param : ExpressionNode.ParamList->Children)
{
TSharedPtr<FCodeGenFragment> ParamFragment = CompiledFragments.FindRef(&(Param.Get()));
ArgumentList.Add(ParamFragment);
}
TSharedPtr<FCodeGenFragment_FuntionCall> FunctionFragment = GenerateFunctionFragment(ExpressionNode, ExpressionNode.FuncName, ArgumentList, *ActiveMessageLog);
if (FunctionFragment.IsValid())
{
CompiledFragments.Add(&ExpressionNode, FunctionFragment);
}
}
// keep traversing the expression tree... we should handle cascading
// errors that result from ones incurred here, gathering them all as we
// go, presenting them to the user later
return true;
}
/**
* From the FExpressionVisitor interface; where we would handle prefixed
* unary operators. Currently support for those is unimplemented, so we just
* log a descriptive error and return.
*
* @return Always true, it is expected that cascading errors are handled (and all should be logged).
*/
virtual bool Visit(FUnaryOperator& ExpressionNode, EVisitPhase Phase) override
{
// don't want to double up on the error message (in the "Post" phase)
if (Phase == VISIT_Pre)
{
FText ErrorText = FText::Format(LOCTEXT("UnaryExpressionError", "Currently, unary operators {0} are prohibited in expressions: '@@'"),
FText::FromString(ExpressionNode.ToString()));
ActiveMessageLog->Error(*ErrorText.ToString(), CompilingNode);
}
// keep traversing the expression tree... we should handle cascading
// errors that result from this, and gather them all to present to the user
return true;
}
/**
* From the FExpressionVisitor interface; where we would handle conditional
* ?: operators. Currently support for those is unimplemented, so we just
* log a descriptive error and return.
*
* @return Always true, it is expected that cascading errors are handled (and all should be logged).
*/
virtual bool Visit(FConditionalOperator& ExpressionNode, EVisitPhase Phase) override
{
check(ActiveMessageLog != nullptr);
// don't want to double up on the error message (in the "Post" phase)
if (Phase == VISIT_Pre)
{
FText ErrorText = FText::Format(LOCTEXT("ConditionalExpressionError", "Currently, conditional operators {0} are prohibited in expressions: '@@'"),
FText::FromString(ExpressionNode.ToString()));
ActiveMessageLog->Error(*ErrorText.ToString(), CompilingNode);
}
// keep traversing the expression tree... we should handle cascading
// errors that result from this, and gather them all to present to the user
return true;
}
private:
/**
* Another FExpressionVisitor interface function... A Generic catch all for
* any expression nodes that we don't explicitly handle. Simply logs an
* error, and returns.
*
* @return Always true, it is expected that cascading errors can be handled (and all should be logged).
*/
virtual bool VisitUnhandled(IFExpressionNode& ExpressionNode, EVisitPhase Phase) override
{
check(ActiveMessageLog != nullptr);
if (Phase == VISIT_Leaf || Phase == VISIT_Pre)
{
FText ErrorText = FText::Format(LOCTEXT("UnhandledExpressionNode", "Unsupported operation ({0}) in the expression: '@@'"),
FText::FromString(ExpressionNode.ToString()));
ActiveMessageLog->Error(*ErrorText.ToString(), CompilingNode);
}
// keep traversing the expression tree... we should handle cascading
// errors that result from this, and gather them all to present to the user
return true;
}
/**
* Either adds a new pin, or finds an existing one on the MathExpression
* node. From that, a fragment is generated (to track the pin, so
* connections can be made later).
*
* @param VariableIdentifier The name of the pin to generate a fragment for.
* @return A new input pin fragment (associated with a pin on the math expression's entry node).
*/
TSharedPtr<FCodeGenFragment_InputPin> GenerateInputPinFragment(FString const VariableIdentifier)
{
TSharedPtr<FCodeGenFragment_InputPin> InputPinFragment;
UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>();
UK2Node_Tunnel* EntryNode = CompilingNode->GetEntryNode();
// if a pin under this name already exists, use that
if (UEdGraphPin* InputPin = EntryNode->FindPin(VariableIdentifier))
{
InputPinFragment = MakeShareable(new FCodeGenFragment_InputPin(InputPin));
}
// otherwise, a new input pin needs to be created for it
else
{
// Create an input pin (using the default guessed type)
FEdGraphPinType DefaultType;
// currently, generated expressions ALWAYS take a float (it is the most versatile type)
DefaultType.PinCategory = K2Schema->PC_Float;
UEdGraphPin* NewInputPin = EntryNode->CreateUserDefinedPin(VariableIdentifier, DefaultType, EGPD_Output);
InputPinFragment = MakeShareable(new FCodeGenFragment_InputPin(NewInputPin));
}
// when regenerating a node, we need to clear any old pins that weren't
// reused (can't do this before the node gen because the user may have
// altered a pin to how they want it), so here we track the ones that
// were used by the latest expression
InputPinNames.Add(VariableIdentifier);
return InputPinFragment;
}
/**
* Attempts to generate a VariableGet node for the blueprint graph. If one
* isn't generated, then this function logs an error (and returns an empty
* pointer). However, if one is successfully created, then a fragment
* wrapper is created and returned (to aid in linking the node later).
*
* @param ExpressionContext The expression node that we're generating this fragment for.
* @param VariableProperty The variable that the generated UK2Node should access.
* @param VariableReference Variable reference to assign to the node
* @return An empty pointer if we failed to generate something, otherwise new variable-get fragment.
*/
TSharedPtr<FCodeGenFragment_VariableGet> GeneratePropertyFragment(FTokenWrapperNode& ExpressionContext, UProperty* VariableProperty, FMemberReference& MemberReference, FCompilerResultsLog& MessageLog)
{
check(ExpressionContext.Token.TokenType == FBasicToken::TOKEN_Identifier || ExpressionContext.Token.TokenType == FBasicToken::TOKEN_Guid);
check(VariableProperty != nullptr);
UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>();
TSharedPtr<FCodeGenFragment_VariableGet> VariableGetFragment;
UClass* VariableAccessClass = TargetBlueprint->SkeletonGeneratedClass;
if (MemberReference.IsLocalScope() || UEdGraphSchema_K2::CanUserKismetAccessVariable(VariableProperty, VariableAccessClass, UEdGraphSchema_K2::CannotBeDelegate))
{
FEdGraphPinType VarType;
if (K2Schema->ConvertPropertyToPinType(VariableProperty, /*out*/VarType))
{
UK2Node_VariableGet* NodeTemplate = NewObject<UK2Node_VariableGet>();
NodeTemplate->VariableReference = MemberReference;
UK2Node_VariableGet* VariableGetNode = SpawnNodeFromTemplate<UK2Node_VariableGet>(&ExpressionContext, NodeTemplate);
VariableGetFragment = MakeShareable(new FCodeGenFragment_VariableGet(VariableGetNode, VarType));
}
else
{
FText ErrorText = FText::Format(LOCTEXT("IncompatibleVarError", "Blueprint '{0}' variable is incompatible with graph pins in the expression: '@@'"),
FText::FromName(VariableProperty->GetFName()));
MessageLog.Error(*ErrorText.ToString(), CompilingNode);
}
}
else
{
FText ErrorText = FText::Format(LOCTEXT("InaccessibleVarError", "Cannot access the blueprint's '{0}' variable from the expression: '@@'"),
FText::FromName(VariableProperty->GetFName()));
MessageLog.Error(*ErrorText.ToString(), CompilingNode);
}
return VariableGetFragment;
}
/**
* Spawns a fragment which wraps a literal value. No graph-node or pin is
* created for this fragment; instead, it saves the literal value for later,
* when this fragment is connected with another (it then enters the literal
* value as the connecting pin's default).
*
* @param ExpressionNode The expression node that we're generating this fragment for.
* @return A new literal fragment.
*/
TSharedPtr<FCodeGenFragment_Literal> GenerateLiteralFragment(FBasicToken const& Token, FCompilerResultsLog& MessageLog)
{
check(Token.TokenType == FBasicToken::TOKEN_Const);
UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>();
FEdGraphPinType LiteralType;
switch (Token.ConstantType)
{
case CPT_Bool:
LiteralType.PinCategory = K2Schema->PC_Boolean;
break;
case CPT_Float:
LiteralType.PinCategory = K2Schema->PC_Float;
break;
case CPT_Int:
LiteralType.PinCategory = K2Schema->PC_Int;
break;
case CPT_String:
LiteralType.PinCategory = K2Schema->PC_String;
break;
default:
MessageLog.Error(*FText::Format(LOCTEXT("UnhandledLiteralType", "Unknown literal type in expression: '@@'"),
FText::AsNumber(Token.ConstantType)).ToString(),
CompilingNode);
break;
};
return MakeShareable(new FCodeGenFragment_Literal(Token.GetConstantValue(), LiteralType));
}
/**
* Attempts to find a coresponding fuction (in this class's FOperatorTable),
* one that matches the supplied operator name and the set of arguments. If
* a matching function is found, then a wrapping UK2Node_CallFunction is
* spawned and linked with the supplied arguments (otherwise, errors will
* be logged and an empty pointer will be returned).
*
* @param ExpressionContext The expression node that we're generating this fragment for.
* @param FunctionName The name of the operator (the key we're going to use looking up into FOperatorTable).
* @param ArgumentList A set of other fragments that will plug in as parameters into function.
* @return An empty pointer if we failed to generate something, otherwise the new function fragment.
*/
TSharedPtr<FCodeGenFragment_FuntionCall> GenerateFunctionFragment(IFExpressionNode& ExpressionContext, FString FunctionName, TArray< TSharedPtr<FCodeGenFragment> > ArgumentList, FCompilerResultsLog& MessageLog)
{
bool bMissingArgument = false;
TArray<FEdGraphPinType> TypeList;
// create a type list from the argument fragments (so we can find a matching function signature)
for (int32 Index = 0; Index < ArgumentList.Num(); ++Index)
{
if (!ArgumentList[Index].IsValid())
{
FText ErrorText = FText::Format(LOCTEXT("MissingArgument", "Failed to generate argument #{0} for the '{1}' function, in the expression: '@@'"),
FText::AsNumber(Index + 1),
FText::FromString(FunctionName));
MessageLog.Error(*ErrorText.ToString(), CompilingNode);
bMissingArgument = true;
continue;
}
TypeList.Add(ArgumentList[Index]->GetOutputType());
}
TSharedPtr<FCodeGenFragment_FuntionCall> FunctionFragment;
if (!OperatorLookup.Contains(FunctionName))
{
FText ErrorText = FText::Format(LOCTEXT("UnknownFuncError", "Unknown function '{0}' in the expression: '@@'"), FText::FromString(FunctionName));
MessageLog.Error(*ErrorText.ToString(), CompilingNode);
}
else if (bMissingArgument)
{
// don't execute the other if-branches, head them off if there is already an error
}
else if (UFunction* MatchingFunction = OperatorLookup.FindMatchingFunction(FunctionName, TypeList))
{
UProperty* ReturnProperty = MatchingFunction->GetReturnProperty();
if (ReturnProperty == nullptr)
{
FText ErrorText = FText::Format(LOCTEXT("NoReturnTypeError", "The '{0}' function returns nothing, it cannot be used in the expression: '@@'"),
FText::FromString(FunctionName));
MessageLog.Error(*ErrorText.ToString(), CompilingNode);
}
else
{
UEdGraphSchema_K2 const* K2Schema = GetDefault<UEdGraphSchema_K2>();
FEdGraphPinType ReturnType;
if (K2Schema->ConvertPropertyToPinType(ReturnProperty, /*out*/ReturnType))
{
UK2Node_CallFunction* NodeTemplate = NewObject<UK2Node_CallFunction>(CompilingNode->GetGraph());
NodeTemplate->SetFromFunction(MatchingFunction);
UK2Node_CallFunction* FunctionCall = SpawnNodeFromTemplate<UK2Node_CallFunction>(&ExpressionContext, NodeTemplate);
int32 InitialErrorCount = MessageLog.NumErrors;
// connect this fragment to its children fragments
int32 PinWireIndex = 0;
for (auto PinIt = FunctionCall->Pins.CreateConstIterator(); PinIt; ++PinIt)
{
UEdGraphPin* InputPin = *PinIt;
if (!K2Schema->IsMetaPin(*InputPin) && (InputPin->Direction == EGPD_Input))
{
if (PinWireIndex < ArgumentList.Num())
{
TSharedPtr<FCodeGenFragment>& ArgumentNode = ArgumentList[PinWireIndex];
// try to make the connection (which might cause an error internally)
if (!ArgumentNode->ConnectToInput(InputPin, MessageLog))
{
FText ErrorText = FText::Format(LOCTEXT("ConnectPinError", "Failed to connect parameter #{0} with input on '@@'"),
FText::AsNumber(PinWireIndex + 1));
MessageLog.Error(*ErrorText.ToString(), FunctionCall);
}
}
else if (InputPin->DefaultValue.IsEmpty()) // there is an ErrorTolerance parameter with a default value in EqualEqual_VectorVector
{
// too many pins - shouldn't be possible due to the checking in FindMatchingFunction() above
FText ErrorText = LOCTEXT("ConnectPinError_RequiresMoreParameters", "The '@@' function requires more parameters than were provided");
MessageLog.Error(*ErrorText.ToString(), FunctionCall);
break;
}
++PinWireIndex;
}
}
bool bConnectionErrors = (InitialErrorCount < MessageLog.NumErrors);
if (bConnectionErrors)
{
MessageLog.Error(*LOCTEXT("InternalExpressionError", "Internal node error for expression: '@@'").ToString(), CompilingNode);
}
FunctionFragment = MakeShareable(new FCodeGenFragment_FuntionCall(FunctionCall, ReturnType));
}
else
{
FText ErrorText = FText::Format(LOCTEXT("ReturnTypeError", "The '{0}' function's return type is incompatible with graph pins in the expression: '@@'"),
FText::FromString(FunctionName));
MessageLog.Error(*ErrorText.ToString(), CompilingNode);
}
}
}
else
{
FText ErrorText = FText::Format(LOCTEXT("OperatorParamsError", "Cannot find a '{0}' function that takes the supplied param types, for expression: '@@'"),
FText::FromString(FunctionName));
MessageLog.Error(*ErrorText.ToString(), CompilingNode);
}
return FunctionFragment;
}
/**
* Utility method to turn an FLayoutVisitor coordinate into graph coordinates.
* FLayoutVisitor coordinates are in terms of nodes (so a depth of 1, would
* mean one node to the right of the initial node).
*
* @param Depth Horizontal coordinate (how many nodes away from the initial node).
* @param Height Vertical coordinate (how many nodes down from the initial node).
* @return A 2D graph position for the center of a node to be placed at.
*/
FVector2D GetNodePosition(int32 Depth, int32 Height) const
{
// get a count of how many nodes there are at this specific depth
int32 TotalHeight = LayoutMapper.DepthHeightLookup.FindRef(LayoutMapper.GetMaximumDepth() - Depth);
const float MiddleHeight = FMath::Max(TotalHeight, 1) * 0.5f;
const float HeightPerNode = 140.0f;
const float DepthPerNode = 240.0f;
return FVector2D(Depth * DepthPerNode, (Height - MiddleHeight + 0.5f) * HeightPerNode);
}
/**
* Templatized function for turning an expression node into a UK2Node. This
* takes the expression node's position in the expression tree and turns it
* into a blueprint graph position (placing the new UK2Node there).
*
* @param ForExpression The expression node that the will UK2Node represent.
* @param Template An instance of the UK2Node type you wish to spawn.
* @return The newly created UK2Node.
*/
template<typename NodeType>
NodeType* SpawnNodeFromTemplate(IFExpressionNode* ForExpression, NodeType* Template)
{
const int32 Y = LayoutMapper.HeightChart.FindRef(ForExpression);
const int32 X = LayoutMapper.GetMaximumDepth() - LayoutMapper.DepthChart.FindRef(ForExpression);
GraphXBounds.X = FMath::Min((int32)GraphXBounds.X, X);
GraphXBounds.Y = FMath::Max((int32)GraphXBounds.Y, X);
const FVector2D Location = GetNodePosition(X, Y);
return FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<NodeType>(CompilingNode->BoundGraph, Template, Location);
}
private:
/** The node that we're generating sub-nodes and pins for */
UK2Node_MathExpression* CompilingNode;
/**
* The blueprint that CompilingNode belongs to (the blueprint this will
* generate a graph for).
*/
UBlueprint* TargetBlueprint;
/** List of known operators, and mappings from them to associated functions */
FOperatorTable OperatorLookup;
/**
* An FLayoutVisitor that charts the depth of the expression tree (and what
* depth/height each expression node is at). Used to layout the graph nicely.
*/
FLayoutVisitor LayoutMapper;
/**
* Supplements LayoutMapper, tracks where nodes were actually placed (sometimes
* the depth of an expression node doesn't map one-to-one with the fragment
* in the graph), so you have the min and max x locations of spawned graph nodes.
*/
FVector2D GraphXBounds;
/**
* Fragments that represent spawned UK2Nodes or literals that were generated
* from traversing the expression tree... These fragments facilitate
* connections between each other (that's why we need to track them).
*/
TMap<IFExpressionNode*, TSharedPtr<FCodeGenFragment> > CompiledFragments;
/**
* Used so the various Visit() methods have a way to log errors, null when
* not in the middle of GenerateCode().
*/
FCompilerResultsLog* ActiveMessageLog;
/**
* After the code generation, we want to clear any old pins that
* weren't reused, so here we track the ones in use.
*/
TArray<FString> InputPinNames;
};
/*******************************************************************************
* FExpressionParser
*******************************************************************************/
#define PARSE_HELPER_BEGIN(NestedRuleName) \
TSharedRef<IFExpressionNode> LHS = NestedRuleName(); \
Begin:
#define PARSE_HELPER_ENTRY(NestedRuleName, DesiredToken) \
if (IsValid() && MatchSymbol(DesiredToken)) \
{ \
TSharedRef<IFExpressionNode> RHS = NestedRuleName(); \
LHS = MakeShareable(new FBinaryOperator(DesiredToken, LHS, RHS)); \
goto Begin; \
} \
#define PARSE_HELPER_END \
{ return LHS; }
/**
* Recursively builds an IFExpressionNode tree, where leaf nodes represent tokens
* (constants, literals, or identifiers), and branch nodes represent operations
* on the attached children. The chaining order of expression functions is what
* determines operator precedence.
*/
class FExpressionParser : public FBasicTokenParser
{
public:
/**
* Takes a string and parses a mathematical expression out of it, returning
* the head of an expression tree that was generated as a result.
*
* @param InExpression The string you wish to parse.
* @return An expression node, which serves as the root of an expression tree representing the provided string.
*/
TSharedRef<IFExpressionNode> ParseExpression(FString InExpression)
{
ExpressionString = InExpression;
ResetParser(*ExpressionString);
TSharedRef<IFExpressionNode> FullExpression = Expression();
// if we didn't parse the full expression and the parser doesn't have
// an error, then there is some unhandled string postfixed to the
// expression (something like "2.x" or "5var")
if (InputPos < InputLen && IsValid())
{
FText ErrorText = FText::Format(LOCTEXT("UnhandledPostfixError", "Unhandled trailing '{0}' at the end of the expression"),
FText::FromString(&Input[InputPos]));
SetError(FErrorState::ParseError, ErrorText);
}
return FullExpression;
}
private:
/**
* Starting point for parsing full expressions (sets off on parsing out
* operations according to operator precedence)... Could be used for the
* initial root expression, or various other sub-expressions (like those
* encapsulated in parentheses, etc.).
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> Expression()
{
// AssignmentExpression has the lowest precedence, start with it (it
// will attempt to parse out higher precedent operations first)
return AssignmentExpression();
}
/**
* Intended to support assignment within the expression (setting temp or
* external variables equal to some value, so they can be used later in the
* expression).
*
* @TODO Implement!
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> AssignmentExpression()
{
// ConditionalExpression takes precedence over a assignment operation, parse it first
return ConditionalExpression();
}
/**
* Looks for a conditional ternary statement (c ? a : b) to parse, and
* tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> ConditionalExpression()
{
// LogicalOrExpression takes precedence over a conditional operation, parse it first
TSharedRef<IFExpressionNode> MainPart = LogicalOrExpression();
if (IsValid() && MatchSymbol(TEXT("?")))
{
TSharedRef<IFExpressionNode> TruePart = Expression();
RequireSymbol(TEXT(":"), TEXT("?: operator"));
TSharedRef<IFExpressionNode> FalsePart = ConditionalExpression();
return MakeShareable(new FConditionalOperator(MainPart, TruePart, FalsePart));
}
else
{
return MainPart;
}
}
/**
* Looks for a binary logical-or statement (a || b) to parse, and
* tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> LogicalOrExpression()
{
// LogicalOrExpression takes precedence over an or operation, parse it first
PARSE_HELPER_BEGIN(LogicalAndExpression)
PARSE_HELPER_ENTRY(LogicalAndExpression, TEXT("||"))
PARSE_HELPER_END
}
/**
* Looks for a binary logical-and statement (a && b) to parse, and
* tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> LogicalAndExpression()
{
// InclusiveOrExpression takes precedence over an and operation, parse it first
PARSE_HELPER_BEGIN(InclusiveOrExpression)
PARSE_HELPER_ENTRY(InclusiveOrExpression, TEXT("&&"))
PARSE_HELPER_END
}
/**
* Looks for a binary bitwise-or statement (a | b) to parse, and
* tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> InclusiveOrExpression()
{
// ExclusiveOrExpression takes precedence over an inclusive or operation, parse it first
PARSE_HELPER_BEGIN(ExclusiveOrExpression)
PARSE_HELPER_ENTRY(ExclusiveOrExpression, TEXT("|"))
PARSE_HELPER_END
}
/**
* Looks for a binary exclusive-or statement (a ^ b) to parse, and
* tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> ExclusiveOrExpression()
{
// AndExpression takes precedence over an exclusive or operation, parse it first
PARSE_HELPER_BEGIN(AndExpression)
PARSE_HELPER_ENTRY(AndExpression, TEXT("^"))
PARSE_HELPER_END
}
/**
* Looks for a binary bitwise-and statement (a & b) to parse, and
* tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> AndExpression()
{
// EqualityExpression takes precedence over an and operation, parse it first
PARSE_HELPER_BEGIN(EqualityExpression)
PARSE_HELPER_ENTRY(EqualityExpression, TEXT("&"))
PARSE_HELPER_END
}
/**
* Looks for a binary equality statement (like [a == b], or [a != b]) to
* parse, and tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> EqualityExpression()
{
// RelationalExpression takes precedence over an equality expression, parse it first
PARSE_HELPER_BEGIN(RelationalExpression)
PARSE_HELPER_ENTRY(RelationalExpression, TEXT("=="))
PARSE_HELPER_ENTRY(RelationalExpression, TEXT("!="))
PARSE_HELPER_END
}
/**
* Looks for a binary comparison statement to parse (like [a > b], [a <= b],
* etc.), and tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> RelationalExpression()
{
// ShiftExpression takes precedence over a relational expression, parse it first
PARSE_HELPER_BEGIN(ShiftExpression)
PARSE_HELPER_ENTRY(ShiftExpression, TEXT("<"))
PARSE_HELPER_ENTRY(ShiftExpression, TEXT(">"))
PARSE_HELPER_ENTRY(ShiftExpression, TEXT("<="))
PARSE_HELPER_ENTRY(ShiftExpression, TEXT(">="))
PARSE_HELPER_END
}
/**
* Looks for a binary bitwise shift statement to parse (like [a << b], or
* [a >> b]), and tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> ShiftExpression()
{
// AdditiveExpression takes precedence over a shift, parse it first
PARSE_HELPER_BEGIN(AdditiveExpression)
PARSE_HELPER_ENTRY(AdditiveExpression, TEXT("<<"))
PARSE_HELPER_ENTRY(AdditiveExpression, TEXT(">>"))
PARSE_HELPER_END
}
/**
* Looks for a binary addition/subtraction statement to parse ([a + b], or
* [a - b]), and tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> AdditiveExpression()
{
// MultiplicativeExpression takes precedence over an add/subtract, parse it first
PARSE_HELPER_BEGIN(MultiplicativeExpression)
PARSE_HELPER_ENTRY(MultiplicativeExpression, TEXT("+"))
PARSE_HELPER_ENTRY(MultiplicativeExpression, TEXT("-"))
PARSE_HELPER_END
}
/**
* Looks for a binary multiplication/division/modulus statement to parse
* ([a * b], [a / b], or [a % b]), and tokenizes the operands.
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> MultiplicativeExpression()
{
// CastExpression takes precedence over a multiply/division/modulus, parse it first
PARSE_HELPER_BEGIN(CastExpression)
PARSE_HELPER_ENTRY(CastExpression, TEXT("*"))
PARSE_HELPER_ENTRY(CastExpression, TEXT("/"))
PARSE_HELPER_ENTRY(CastExpression, TEXT("%"))
PARSE_HELPER_END
}
/**
* Intended to handle type-casts (like from float to int, etc.).
*
* @TODO Implement!
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> CastExpression()
{
// @TODO: support casts (currently this is too greedy, and messes up "4*(5)" interpreting (5) as a cast)
// if (MatchSymbol(TEXT("(")))
// {
// //@TODO: Need to support qualifiers / typedefs / what have you
// FBasicToken TypeName;
// GetToken(TypeName);
// TSharedRef<IFExpressionNode> TypeExpression = MakeShareable(new FTokenWrapperNode(TypeName));
//
// RequireSymbol(TEXT(")"), TEXT("Closing ) in cast"));
//
// TSharedRef<IFExpressionNode> ValueExpression = CastExpression(Context);
//
// return MakeShareable(new FCastOperator(TypeExpression, ValueExpression));
// }
// else
{
return UnaryExpression();
}
}
/**
* Attempts to parse various unary statements (like positive/negative
* markers, logical negation, pre increment/decrement, etc.)
*
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> UnaryExpression()
{
// prefix increment: ++<unary-expression>
// prefix decrement: --<unary-expression>
// bitwise not: ~<unary-expression>
// logical not: !<unary-expression>
// positive sign: +<unary-expression>
// negative sign: -<unary-expression>
// reference: &<unary-expression>
// dereference: *<unary-expression>
// negative sign: -<unary-expression>
// allocation: new <unary-expression>
// deallocation: delete <unary-expression>
// parameter pack: sizeof <unary-expression>
// C-style cast: (type) <unary-expression>
//
// check for the various prefix operators and jump back to
// CastExpression() for parsing the right operand...
if (MatchSymbol(TEXT("&")))
{
return MakeShareable(new FUnaryOperator(TEXT("&"), CastExpression()));
}
else if (MatchSymbol(TEXT("+")))
{
// would return pre-increment operators like so:
// unaryOp(+).RHS = unaryOp(+)
return MakeShareable(new FUnaryOperator(TEXT("+"), CastExpression()));
}
else if (MatchSymbol(TEXT("-")))
{
// would return post-increment operators like so:
// unaryOp(-).RHS = unaryOp(-)
return MakeShareable(new FUnaryOperator(TEXT("-"), CastExpression()));
}
else if (MatchSymbol(TEXT("~")))
{
return MakeShareable(new FUnaryOperator(TEXT("~"), CastExpression()));
}
else if (MatchSymbol(TEXT("!")))
{
return MakeShareable(new FUnaryOperator(TEXT("!"), CastExpression()));
}
else
{
return PostfixExpression();
}
}
/**
* Intended to handle postfix operations (like post increment/decrement,
* array subscripting, member access, etc.).
*
* @TODO Implement!
* @return Root node of an expression tree that was generated from where we
* are in parsing ExpressionString (at time of calling).
*/
TSharedRef<IFExpressionNode> PostfixExpression()
{
// if (MatchSymbol(TEXT("[")))
// {
// // Array indexing
// TSharedRef<IFExpressionNode> IndexExpression = Expression(Context);
// RequireSymbol(TEXT("]"), TEXT("Closing ] in array indexing"));
//
// return NullExpression; // @TODO: return a valid expression node
// }
// else if (MatchSymbol(TEXT("(")))
// {
// while (!PeekSymbol(TEXT(")")))
// {
// TSharedRef<IFExpressionNode> Item = AssignmentExpression(Context);
// }
//
// RequireSymbol(TEXT(")"), TEXT("Closing ) in function call"));
//
// return NullExpression; // @TODO: return a valid expression node
// }
// else if (MatchSymbol(TEXT(".")) || MatchSymbol(TEXT("->")))
// {
// // Member reference
// FBasicToken Identifier;
// if (GetIdentifier(Identifier, /*bNoConsts=*/ true))
// {
// //@TODO: Do stuffs
// }
// else
// {
// // @TODO: error
// }
//
// return NullExpression; // @TODO: return a valid expression node
// }
// else
{
return PrimaryExpression();
}
}
/**
* End of the line, where we attempt to generate a leaf node (an identifier,
* const literal, or a string). However, here we also look for the start of
* a sub-expression (one encapsulated in parentheses).
*
* @return Either a leaf node (representing a variable, or literal), or another
* branch node, representing a sub-expression encapsulated by parentheses.
*/
TSharedRef<IFExpressionNode> PrimaryExpression()
{
if (MatchSymbol(TEXT("(")))
{
TSharedRef<IFExpressionNode> Result = Expression();
RequireSymbol(TEXT(")"), TEXT("group closing"));
return Result;
}
else
{
// identifier, constant, or string
FBasicToken Token;
GetToken(Token);
// or maybe a function call?
if (MatchSymbol(TEXT("(")))
{
TSharedPtr<FExpressionList> FuncArguments;
// if this is an empty function (takes no parameters)
if (PeekSymbol(TEXT(")")))
{
FuncArguments = MakeShareable(new FExpressionList);
}
else
{
FuncArguments = ListExpression();
}
TSharedRef<IFExpressionNode> FuncExpression = MakeShareable(new FFunctionExpression(Token.Identifier, FuncArguments.ToSharedRef()));
FText RequireError = FText::Format(LOCTEXT("MissingFuncClose", "'{0}' closing"), FText::FromString(Token.Identifier));
RequireSymbol(TEXT(")"), *RequireError.ToString());
return FuncExpression;
}
else
{
return MakeShareable(new FTokenWrapperNode(Token));
}
}
}
/**
* Parses out a comma separated list of sub-expressions (arguments for a
* function or struct).
*
* @return A branch FExpressionList node, which holds a series of sub-expressions.
*/
TSharedRef<FExpressionList> ListExpression()
{
TSharedRef<FExpressionList> ListNode = MakeShareable(new FExpressionList);
do
{
ListNode->Children.Add(Expression());
} while (MatchSymbol(TEXT(",")));
return ListNode;
}
private:
/** The intact expression string that this is currently in charge of parsing */
FString ExpressionString;
};
/*******************************************************************************
* UK2Node_MathExpression
*******************************************************************************/
//------------------------------------------------------------------------------
UK2Node_MathExpression::UK2Node_MathExpression(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
// renaming the node rebuilds the expression (the node name is where they
// specify the math equation)
bCanRenameNode = true;
bMadeAfterRotChange = false;
}
void UK2Node_MathExpression::Serialize(FArchive& Ar)
{
UK2Node_Composite::Serialize(Ar);
if (Ar.IsLoading() && !bMadeAfterRotChange)
{
// remember that this logic has been run, we only want to run it once:
bMadeAfterRotChange = true;
// We need to reorder the parameters to MakeRot/MakeRotator/Rotator/Rot, to filter this expensive logic I'm just searching
// expressions for 'rot':
if (Expression.Contains(TEXT("Rot")))
{
// Now parse the expression and look for function expressions to the old MakeRot function:
FExpressionParser Parser;
TSharedPtr<IFExpressionNode> ExpressionRoot = Parser.ParseExpression(Expression);
struct FMakeRotFixupVisitor : public FExpressionVisitor
{
virtual bool Visit(class FFunctionExpression& Node, EVisitPhase Phase)
{
if (Phase != VISIT_Pre)
{
return false;
}
const bool bIsMakeRot = (Node.FuncName == TEXT("makerot"));
if (bIsMakeRot ||
Node.FuncName == TEXT("rotator") ||
Node.FuncName == TEXT("rot"))
{
// reorder parameters to match new order of MakeRotator:
if (Node.ParamList->Children.Num() == 3)
{
TSharedRef<IFExpressionNode> OldPitch = Node.ParamList->Children[0];
TSharedRef<IFExpressionNode> OldYaw = Node.ParamList->Children[1];
TSharedRef<IFExpressionNode> OldRoll = Node.ParamList->Children[2];
Node.ParamList->Children[0] = OldRoll;
Node.ParamList->Children[1] = OldPitch;
Node.ParamList->Children[2] = OldYaw;
}
// MakeRot also needs to be updated to the new name:
if (bIsMakeRot)
{
Node.FuncName = TEXT("MakeRotator");
}
}
return true;
}
};
// perform the update:
FMakeRotFixupVisitor Fixup;
ExpressionRoot->Accept(Fixup);
// reform the expression with the updated parameter order/function names:
Expression = ExpressionRoot->ToString();
}
}
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
const FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
if (PropertyName == GET_MEMBER_NAME_CHECKED(UK2Node_MathExpression, Expression))
{
RebuildExpression(Expression);
}
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
// actions get registered under specific object-keys; the idea is that
// actions might have to be updated (or deleted) if their object-key is
// mutated (or removed)... here we use the node's class (so if the node
// type disappears, then the action should go with it)
UClass* ActionKey = GetClass();
// to keep from needlessly instantiating a UBlueprintNodeSpawner, first
// check to make sure that the registrar is looking for actions of this type
// (could be regenerating actions for a specific asset, and therefore the
// registrar would only accept actions corresponding to that asset)
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
}
}
//------------------------------------------------------------------------------
FNodeHandlingFunctor* UK2Node_MathExpression::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_MathExpression(CompilerContext);
}
//------------------------------------------------------------------------------
bool UK2Node_MathExpression::ShouldExpandInsteadCompile() const
{
const int32 TunnelNodesNum = 2;
if (!BoundGraph || (TunnelNodesNum >= BoundGraph->Nodes.Num()))
{
return true;
}
if ((TunnelNodesNum + 1) == BoundGraph->Nodes.Num())
{
TArray<UEdGraphNode*> InnerNodes = BoundGraph->Nodes;
InnerNodes.RemoveSingleSwap(GetEntryNode(), false);
InnerNodes.RemoveSingleSwap(GetExitNode(), false);
const bool bTheOnlyNodeIsNotAFunctionCall = (1 == InnerNodes.Num())
&& (nullptr != InnerNodes[0])
&& !InnerNodes[0]->IsA<UK2Node_CallFunction>();
if (bTheOnlyNodeIsNotAFunctionCall)
{
return true;
}
}
return false;
}
//------------------------------------------------------------------------------
TSharedPtr<class INameValidatorInterface> UK2Node_MathExpression::MakeNameValidator() const
{
// we'll let our parser mark the node for errors after the face (once the
// name is submitted)... parsing it with every character could be slow
return MakeShareable(new FDummyNameValidator(EValidatorResult::Ok));
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::OnRenameNode(const FString& NewName)
{
RebuildExpression(NewName);
CachedNodeTitle.MarkDirty();
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::RebuildExpression(FString InExpression)
{
static bool bIsAlreadyRebuilding = false;
// the rebuild can invoke a ReconstructNode(), which triggers this again,
// so this combined with the following
if (!bIsAlreadyRebuilding)
{
TGuardValue<bool> RecursionGuard(bIsAlreadyRebuilding, true);
ClearExpression();
Expression = InExpression;
// This should not be sanitized, if anything fails to occur, what the user inputed should be what is displayed
CachedDisplayExpression.SetCachedText(FText::FromString(Expression), this);
CachedNodeTitle.SetCachedText(GetFullTitle(CachedDisplayExpression), this);
if (!InExpression.IsEmpty()) // @TODO: is this needed?
{
// build a expression tree from the string
FExpressionParser Parser;
TSharedPtr<IFExpressionNode> ExpressionRoot = Parser.ParseExpression(InExpression);
// if the parser successfully chewed through the string
if (Parser.IsValid())
{
FMathGraphGenerator GraphGenerator(this);
// generate new nodes from the expression tree (could result in
// a series of errors being attached to the node).
if (!GraphGenerator.GenerateCode(ExpressionRoot.ToSharedRef(), *CachedMessageLog))
{
CachedMessageLog->Error(*LOCTEXT("MathExprGFailedGen", "Failed to generate full expression graph for: '@@'").ToString(), this);
}
else
{
Expression = ExpressionRoot->ToString();
CachedDisplayExpression.SetCachedText(FText::FromString(SanitizeDisplayExpression(ExpressionRoot->ToDisplayString(GetBlueprint()))), this);
CachedNodeTitle.SetCachedText(GetFullTitle(CachedDisplayExpression), this);
}
if (UK2Node_Tunnel* EntryNode = GetEntryNode())
{
// iterate backwards so we can remove as we go... we want to
// clear any pins that weren't used by the expression (if we
// clear any, then they were probably remnants from the last
// expression... we can't delete them before, because the
// user may have mutated one for the new expression)
for (int32 PinIndex = EntryNode->UserDefinedPins.Num() - 1; PinIndex >= 0; --PinIndex)
{
TSharedPtr<FUserPinInfo> PinInfo = EntryNode->UserDefinedPins[PinIndex];
if (!GraphGenerator.IsPinInUse(PinInfo))
{
EntryNode->RemoveUserDefinedPin(PinInfo);
}
}
}
}
else
{
FText ErrorText = FText::Format(LOCTEXT("MathExprParseError", "PARSE ERROR in '@@': {0}"),
Parser.GetErrorState().Description);
CachedMessageLog->Error(*ErrorText.ToString(), this);
}
}
// refresh the node since the connections may have changed
Super::ReconstructNode();
// finally, recompile
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
// The UI needs a refresh, so notify any interested parties that the blueprint has changed
Blueprint->BroadcastChanged();
}
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::ClearExpression()
{
// clear any errors
SetNodeError(this, FText::GetEmpty());
// clear out old nodes
DeleteGeneratedNodesInGraph(BoundGraph);
// delete the old return pins (they will always be regenerated)... save the
// input pins though (because someone may have changed the input type to
// something other than a float)
if (UK2Node_Tunnel* ExitNode = GetExitNode())
{
// iterate backwards so we can remove as we go
for (int32 PinIndex = ExitNode->UserDefinedPins.Num() - 1; PinIndex >= 0; --PinIndex)
{
TSharedPtr<FUserPinInfo> PinInfo = ExitNode->UserDefinedPins[PinIndex];
ExitNode->RemoveUserDefinedPin(PinInfo);
}
}
Copying //UE4/Dev-Blueprints to //UE4/Dev-Main (Source: //UE4/Dev-Blueprints @ 2967759) #lockdown Nick.Penwarden ========================== MAJOR FEATURES + CHANGES ========================== Change 2919729 on 2016/03/23 by Ben.Cosh Support for macros in the profiler #UEBP-177 - Macro instance handling #Proj Kismet, BlueprintProfiler - Adds support for timings inside macro calls - Extends the tunnel graph support to handle multiple entry/exit sites Change 2938064 on 2016/04/08 by Phillip.Kavan [UE-17794] The "Delete Unused Variable" feature now considers the GetClassDefaults node as well. change summary: - added external linkage to UK2Node_GetClassDefaults::FindClassPin(). - added an include for the K2Node_GetClassDefaults header file to BlueprintGraphDefinitions.h. - added UK2Node_GetClassDefaults::GetInputClass() as a public API w/ external linkage; moved default 'nullptr' param logic into this impl. - modified FBlueprintEditorUtils::IsVariableUsed() to add an extra check for a GetClassDefaults node with a visible output pin for the variable that's also connected. - modified UK2Node_GetClassDefaults::GetInputClass() to return the generated skeleton class for Blueprint class types. Change 2938088 on 2016/04/08 by Mike.Beach Making bytecode produced by latent action nodes deterministic. Change 2938101 on 2016/04/08 by Mike.Beach Fixing a bug where the compile summary was not being reported because another compiler log was lingering - making it so the MathExpression node compiler log is not initialized with intent to report its own summary (it gets folded into the primary log). Change 2938121 on 2016/04/08 by Phillip.Kavan Remove a few redundant MBASM calls on variable type change. Change 2940295 on 2016/04/11 by Dan.Oconnor We now 'tag subobjects' of a blueprint even if it's part of the rootset, this means we correctly detect references to the BPGC in FPendingDelete::CheckForReferences(). Original rootset check dates to 2012 and I can find no justification for it currently. #jira UE-29216 Change 2943227 on 2016/04/13 by Dan.Oconnor Fixed improper detection of functions from interfaces that themselves inherit from some other interface #jira UE-29440 Change 2944270 on 2016/04/14 by Phillip.Kavan [UEBP-176] First pass at BP graph node heat map visualization while profiling. change summary: - added an "indicator overlay" to graph node widget layouts - added a heat mode "mode" selector widget to the BP profiler view panel - extended IBlueprintProfilerInterface to include APIs for accessing current heat map mode state - added FScriptNodePerfData::GetBlueprintPerfDataForAllTracePaths() (tentative - may need revisiting) - added SGraphNode::GetNodeIndicatorOverlayColor() and GetNodeIndicatorOverlayVisibility() delegates - added BP-specific delegate overrides to SGraphNodeK2Base; extended to include both compact and variable nodes Change 2946932 on 2016/04/18 by Mike.Beach Guarding against invalid EdGraphPins (ones that have been moved to the transient package) when constructing the widget - prevents a crash that we've been unable to repro or determine the cause of (turns it instead into an ensure, so we can collect more contextual information on the issue). #jira UE-26998 Change 2949968 on 2016/04/20 by Dan.Oconnor Array access out of bounds by value is a warning again, added ability to elevate individual warnings on a per project basis (or supress them) #jira UE-28971 Change 2950113 on 2016/04/20 by Dan.Oconnor Removed GBlueprintCompileTime, it was not accurate. Printing BlueprintCompileAndLoadTimerData.GetTime() at start instead Change 2951086 on 2016/04/21 by Ben.Cosh This change addresses the edge case in the blueprint profiler that caused stats to fail when tunnel nodes were linked through to other tunnel nodes. #jira UE-28750 - Crash compiling a Blueprint that contains a For Loop with profiler active #Proj Kismet, BlueprintProfiler Change 2951336 on 2016/04/21 by Ben.Cosh This change enables blueprint breakpoints during instrumented conditions. #jira UEBP-178 - Fix breakpoints under profiling conditions #Proj CoreUObject, BlueprintProfiler, UnrealEd, KismetCompiler Change 2951406 on 2016/04/21 by Ben.Cosh Fix for blueprint profiler stats for the top level blueprint stat entry not updating correctly. #Proj Kismet Change 2951832 on 2016/04/21 by Ben.Cosh Fix for certain blueprint profiler stats not being updated and collected at the blueprint container level due to incorrect tracepaths. #Proj Kismet #info This should fix the node heatmaps as a side effect. #Codereview Phillip.Kavan Change 2956696 on 2016/04/26 by Dan.Oconnor Tweak fix for macros being BS_Dirty after loading. The current fix had the side effect of not recompiling clients of the macro after making a change to the macro and entering PIE #jira UE-29495 Change 2957564 on 2016/04/27 by Maciej.Mroz Various fixes related to nativized enums. #jira UE-27735 Enumerators are not set correctly in packaged games if Nativize Blueprint Assets is set to true Change 2961626 on 2016/04/29 by Mike.Beach Merging //UE4/Dev-Main to Dev-Blueprints (//UE4/Dev-Blueprints) Change 2962747 on 2016/05/02 by Maciej.Mroz #jira UE-30123 Cannot use abilities in nativized Orion build GameplayAbility handles BPGC and DynamicClass the same way. C++ backend do not assumes that some literal objects are UserDefinesEnum/UserDefinedStruct/BlueprintGeneratedClass. Change 2965679 on 2016/05/04 by Maciej.Mroz Increased stack size (384k) of threads spawned by Task Graph. (Temporary?) fix for stack overflow, when amination in Orion are evaluated. Change 2965758 on 2016/05/04 by Maciej.Mroz #jira UE-30300 "ReturnToBase" ability does not work in nativized Orion. Fixed CDO creation in async loaded Dynamic Class. Fixed too restrict cast assertions. Change 2966543 on 2016/05/04 by Maciej.Mroz #jira UE-30235 Mac QAGame fails to package with nativization #jira UE-30282 Match3 nativized android package fails to build Change 2966839 on 2016/05/04 by Dan.Oconnor Typo IMPLEMENT_MODULE creates weird linking error, also may need entry in Target.cs to get BlueprintRuntime to build. Copying that pattern from ___LoadingScreen modules #jira UE-30333 Change 2967347 on 2016/05/05 by Maciej.Mroz #jira UE-30196 Unable to package a copy of project with Nativize Blueprints enabled CommandUtils.GetDirectoryName should not be used with directory path (but only with file path), because it cannot handle paths like "e:\My11Project 4.13" (containing '.'). It seems useless with directory path anyway. [CL 2968184 by Dan Oconnor in Main branch]
2016-05-05 18:28:40 -04:00
// passing true to FCompilerResultsLog's constructor would make this the
// primary compiler log (it is not) - the idea being that upon destruction
// the primary log prints a summary; well, since this isn't destructed at
// the end of compilation, and it blocks the full compiler log from
// becoming the "CurrentEventTarget", we pass false - we append logs
// collected by this one to the full compiler log later on anyways (so they won't be missed)
CachedMessageLog = MakeShareable(new FCompilerResultsLog(/*bIsCompatibleWithEvents =*/false));
Expression.Empty();
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::ValidateNodeDuringCompilation(FCompilerResultsLog& MessageLog) const
{
if (CachedMessageLog.IsValid())
{
MessageLog.Append(*CachedMessageLog);
}
// else, this may be some intermediate node in the compile, let's look at the errors from the original...
else
{
if(UObject const* SourceObject = MessageLog.FindSourceObject(this))
{
UK2Node_MathExpression const* MathExpression = nullptr;
// If the source object is a MacroInstance, we need to look elsewhere for the original MathExpression
if(Cast<UK2Node_MacroInstance>(SourceObject))
{
Copying //UE4/Dev-Blueprints to //UE4/Dev-Main (Source: //UE4/Dev-Blueprints @ 3152873) #lockdown Nick.Penwarden #rb none ========================== MAJOR FEATURES + CHANGES ========================== Change 3131279 on 2016/09/19 by Mike.Beach Fixing FText::Format warning for the Sub-Level Blueprints menu - Was using {LevelName} tag when there was no value for {LevelName} (defaulted to the first argument). #jira UE-36097 Change 3131318 on 2016/09/19 by Phillip.Kavan [UE-35690] Minor revisions to Blueprint SCS execution to improve efficiency and address an out-of-order registration issue. change summary: - modified AActor::PostSpawnInitialize() to defer native component registration if there is no native scene root component set and if the actor is a BP type (i.e. will invoke SCS). this means that native actor classes with only non-scene components will now defer registration/post-registration until after SCS execution has established a valid scene root. - modified AActor::ExecuteConstruction() to gather the set of native scene components that SCS nodes can attach to before invoking the SCS. this was previously being done redundantly within the SCS itself at each level of the BP class inheritance hierarchy. - modified AActor::ExecuteConstruction() to do a final registration pass over all components after the all SCS levels have been executed. this was also previously being done within the SCS at each level. this avoids some extra redundancy. - modified USCS_Node::ExecuteNodeOnActor() to call RegisterAllComponents() on the given actor instance after establishing a valid scene root component if it was previously deferred at spawn time. - modified USCS_Node::ExecuteNodeOnActor() to now register components after they're created. since SCS execution goes from parent to child, parent scene components will always be registered before their children. non-native, non-scene component registration will also be deferred until a scene root component has been established. - added AActor::HasDeferredComponentRegistration() - modified AActor::RegisterAllComponents() to reset the actor's deferred component registration flag when called - modified AActor::AddComponent() to check the 'bAutoRegister' flag before calling RegisterComponent() (for consistency) - moved the RegisterInstancedComponent() utility method into USimpleConstructionScript and modified it to ensure that parent attachments are registered before their children. - modified USimpleConstructionScript::ExecuteScriptOnActor() to include an additional input parameter for passing in the set of native scene components that can be attached to. - modified USimpleConstructionScript::ExecuteScriptOnActor() to remove redundant/unnecessary work as noted above. #jira UE-35690 Change 3131842 on 2016/09/20 by Maciej.Mroz #jira UE-34984 Broken (weak) object params on Blueprint function (cannot add plain reference) Change 3131847 on 2016/09/20 by Maciej.Mroz SPropertyEditorAsset doesn't display UClass with "_C" prefix anymore. Change 3131923 on 2016/09/20 by Maciej.Mroz #jira UE-33812 Crash while closing Create Blank New Blueprint window after attempting to name blueprint the same name as another blueprint Change 3132348 on 2016/09/20 by Phillip.Kavan Fix CIS build issue (SA). Change 3132383 on 2016/09/20 by Maciej.Mroz #jira UE-35830 Float Curve that is set as a local variable in an actor's function is garbage collected when in Standalone, causing a crash Array UStruct::ScriptObjectReferences is filled while compilation. GC doesn't serialize script bytecode in editor anymore. Change 3133072 on 2016/09/20 by Maciej.Mroz #jira UE-34388 Crash upon deleting Blueprints folder Change 3133216 on 2016/09/20 by Dan.Oconnor + BlueprintSetLibrary (add, remove, find, etc) + HasGetTypeHash compile time function for detecting types that have GetTypeHish (modeled after HasOperatorEquals) = SPinTypeSelector can now disable container types based on current primary type, required transition to SComboButtton/SListView from SComboBox = Hide blueprint set library via BaseEditor.ini #jira UE-2114 Change 3133227 on 2016/09/20 by Dan.Oconnor Test assets for TSet Change 3133804 on 2016/09/21 by Maciej.Mroz #jira UE-34069 ObjectLibrary stores UBlueprint instead of BPGC In UObjectLibrary, when bHasBlueprintClasses is true, BP references are automatically replaced by BPGC references. Change 3133817 on 2016/09/21 by Maciej.Mroz Fixed static Static Analysis warning Change 3134377 on 2016/09/21 by Dan.Oconnor ShowWorldContextObject is now inherited #jira UE-35674 Change 3134955 on 2016/09/21 by Mike.Beach Making it so AdvancedDisplay metadata is taken into consideration and used in MakeStruct nodes. Change 3134965 on 2016/09/21 by Dan.Oconnor Fix for crash in FCDODiffControl when CDOs have different numbers of properties. First branch in the while loop would incorrectly advance Iter past the end of the array. Comments courtesy of Jon.Nabozny #jira UE-36263 Change 3135523 on 2016/09/22 by Dan.Oconnor PR #2755: Master (Contributed by jeremyyeung) Notable change: searching for Vector in BP editor context menu now gives different default result, prior result was mediocre, though (vector - vector) #jira UE-35450 Change 3136508 on 2016/09/22 by Mike.Beach Removing a bIsVisible guard for level Blueprint menu actions - this was causing level BP options to disappear when you hid sub-levels. The guard doesn't seem to matter, as those actions will be removed with the world (when it is updated, or unloaded). #jira UE-34019 Change 3137587 on 2016/09/23 by Maciej.Mroz #jira ODIN-1017 [Nativization] Crash while loading Hub_env level Merged cl#3137578 from Odin branch Change 3137666 on 2016/09/23 by Ben.Cosh This adds the ability to map composite graph instances in the same way we map macro instances for blueprint debug data and improves the quality of the debug data providing correct information for nested macro/composite instances at any script location in instrumented blueprint compilations. #Jira UE-33396 - Nested macro nodes don't map correctly if you place multiple instances in the same graph #Proj KismetCompiler, BlueprintGraph, UnrealEd - This is the first part of a two part change, the subsequent change will make use of the debug output to resolve complex trees of tunnel instances in the blueprint profiler. Change 3137800 on 2016/09/23 by Phillip.Kavan [UE-34896] Properties are now generated for client-only Blueprint components in an uncooked server-only context. change summary: - bumped BlueprintObjectsVersion - added a new 'ComponentClass' property to USCS_Node - added a new 'ComponentClass' field to the FComponentOverrideRecord struct (UInheritableComponentHandler) - added a USCS_Node::Serialize() override to fix up 'ComponentClass' on load (so that it's set prior to compile-on-load) - modified USimpleConstructionScript::CreateNodeImpl() to set the ComponentClass property in a new SCS node - modified USimpleConstructionScript::ValidateNodeTemplates() to consider the node to be valid if ComponentClass is set and is known to be filtered (i.e. the node will not be removed in this case) - modified USimpleConstructionScript::ValidateNodeTemplates() to emit a warning message in an uncooked client-only or server-only context if the ComponentClass could not be set in an existing package (i.e. if a resave is needed) - modified UInheritableComponentHandler::PostLoad() to fix up 'ComponentClass' on load - modified UInheritableComponentHandler::CreateOverridenComponentTemplate() to set the ComponentClass field in a new override record - modified UInheritableComponentHandler::IsRecordValid() to consider the record to be valid if ComponentClass is set (when ComponentTemplate is NULL) - modified UInheritableComponentHandler::IsRecordNecessary() to consider the record to be necessary if ComponentClass is set and is known to be filtered - modified FKismetCompilerContext::CreateClassVariablesFromBlueprint() to use 'ComponentClass' rather than 'ComponentTemplate' to infer the property subtype #jira UE-34896 Change 3137851 on 2016/09/23 by Phillip.Kavan [UE-36079] Component overrides in a child blueprint will no longer trigger a warning message when the original component is removed from its parent. change summary: - modified UInheritableComponentHandler::IsRecordValid() to no longer consider a NULL OriginalTemplate to be invalid (so that the warning message is suppressed) - modified UInheritableComponentHandler::IsRecordNecessary() to consider a NULL OriginalTemplate to be unnecessary (so that the record is still removed in this case) #jira UE-36079 Change 3137948 on 2016/09/23 by Ben.Cosh CIS warning fix on mac for out of order initialisation. Change 3139351 on 2016/09/25 by Ben.Cosh Updates the blueprint profiler to make use of the recent changes to macro/composite tunnel mapping and enhanced debug data. #Jira UE-33396 - Nested macro nodes don't map correctly if you place multiple instances in the same graph #Proj BlueprintProfiler, Engine - This is the second part of a two part change enabling multiple instances of nested macro/composite graphs in the blueprint profiler. Change 3139376 on 2016/09/25 by Ben.Cosh CIS static analysis fix for CL 3137666 Change 3139377 on 2016/09/25 by Ben.Cosh Adding script code location checking for pure nodes that was missed in CL 3139351 #Jira UE-33396 - Nested macro nodes don't map correctly if you place multiple instances in the same graph #Proj BlueprintProfiler - Fixes a missed issue with pure nodes inside macros/tunnels Change 3139624 on 2016/09/26 by Maciej.Mroz Fixed const local variables in Nativized code Merged cl#3139622 from Odin branch. Change 3139641 on 2016/09/26 by Maciej.Mroz #jira UE-31099 Renaming an input mapping does not generate a warning when compile a blueprint using that input Since we cannot distinguish which nodes are isolated by users (and shouldn't be validated) and which nodes are isolated during expansion step (and should be validated), the isolated nodes are pruned both before and after expantion step (and validation). Change 3139961 on 2016/09/26 by Ben.Cosh CIS static analysis fix for CL 3137666 - missed one of the warnings in a previous attempt. Change 3140143 on 2016/09/26 by Dan.Oconnor Fix for component property clearing on load, submitted on behalf of Mike.Beach #jira UE-36395 Change 3140694 on 2016/09/26 by Dan.Oconnor Fix for GLEO when duplicating levels that have knots that reference delegates (specifically custom events) #jira UE-34954 Change 3140772 on 2016/09/26 by Dan.Oconnor Further hardening SGraphPin::GraphPinObj access #jira UE-36280 Change 3140812 on 2016/09/26 by Dan.Oconnor Corrected overzealous warning. Codepath is expected when functions are deleted but breakpoints aren't updated #jira UE-32736 Change 3140869 on 2016/09/26 by Dan.Oconnor Update check to handle nested DSOs #jira UE-34568 Change 3141125 on 2016/09/27 by Maciej.Mroz #jira UE-36326 Attempting to generate abstract class from blueprint crashes editor on compile While reinstancing the CLASS_Abstract is cleared (just like the CLASS_Deprecated flag) Change 3142715 on 2016/09/27 by Dan.Oconnor Fix for crash when pasting nodes that have connections to nodes that aren't in the clipboard from one graph into another #jira OR-29584 Change 3143469 on 2016/09/28 by Ryan.Rauschkolb BP Profiler: Fixed Assert when profiling parent/child Blueprint #jira UE-35487 Change 3145215 on 2016/09/29 by Maciej.Mroz #jira UE-36494 [CrashReport] UE4Editor_KismetCompiler!FKismetCompilerContext::CreatePinEventNodeForTimelineFunction() [kismetcompiler.cpp:2062] Change 3145580 on 2016/09/29 by Dan.Oconnor Collapse secondary image instead of hiding it, allowing x button to be closer to primary image when secondary type image isn't present #jira UE-36577 Change 3146470 on 2016/09/30 by Maciej.Mroz #jira UE-36655 Failed ensure when TIleline is pasted Restored cl#3085572 - it was lost while merging. Change 3147046 on 2016/09/30 by Maciej.Mroz #jira UE-34961 Assert when calling BP function with weak object parameter. BP doesn;t support weak obj ptr as parameters - Validation added. Function, created from colappsed nodes, cannot have a parameter of weakptr type. Change 3148022 on 2016/10/01 by Phillip.Kavan [UE-21109] Fix component instance data loss after renaming SCS component nodes at the Blueprint class level. change summary: - deprecated the public USCS_Node::VariableName member and replaced it with an internal-only member accessible via Get/Set method (i need to be in control of the set logic) - changed all occurrences of direct access to USCS_Node::VariableName to use a GetVariableName() call (since it's now internal) - simplified USCS_Node::GetVariableName() as what it used to do was legacy and thus is no longer necessary (it's been handled by USimpleConstructionScript::PostLoad for awhile now) - added USCS_Node::SetVariableName(); this now renames the component template (if valid) and all instances of it prior to changing the internal variable name. this ensures that archetype lookups will continue to function after a rename. - added USCS_Node::RenameComponentTemplate() to handle SCS node component template rename logic on a variable name change - switched the AActor::CheckComponentInstanceName() API to be publically-accessible; need to call this when renaming instanced components to match a new variable name in order to ensure that we rename any instance-only components out of the way first (this is the same logic that we run when constructing component instances on map load/RRCS, so it's consistent) - modified UInheritableComponentHandler::PostLoad() to fix up the component template within each record to match the original template object name. this ensures that ICH-specific archetype lookups will continue to function after a rename. it also ensures that any mismatched template names in existing assets will now be fixed up on load. - moved UActorComponent::ComponentTemplateSuffixName into the USimpleConstructionScript class (since the association is tied to templates created by that class specifically). note: this revises a change from a recent PR submission. - modified UpdateAttachedIsEditorOnly() to check the RF_ArchetypeObject flag rather than the ComponentTemplateSuffixName (part of the revision to the recent PR submission noted above) #jira UE-21109 Change 3148023 on 2016/10/01 by Phillip.Kavan [UE-35562] Fix inherited Blueprinted component template defaults data loss in child Blueprint classes after recompiling the Blueprinted component class. change summary: - added a local FArchetypeReinstanceHelper struct + GetArchetypeObjects()/FindUniqueArchetypeObjectName() utility method implementations to KismetReinstanceUtilities.cpp - modified FBlueprintCompileReinstancer::ReplaceInstancesOfClass_Inner() to ensure that the full inherited component template (archetype) ancestry is renamed along with the base template when we rename the base template object away from its original name in order to make way for the new (reinstanced) template. the names must match the base template along the entire inheritance hierarchy in order for forward/reverse inherited component archetype lookups to succeed. notes: - BP (non-native) component templates (e.g. SCS/AddComponent nodes) that belong to the class being reinstanced (i.e. whose templates are not inherited from a parent BP) always inherit directly from the component CDO, and thus do not require this code path (that is, archetype lookups are not dependent on matching the CDO by name). similarly, native components (defined in C++) are included as part of the CDO defaults data, and thus also do not require this code path. #jira UE-35562 Change 3148030 on 2016/10/01 by Phillip.Kavan UT fixes for CIS warnings related to SCS node API changes. Change 3148256 on 2016/10/02 by Ben.Cosh This change adds the ability to filter debug/wire trace instrumenation and tracks expansion nodes for future use. #Jira UE-34866 - No profiler timings listed for nodes executed after an interface message #Proj KismetCompiler, BlueprintGraph, UnrealEd Change 3148261 on 2016/10/02 by Ben.Cosh CIS fix, some code from another changelist leaked into CL 3148256 Change 3148480 on 2016/10/03 by Ben.Cosh This change attempts to address some profiler issues with class function context switching in the blueprint profiler. #Jira UE-35819 - Crash occurs when instrumenting an event from a member actor #Proj BlueprintProfiler, BlueprintGraph Change 3148545 on 2016/10/03 by Phillip.Kavan Skip unnecessary component validation work to fix invalid warnings when duplicating a BP class for reinstancing. Change 3149001 on 2016/10/03 by Ben.Cosh This fixes an issue found with the instrumented blueprint compilations in which a certain compilation path would not provide the extended composite tunnel node heararchy in debug data and removes an unneceassary check that was causing problems. #Jira UE-36704 - Crash on PIE while profiling TestBP_ProfilerEvents in QAGame #Proj KismetCompiler, BlueprintProfiler Change 3149031 on 2016/10/03 by Maciej.Mroz #jira UE-36687, UE-36691 Tunnel nodes without exec pin are not pruned before expansion. Change 3149150 on 2016/10/03 by Maciej.Mroz #jira UE-36685 UGameplayTagsK2Node_LiteralGameplayTag is pure. Change 3149290 on 2016/10/03 by Maciej.Mroz #jira UE-36750 GetBlueprintContext node is Pure. Change 3149595 on 2016/10/03 by Mike.Beach Fixing up some Orion content errors/warnings that should have been issues a while ago (error reporting was broken for a time, now fixed). #jira UE-36758, UE-36759 Change 3149667 on 2016/10/03 by Mike.Beach Fixing up some Ocean content errors as fallout from a change in Dev-Blueprints - the errors properly identified a node that was using a culled input that was uninitialized. #jira UE-36770 Change 3149777 on 2016/10/03 by Mike.Beach Fixing up an Orion content warning - disconnecting a cast path in Hero_Automation, now that the node is producing a warning (the cast is impossible, and therefore the path is superfluous). #jira UE-36759 Change 3149988 on 2016/10/04 by Maciej.Mroz #jira UE-36750, UE-36685 Fixed IsNodePure functions. Change 3150146 on 2016/10/04 by Maciej.Mroz #jira UE-36759 First pruning pass in done after ExpandTunnelsAndMacros is called. Isolated tunnels are pruned just like regular nodes. Change 3150743 on 2016/10/04 by Mike.Beach Mirroring CL 3150661 from Dev-VREditor Fix for crash on editor close after VR Foliage Editing. #jira UE-36754 Change 3151104 on 2016/10/04 by Maciej.Mroz Added comment. Change 3151979 on 2016/10/05 by Mike.Beach Adding the keyword "custom" to K2Node_CustomEvent, so that it is prioritized when searching the Blueprint menu. #jira UE-35512 Change 3152286 on 2016/10/05 by Maciej.Mroz Make sure, that an isolated node, that should be pure (but is not) won't be pruned. [CL 3152997 by Mike Beach in Main branch]
2016-10-05 23:32:35 -04:00
MathExpression = CastChecked<UK2Node_MathExpression>(MessageLog.GetSourceTunnelNode(this));
}
else
{
MathExpression = MessageLog.FindSourceObjectTypeChecked<UK2Node_MathExpression>(this);
}
// Should always be able to find the source math expression
check(MathExpression);
// if the expressions match, then the errors should
check(MathExpression->Expression == Expression);
// take the same errors from the original node (so we don't have to
// re-parse/re-gen to fish out the same errors)
if (MathExpression->CachedMessageLog.IsValid())
{
MessageLog.Append(*MathExpression->CachedMessageLog);
}
}
}
}
//------------------------------------------------------------------------------
FText UK2Node_MathExpression::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
if (Expression.IsEmpty() && (TitleType == ENodeTitleType::MenuTitle))
{
return LOCTEXT("AddMathExprMenuOption", "Add Math Expression...");
}
else if (TitleType != ENodeTitleType::FullTitle)
{
if (CachedDisplayExpression.IsOutOfDate(this))
{
FExpressionParser Parser;
TSharedPtr<IFExpressionNode> ExpressionRoot = Parser.ParseExpression(Expression);
if(Parser.IsValid())
{
CachedDisplayExpression.SetCachedText(FText::FromString(SanitizeDisplayExpression(ExpressionRoot->ToDisplayString(GetBlueprint()))), this);
}
else
{
// Fallback and display the expression in it's raw form
CachedDisplayExpression.SetCachedText(FText::FromString(Expression), this);
}
}
return CachedDisplayExpression;
}
else if (CachedNodeTitle.IsOutOfDate(this))
{
FExpressionParser Parser;
TSharedPtr<IFExpressionNode> ExpressionRoot = Parser.ParseExpression(Expression);
if(Parser.IsValid())
{
CachedDisplayExpression.SetCachedText(FText::FromString(SanitizeDisplayExpression(ExpressionRoot->ToDisplayString(GetBlueprint()))), this);
}
CachedNodeTitle.SetCachedText(GetFullTitle(CachedDisplayExpression), this);
}
return CachedNodeTitle;
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::PostPlacedNewNode()
{
bMadeAfterRotChange = true;
Super::PostPlacedNewNode();
FEdGraphUtilities::RenameGraphToNameOrCloseToName(BoundGraph, "MathExpression");
}
//------------------------------------------------------------------------------
void UK2Node_MathExpression::ReconstructNode()
{
if (!HasAnyFlags(RF_NeedLoad))
{
RebuildExpression(Expression);
}
Super::ReconstructNode();
}
//------------------------------------------------------------------------------
FString UK2Node_MathExpression::SanitizeDisplayExpression(FString InExpression) const
{
// We do not want the outermost parentheses in the display expression, they add nothing to the logical comprehension
InExpression.RemoveFromStart(TEXT("("));
InExpression.RemoveFromEnd(TEXT(")"));
return InExpression;
}
FText UK2Node_MathExpression::GetFullTitle(FText InExpression) const
{
// FText::Format() is slow, so we cache this to save on performance
return FText::Format(LOCTEXT("MathExpressionSecondTitleLine", "{0}\nMath Expression"), InExpression);
}
void UK2Node_MathExpression::FindDiffs(class UEdGraphNode* OtherNode, struct FDiffResults& Results )
{
UK2Node_MathExpression* MathExpression1 = this;
UK2Node_MathExpression* MathExpression2 = Cast<UK2Node_MathExpression>(OtherNode);
// Compare the visual display of a math expression (the visual display involves consolidating variable Guid's into readable parameters)
FText Expression1 = MathExpression1->GetNodeTitle(ENodeTitleType::EditableTitle);
FText Expression2 = MathExpression2->GetNodeTitle(ENodeTitleType::EditableTitle);
if (Expression1.CompareTo(Expression2) != 0)
{
FDiffSingleResult Diff;
Diff.Node1 = MathExpression2;
Diff.Node2 = MathExpression1;
Diff.Diff = EDiffType::NODE_PROPERTY;
FText NodeName = GetNodeTitle(ENodeTitleType::ListView);
FFormatNamedArguments Args;
Args.Add(TEXT("Expression1"), Expression1);
Args.Add(TEXT("Expression2"), Expression2);
Diff.ToolTip = FText::Format(LOCTEXT("DIF_MathExpressionToolTip", "Math Expression '{Expression1}' changed to '{Expression2}'"), Args);
Diff.DisplayColor = FLinearColor(0.85f,0.71f,0.25f);
Diff.DisplayString = FText::Format(LOCTEXT("DIF_MathExpression", "Math Expression '{Expression1}' changed to '{Expression2}'"), Args);
Results.Add(Diff);
}
}
#undef LOCTEXT_NAMESPACE