diff --git a/src/Engine/ProtoCore/CodeGen.cs b/src/Engine/ProtoCore/CodeGen.cs index a5d7c02e574..f188c143f9b 100644 --- a/src/Engine/ProtoCore/CodeGen.cs +++ b/src/Engine/ProtoCore/CodeGen.cs @@ -163,10 +163,10 @@ protected void GenerateCallsiteIdentifierForGraphNode(AssociativeGraph.GraphNode // Build the unique ID for a callsite string callsiteIdentifier = procName + - "_InClassDecl" + globalClassIndex + - "_InFunctionScope" + globalProcIndex + - "_Instance" + functionCallInstance.ToString() + - "_" + graphNode.guid.ToString(); + Constants.kSingleUnderscore + Constants.kInClassDecl + globalClassIndex + + Constants.kSingleUnderscore + Constants.kInFunctionScope + globalProcIndex + + Constants.kSingleUnderscore + Constants.kInstance + functionCallInstance + + Constants.kSingleUnderscore + graphNode.guid; // TODO Jun: Address this in MAGN-3774 // The current limitation is retrieving the cached trace data for multiple callsites in a single CBN diff --git a/src/Engine/ProtoCore/DSASM/Defs.cs b/src/Engine/ProtoCore/DSASM/Defs.cs index 051b6721611..137d51f8345 100644 --- a/src/Engine/ProtoCore/DSASM/Defs.cs +++ b/src/Engine/ProtoCore/DSASM/Defs.cs @@ -395,6 +395,10 @@ public struct Constants public const string kDoubleUnderscores = "__"; public const string kSingleUnderscore = "_"; public const string kTempVarForTypedIdentifier = "%tTypedIdent"; + + internal const string kInClassDecl = "InClassDecl"; + internal const string kInFunctionScope = "InFunctionScope"; + internal const string kInstance = "Instance"; } public enum MemoryRegion diff --git a/src/Engine/ProtoCore/Lang/CallSite.cs b/src/Engine/ProtoCore/Lang/CallSite.cs index e8a423a38a6..aaf8e0ecd40 100644 --- a/src/Engine/ProtoCore/Lang/CallSite.cs +++ b/src/Engine/ProtoCore/Lang/CallSite.cs @@ -75,14 +75,12 @@ public bool HasAnyNestedData if (HasData) return true; - else - { - //Not empty, and doesn't have data so test recursive - Validity.Assert(NestedData != null, - "Invalid recursion logic, this is a VM bug, please report to the Dynamo Team"); + + //Not empty, and doesn't have data so test recursive + Validity.Assert(NestedData != null, + "Invalid recursion logic, this is a VM bug, please report to the Dynamo Team"); - return NestedData.Any(srtd => srtd.HasAnyNestedData); - } + return NestedData.Any(srtd => srtd.HasAnyNestedData); } } @@ -172,26 +170,21 @@ public ISerializable GetLeftMostData() { if (HasData) return Data; - else - { - if (!HasNestedData) - return null; - else - { + + if (!HasNestedData) + return null; #if DEBUG - Validity.Assert(NestedData != null, "Nested data has changed null status since last check, suspected race"); - Validity.Assert(NestedData.Count > 0, "Empty subnested array, please file repo data with @lukechurch, relates to MAGN-4059"); + Validity.Assert(NestedData != null, "Nested data has changed null status since last check, suspected race"); + Validity.Assert(NestedData.Count > 0, "Empty subnested array, please file repo data with @lukechurch, relates to MAGN-4059"); #endif - //Safety trap to protect against an empty array, need repro test to figure out why this is getting set with empty arrays - if (NestedData.Count == 0) - return null; + //Safety trap to protect against an empty array, need repro test to figure out why this is getting set with empty arrays + if (NestedData.Count == 0) + return null; - SingleRunTraceData nestedTraceData = NestedData[0]; - return nestedTraceData.GetLeftMostData(); - } - } + SingleRunTraceData nestedTraceData = NestedData[0]; + return nestedTraceData.GetLeftMostData(); } public List NestedData; @@ -238,7 +231,7 @@ public List RecursiveGetNestedData() /// /// TraceBinder is used to find assemblies to be used for - /// deserialization in cases where the exact assemlby that was + /// deserialization in cases where the exact assembly that was /// used in the serialization is not available. /// internal class TraceBinder : SerializationBinder @@ -286,7 +279,7 @@ public override System.Type BindToType(string assemblyName, string typeName) /// Normal usage patten is: /// 1. Instantiate /// 2. Push Trace data from callsite - /// 3. Call GetObjectData to serialise it onto a stream + /// 3. Call GetObjectData to serialize it onto a stream /// 4. Recreate using the special constructor /// [Serializable] @@ -325,14 +318,13 @@ public TraceSerialiserHelper(SerializationInfo info, StreamingContext context) #if DEBUG Debug.WriteLine("Deserialization of trace data failed."); #endif - continue; } } } /// - /// Save the data into the standard serialisation pattern + /// Save the data into the standard serialization pattern /// public void GetObjectData(SerializationInfo info, StreamingContext context) { @@ -615,7 +607,7 @@ public bool WillCallReplicate(Context context, List arguments, /// /// Call this method to obtain the Base64 encoded string that - /// represent this instance of CallSite;s trace data + /// represents this callsite instance's trace data /// /// Returns the Base64 encoded string that represents the /// trace data of this callsite diff --git a/src/Engine/ProtoCore/RuntimeData.cs b/src/Engine/ProtoCore/RuntimeData.cs index 8c15eb048cf..a473c21fb06 100644 --- a/src/Engine/ProtoCore/RuntimeData.cs +++ b/src/Engine/ProtoCore/RuntimeData.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using ProtoCore.AssociativeGraph; using ProtoCore.DSASM; using ProtoCore.Utils; @@ -25,11 +26,23 @@ public class RuntimeData private Dictionary> uiNodeToSerializedDataMap = null; + + private static readonly string identifierPattern = @"([a-zA-Z-_@0-9]+)"; + private static readonly string indexPattern = @"([-+]?\d+)"; + private static readonly string callsiteIDPattern = + identifierPattern + + Constants.kInClassDecl + indexPattern + + Constants.kSingleUnderscore + Constants.kInFunctionScope + indexPattern + + Constants.kSingleUnderscore + Constants.kInstance + indexPattern + + identifierPattern; + private static readonly string joinPattern = ';' + callsiteIDPattern; + private static readonly string fullCallsiteID = callsiteIDPattern + string.Format("({0})*", joinPattern); + public IDictionary CallsiteCache { get; set; } /// /// Map from a callsite's guid to a graph UI node. /// - public Dictionary CallSiteToNodeMap { get; private set; } + public Dictionary CallSiteToNodeMap { get; } #endregion @@ -289,7 +302,7 @@ public void SetTraceDataForNodes( /// /// Call this method to pop the top-most serialized callsite trace data. - /// Note that this call only pops off a signle callsite trace data + /// Note that this call only pops off a single callsite trace data /// belonging to a given UI node denoted by the given node guid. /// /// The Guid of a given UI node whose top-most @@ -320,7 +333,7 @@ private string GetAndRemoveTraceDataForNode(Guid nodeGuid, string callsiteID) { for (int i = 0; i < callsiteDataList.Count; i++) { - if (callsiteDataList[i].ID == callsiteID) + if (DoCallSiteIDsMatch(callsiteID, callsiteDataList[i].ID)) { callsiteTraceData = callsiteDataList[i].Data; callsiteDataList.RemoveAt(i); @@ -344,10 +357,32 @@ private string GetAndRemoveTraceDataForNode(Guid nodeGuid, string callsiteID) { return GetAndRemoveTraceDataForNode(nodeGuid, string.Empty); } - else + + return callsiteTraceData; + } + + private static bool DoCallSiteIDsMatch(string compilerGenerated, string deserialized) + { + if (compilerGenerated == deserialized) return true; + + var matches1 = Regex.Match(compilerGenerated, fullCallsiteID); + var matches2 = Regex.Match(deserialized, fullCallsiteID); + + if (matches1.Groups.Count != matches2.Groups.Count) return false; + + // If both group counts match, they should number 12 in all. + // We should ignore checking for the 1st, 7th, and 10th group specifically + // as per the Regex pattern (for fullCallsiteID) since that group includes the function scope + // that can vary for custom nodes or DS functions that make nested calls to + // host element creation methods. + for (int i = 0; i < matches1.Groups.Count; i++) { - return callsiteTraceData; + if (i == 0 || i == 6 || i == 9) continue; + + if (matches1.Groups[i].Value != matches2.Groups[i].Value) return false; } + + return true; } #endregion // Trace Data Serialization Methods/Members