private static void PopulateNameValuePairData(JsonNode nameValuePairNode, Dictionary <string, NameValuePairData> nameValuePairData) { JsonTree.VisitTree( nameValuePairNode, node => { nameValuePairData[node.Name] = new NameValuePairData( node.Name, node.Values.Count > 0 ? node.Values[0] : CacheMissAnalysisUtilities.RepeatedStrings.MissingValue); }, recurse: false); }
/// <summary> /// Diffs weak fingerprints. /// </summary> /// <param name="weakFingerprint">Weak fingerprint.</param> /// <param name="weakFingerprintTree">Weak fingerprint tree.</param> /// <param name="otherWeakFingerprint">Other weak fingerprint.</param> /// <param name="otherWeakFingerprintTree">Other weak fingerprint tree.</param> /// <returns></returns> public static JObject DiffWeakFingerprints( string weakFingerprint, JsonNode weakFingerprintTree, string otherWeakFingerprint, JsonNode otherWeakFingerprintTree) { JObject result = new JObject(); if (weakFingerprint == otherWeakFingerprint) { return(result); } // { // WeakFingerprint: { Old: old_weak_fingerprint, New: new_weak_fingerprint } // } AddPropertyIfNotNull(result, RenderSingleValueDiff("WeakFingerprint", weakFingerprint, otherWeakFingerprint)); using (var weakFingerprintDataPool = JsonNodeMapPool.GetInstance()) using (var otherWeakFingerprintDataPool = JsonNodeMapPool.GetInstance()) { var weakFingerprintData = weakFingerprintDataPool.Instance; var otherWeakFingerprintData = otherWeakFingerprintDataPool.Instance; JsonTree.VisitTree(weakFingerprintTree, wfNode => weakFingerprintData[wfNode.Name] = wfNode, recurse: false); JsonTree.VisitTree(otherWeakFingerprintTree, wfNode => otherWeakFingerprintData[wfNode.Name] = wfNode, recurse: false); var fields = new HashSet <string>(weakFingerprintData.Keys.Concat(otherWeakFingerprintData.Keys)); foreach (var field in fields) { bool getFieldNode = weakFingerprintData.TryGetValue(field, out JsonNode fieldNode); bool getOtherFieldNode = otherWeakFingerprintData.TryGetValue(field, out JsonNode otherFieldNode); if (getFieldNode != getOtherFieldNode) { string fieldValue = getFieldNode ? (fieldNode.Values != null && fieldNode.Values.Count == 1 ? fieldNode.Values[0] : CacheMissAnalysisUtilities.RepeatedStrings.ExistentValue) : CacheMissAnalysisUtilities.RepeatedStrings.UnspecifiedValue; string otherFieldValue = getOtherFieldNode ? (otherFieldNode.Values != null && otherFieldNode.Values.Count == 1 ? otherFieldNode.Values[0] : CacheMissAnalysisUtilities.RepeatedStrings.ExistentValue) : CacheMissAnalysisUtilities.RepeatedStrings.UnspecifiedValue; AddPropertyIfNotNull(result, RenderSingleValueDiff(field, fieldValue, otherFieldValue)); } else if (getFieldNode && getOtherFieldNode) { Contract.Assert(fieldNode != null); Contract.Assert(otherFieldNode != null); AddPropertyIfNotNull(result, DiffWeakFingerprintField(fieldNode, otherFieldNode)); } } } Contract.Assert(result.Count > 0); return(result); }
private static JProperty DiffWeakFingerprintField(JsonNode fieldNode, JsonNode otherFieldNode) { Contract.Requires(fieldNode != null); Contract.Requires(otherFieldNode != null); Contract.Requires(fieldNode.Name == otherFieldNode.Name); switch (fieldNode.Name) { case nameof(Process.Dependencies): { using (var inputFileDataPool = InputFileDataMapPool.GetInstance()) using (var otherInputFileDataPool = InputFileDataMapPool.GetInstance()) { var inputFileData = inputFileDataPool.Instance; var otherInputFileData = otherInputFileDataPool.Instance; populateInputFileData(fieldNode, inputFileData); populateInputFileData(otherFieldNode, otherInputFileData); return(ExtractUnorderedMapDiff( inputFileData, otherInputFileData, (dOld, dNew) => dOld.Equals(dNew), out var added, out var removed, out var changed) ? new JProperty(fieldNode.Name, RenderUnorderedMapDiff( inputFileData, otherInputFileData, added, removed, changed, RenderPath, (dataA, dataB) => dataA.HashOrContent)) : null); } } case nameof(Process.FileOutputs): { using (var outputFileDataPool = OutputFileDataMapPool.GetInstance()) using (var otherOutputFileDataPool = OutputFileDataMapPool.GetInstance()) { var outputFileData = outputFileDataPool.Instance; var otherOutputFileData = otherOutputFileDataPool.Instance; populateOutputFileData(fieldNode, outputFileData); populateOutputFileData(otherFieldNode, otherOutputFileData); return(ExtractUnorderedMapDiff( outputFileData, otherOutputFileData, (dOld, dNew) => dOld.Equals(dNew), out var added, out var removed, out var changed) ? new JProperty(fieldNode.Name, RenderUnorderedMapDiff( outputFileData, otherOutputFileData, added, removed, changed, RenderPath, (dataA, dataB) => dataA.Attributes)) : null); } } case nameof(PipFingerprintField.ExecutionAndFingerprintOptions): case nameof(Process.EnvironmentVariables): { var result = DiffNameValuePairs(fieldNode, otherFieldNode); return(result != null ? new JProperty(fieldNode.Name, result) : null); } case nameof(Process.DirectoryDependencies): case nameof(Process.DirectoryOutputs): case nameof(Process.UntrackedPaths): case nameof(Process.UntrackedScopes): case nameof(Process.PreserveOutputAllowlist): case nameof(Process.SuccessExitCodes): case PipFingerprintField.Process.SourceChangeAffectedInputList: case nameof(Process.ChildProcessesToBreakawayFromSandbox): { var data = fieldNode.Values; var otherData = otherFieldNode.Values; return(ExtractUnorderedListDiff(data, otherData, out var added, out var removed) ? new JProperty(fieldNode.Name, RenderUnorderedListDiff(added, removed, RenderPath)) : null); } default: return(RenderSingleValueDiff(fieldNode.Name, getSingleValueNode(fieldNode), getSingleValueNode(otherFieldNode))); } string getSingleValueNode(JsonNode node) => node.Values.Count > 0 ? node.Values[0] : CacheMissAnalysisUtilities.RepeatedStrings.MissingValue; void populateInputFileData(JsonNode dependencyNode, Dictionary <string, InputFileData> inputFileData) { JsonTree.VisitTree( dependencyNode, node => { string value = CacheMissAnalysisUtilities.RepeatedStrings.MissingValue; if (node.Values.Count > 0) { value = node.Values[0]; } else if (node.Children.First != null && node.Children.First.Value.Name == PipFingerprintField.FileDependency.PathNormalizedWriteFileContent && node.Children.First.Value.Values.Count > 0) { value = node.Children.First.Value.Values[0]; } inputFileData[node.Name] = new InputFileData(node.Name, value); }, recurse: false); } void populateOutputFileData(JsonNode outputNode, Dictionary <string, OutputFileData> outputFileData) { JsonTree.VisitTree( outputNode, node => { string value = CacheMissAnalysisUtilities.RepeatedStrings.MissingValue; if (node.Children.First != null && node.Children.First.Value.Name == PipFingerprintField.FileOutput.Attributes && node.Children.First.Value.Values.Count > 0) { value = node.Children.First.Value.Values[0]; } outputFileData[node.Name] = new OutputFileData(node.Name, value); }, recurse: false); } }