GitHub Actions Runner

This commit is contained in:
Tingluo Huang
2019-10-10 00:52:42 -04:00
commit c8afc84840
1255 changed files with 198670 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[DataContract]
[JsonObject]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class ArrayContextData : PipelineContextData, IEnumerable<PipelineContextData>, IReadOnlyArray
{
public ArrayContextData()
: base(PipelineContextDataType.Array)
{
}
[IgnoreDataMember]
public Int32 Count => m_items?.Count ?? 0;
public PipelineContextData this[Int32 index] => m_items[index];
Object IReadOnlyArray.this[Int32 index] => m_items[index];
public void Add(PipelineContextData item)
{
if (m_items == null)
{
m_items = new List<PipelineContextData>();
}
m_items.Add(item);
}
public override PipelineContextData Clone()
{
var result = new ArrayContextData();
if (m_items?.Count > 0)
{
result.m_items = new List<PipelineContextData>(m_items.Count);
foreach (var item in m_items)
{
result.m_items.Add(item);
}
}
return result;
}
public override JToken ToJToken()
{
var result = new JArray();
if (m_items?.Count > 0)
{
foreach (var item in m_items)
{
result.Add(item?.ToJToken() ?? JValue.CreateNull());
}
}
return result;
}
public IEnumerator<PipelineContextData> GetEnumerator()
{
if (m_items?.Count > 0)
{
foreach (var item in m_items)
{
yield return item;
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
if (m_items?.Count > 0)
{
foreach (var item in m_items)
{
yield return item;
}
}
}
IEnumerator IReadOnlyArray.GetEnumerator()
{
if (m_items?.Count > 0)
{
foreach (var item in m_items)
{
yield return item;
}
}
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_items?.Count == 0)
{
m_items = null;
}
}
[DataMember(Name = "a", EmitDefaultValue = false)]
private List<PipelineContextData> m_items;
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class BooleanContextData : PipelineContextData, IBoolean
{
public BooleanContextData(Boolean value)
: base(PipelineContextDataType.Boolean)
{
m_value = value;
}
public Boolean Value
{
get
{
return m_value;
}
}
public override PipelineContextData Clone()
{
return new BooleanContextData(m_value);
}
public override JToken ToJToken()
{
return (JToken)m_value;
}
public override String ToString()
{
return m_value ? "true" : "false";
}
Boolean IBoolean.GetBoolean()
{
return Value;
}
public static implicit operator Boolean(BooleanContextData data)
{
return data.Value;
}
public static implicit operator BooleanContextData(Boolean data)
{
return new BooleanContextData(data);
}
[DataMember(Name = "b", EmitDefaultValue = false)]
private Boolean m_value;
}
}

View File

@@ -0,0 +1,293 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[DataContract]
[JsonObject]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public class CaseSensitiveDictionaryContextData : PipelineContextData, IEnumerable<KeyValuePair<String, PipelineContextData>>, IReadOnlyObject
{
public CaseSensitiveDictionaryContextData()
: base(PipelineContextDataType.CaseSensitiveDictionary)
{
}
[IgnoreDataMember]
public Int32 Count => m_list?.Count ?? 0;
[IgnoreDataMember]
public IEnumerable<String> Keys
{
get
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return pair.Key;
}
}
}
}
[IgnoreDataMember]
public IEnumerable<PipelineContextData> Values
{
get
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return pair.Value;
}
}
}
}
IEnumerable<Object> IReadOnlyObject.Values
{
get
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return pair.Value;
}
}
}
}
private Dictionary<String, Int32> IndexLookup
{
get
{
if (m_indexLookup == null)
{
m_indexLookup = new Dictionary<String, Int32>(StringComparer.Ordinal);
if (m_list?.Count > 0)
{
for (var i = 0; i < m_list.Count; i++)
{
var pair = m_list[i];
m_indexLookup.Add(pair.Key, i);
}
}
}
return m_indexLookup;
}
}
private List<DictionaryContextDataPair> List
{
get
{
if (m_list == null)
{
m_list = new List<DictionaryContextDataPair>();
}
return m_list;
}
}
public PipelineContextData this[String key]
{
get
{
var index = IndexLookup[key];
return m_list[index].Value;
}
set
{
// Existing
if (IndexLookup.TryGetValue(key, out var index))
{
key = m_list[index].Key; // preserve casing
m_list[index] = new DictionaryContextDataPair(key, value);
}
// New
else
{
Add(key, value);
}
}
}
Object IReadOnlyObject.this[String key]
{
get
{
var index = IndexLookup[key];
return m_list[index].Value;
}
}
internal KeyValuePair<String, PipelineContextData> this[Int32 index]
{
get
{
var pair = m_list[index];
return new KeyValuePair<String, PipelineContextData>(pair.Key, pair.Value);
}
}
public void Add(IEnumerable<KeyValuePair<String, PipelineContextData>> pairs)
{
foreach (var pair in pairs)
{
Add(pair.Key, pair.Value);
}
}
public void Add(
String key,
PipelineContextData value)
{
IndexLookup.Add(key, m_list?.Count ?? 0);
List.Add(new DictionaryContextDataPair(key, value));
}
public override PipelineContextData Clone()
{
var result = new CaseSensitiveDictionaryContextData();
if (m_list?.Count > 0)
{
result.m_list = new List<DictionaryContextDataPair>(m_list.Count);
foreach (var item in m_list)
{
result.m_list.Add(new DictionaryContextDataPair(item.Key, item.Value?.Clone()));
}
}
return result;
}
public override JToken ToJToken()
{
var json = new JObject();
if (m_list?.Count > 0)
{
foreach (var item in m_list)
{
json.Add(item.Key, item.Value?.ToJToken() ?? JValue.CreateNull());
}
}
return json;
}
public Boolean ContainsKey(String key)
{
return TryGetValue(key, out _);
}
public IEnumerator<KeyValuePair<String, PipelineContextData>> GetEnumerator()
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return new KeyValuePair<String, PipelineContextData>(pair.Key, pair.Value);
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return new KeyValuePair<String, PipelineContextData>(pair.Key, pair.Value);
}
}
}
IEnumerator IReadOnlyObject.GetEnumerator()
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return new KeyValuePair<String, Object>(pair.Key, pair.Value);
}
}
}
public Boolean TryGetValue(
String key,
out PipelineContextData value)
{
if (m_list?.Count > 0 &&
IndexLookup.TryGetValue(key, out var index))
{
value = m_list[index].Value;
return true;
}
value = null;
return false;
}
Boolean IReadOnlyObject.TryGetValue(
String key,
out Object value)
{
if (TryGetValue(key, out PipelineContextData data))
{
value = data;
return true;
}
value = null;
return false;
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_list?.Count == 0)
{
m_list = null;
}
}
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
private sealed class DictionaryContextDataPair
{
public DictionaryContextDataPair(
String key,
PipelineContextData value)
{
Key = key;
Value = value;
}
[DataMember(Name = "k")]
public readonly String Key;
[DataMember(Name = "v")]
public readonly PipelineContextData Value;
}
private Dictionary<String, Int32> m_indexLookup;
[DataMember(Name = "d", EmitDefaultValue = false)]
private List<DictionaryContextDataPair> m_list;
}
}

View File

@@ -0,0 +1,293 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[DataContract]
[JsonObject]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public class DictionaryContextData : PipelineContextData, IEnumerable<KeyValuePair<String, PipelineContextData>>, IReadOnlyObject
{
public DictionaryContextData()
: base(PipelineContextDataType.Dictionary)
{
}
[IgnoreDataMember]
public Int32 Count => m_list?.Count ?? 0;
[IgnoreDataMember]
public IEnumerable<String> Keys
{
get
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return pair.Key;
}
}
}
}
[IgnoreDataMember]
public IEnumerable<PipelineContextData> Values
{
get
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return pair.Value;
}
}
}
}
IEnumerable<Object> IReadOnlyObject.Values
{
get
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return pair.Value;
}
}
}
}
private Dictionary<String, Int32> IndexLookup
{
get
{
if (m_indexLookup == null)
{
m_indexLookup = new Dictionary<String, Int32>(StringComparer.OrdinalIgnoreCase);
if (m_list?.Count > 0)
{
for (var i = 0; i < m_list.Count; i++)
{
var pair = m_list[i];
m_indexLookup.Add(pair.Key, i);
}
}
}
return m_indexLookup;
}
}
private List<DictionaryContextDataPair> List
{
get
{
if (m_list == null)
{
m_list = new List<DictionaryContextDataPair>();
}
return m_list;
}
}
public PipelineContextData this[String key]
{
get
{
var index = IndexLookup[key];
return m_list[index].Value;
}
set
{
// Existing
if (IndexLookup.TryGetValue(key, out var index))
{
key = m_list[index].Key; // preserve casing
m_list[index] = new DictionaryContextDataPair(key, value);
}
// New
else
{
Add(key, value);
}
}
}
Object IReadOnlyObject.this[String key]
{
get
{
var index = IndexLookup[key];
return m_list[index].Value;
}
}
internal KeyValuePair<String, PipelineContextData> this[Int32 index]
{
get
{
var pair = m_list[index];
return new KeyValuePair<String, PipelineContextData>(pair.Key, pair.Value);
}
}
public void Add(IEnumerable<KeyValuePair<String, PipelineContextData>> pairs)
{
foreach (var pair in pairs)
{
Add(pair.Key, pair.Value);
}
}
public void Add(
String key,
PipelineContextData value)
{
IndexLookup.Add(key, m_list?.Count ?? 0);
List.Add(new DictionaryContextDataPair(key, value));
}
public override PipelineContextData Clone()
{
var result = new DictionaryContextData();
if (m_list?.Count > 0)
{
result.m_list = new List<DictionaryContextDataPair>(m_list.Count);
foreach (var item in m_list)
{
result.m_list.Add(new DictionaryContextDataPair(item.Key, item.Value?.Clone()));
}
}
return result;
}
public override JToken ToJToken()
{
var json = new JObject();
if (m_list?.Count > 0)
{
foreach (var item in m_list)
{
json.Add(item.Key, item.Value?.ToJToken() ?? JValue.CreateNull());
}
}
return json;
}
public Boolean ContainsKey(String key)
{
return TryGetValue(key, out _);
}
public IEnumerator<KeyValuePair<String, PipelineContextData>> GetEnumerator()
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return new KeyValuePair<String, PipelineContextData>(pair.Key, pair.Value);
}
}
}
IEnumerator IEnumerable.GetEnumerator()
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return new KeyValuePair<String, PipelineContextData>(pair.Key, pair.Value);
}
}
}
IEnumerator IReadOnlyObject.GetEnumerator()
{
if (m_list?.Count > 0)
{
foreach (var pair in m_list)
{
yield return new KeyValuePair<String, Object>(pair.Key, pair.Value);
}
}
}
public Boolean TryGetValue(
String key,
out PipelineContextData value)
{
if (m_list?.Count > 0 &&
IndexLookup.TryGetValue(key, out var index))
{
value = m_list[index].Value;
return true;
}
value = null;
return false;
}
Boolean IReadOnlyObject.TryGetValue(
String key,
out Object value)
{
if (TryGetValue(key, out PipelineContextData data))
{
value = data;
return true;
}
value = null;
return false;
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_list?.Count == 0)
{
m_list = null;
}
}
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
private sealed class DictionaryContextDataPair
{
public DictionaryContextDataPair(
String key,
PipelineContextData value)
{
Key = key;
Value = value;
}
[DataMember(Name = "k")]
public readonly String Key;
[DataMember(Name = "v")]
public readonly PipelineContextData Value;
}
private Dictionary<String, Int32> m_indexLookup;
[DataMember(Name = "d", EmitDefaultValue = false)]
private List<DictionaryContextDataPair> m_list;
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.ComponentModel;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static class JTokenExtensions
{
public static PipelineContextData ToPipelineContextData(this JToken value)
{
return value.ToPipelineContextData(1, 100);
}
public static PipelineContextData ToPipelineContextData(
this JToken value,
Int32 depth,
Int32 maxDepth)
{
if (depth < maxDepth)
{
if (value.Type == JTokenType.String)
{
return new StringContextData((String)value);
}
else if (value.Type == JTokenType.Boolean)
{
return new BooleanContextData((Boolean)value);
}
else if (value.Type == JTokenType.Float || value.Type == JTokenType.Integer)
{
return new NumberContextData((Double)value);
}
else if (value.Type == JTokenType.Object)
{
var subContext = new DictionaryContextData();
var obj = (JObject)value;
foreach (var property in obj.Properties())
{
subContext[property.Name] = ToPipelineContextData(property.Value, depth + 1, maxDepth);
}
return subContext;
}
else if (value.Type == JTokenType.Array)
{
var arrayContext = new ArrayContextData();
var arr = (JArray)value;
foreach (var element in arr)
{
arrayContext.Add(ToPipelineContextData(element, depth + 1, maxDepth));
}
return arrayContext;
}
else if (value.Type == JTokenType.Null)
{
return null;
}
}
// We don't understand the type or have reached our max, return as string
return new StringContextData(value.ToString());
}
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class NumberContextData : PipelineContextData, INumber
{
public NumberContextData(Double value)
: base(PipelineContextDataType.Number)
{
m_value = value;
}
public Double Value
{
get
{
return m_value;
}
}
public override PipelineContextData Clone()
{
return new NumberContextData(m_value);
}
public override JToken ToJToken()
{
if (Double.IsNaN(m_value) || m_value == Double.PositiveInfinity || m_value == Double.NegativeInfinity)
{
return (JToken)m_value;
}
var floored = Math.Floor(m_value);
if (m_value == floored && m_value <= (Double)Int32.MaxValue && m_value >= (Double)Int32.MinValue)
{
Int32 flooredInt = (Int32)floored;
return (JToken)flooredInt;
}
else
{
return (JToken)m_value;
}
}
public override String ToString()
{
return m_value.ToString("G15", CultureInfo.InvariantCulture);
}
Double INumber.GetNumber()
{
return Value;
}
public static implicit operator Double(NumberContextData data)
{
return data.Value;
}
public static implicit operator NumberContextData(Double data)
{
return new NumberContextData(data);
}
[DataMember(Name = "n", EmitDefaultValue = false)]
private Double m_value;
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
/// <summary>
/// Base class for all template tokens
/// </summary>
[DataContract]
[JsonConverter(typeof(PipelineContextDataJsonConverter))]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class PipelineContextData
{
protected PipelineContextData(Int32 type)
{
Type = type;
}
[DataMember(Name = "t", EmitDefaultValue = false)]
internal Int32 Type { get; }
public abstract PipelineContextData Clone();
public abstract JToken ToJToken();
}
}

View File

@@ -0,0 +1,290 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static class PipelineContextDataExtensions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static ArrayContextData AssertArray(
this PipelineContextData value,
String objectDescription)
{
if (value is ArrayContextData array)
{
return array;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(ArrayContextData)}' was expected.");
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static DictionaryContextData AssertDictionary(
this PipelineContextData value,
String objectDescription)
{
if (value is DictionaryContextData dictionary)
{
return dictionary;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(DictionaryContextData)}' was expected.");
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static StringContextData AssertString(
this PipelineContextData value,
String objectDescription)
{
if (value is StringContextData str)
{
return str;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(StringContextData)}' was expected.");
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static BooleanContextData AssertBoolean(
this PipelineContextData value,
String objectDescription)
{
if (value is BooleanContextData boolValue)
{
return boolValue;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(BooleanContextData)}' was expected.");
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static NumberContextData AssertNumber(
this PipelineContextData value,
String objectDescription)
{
if (value is NumberContextData num)
{
return num;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(NumberContextData)}' was expected.");
}
/// <summary>
/// Returns all context data objects (depth first)
/// </summary>
internal static IEnumerable<PipelineContextData> Traverse(this PipelineContextData value)
{
return Traverse(value, omitKeys: false);
}
/// <summary>
/// Returns all context data objects (depth first)
/// </summary>
internal static IEnumerable<PipelineContextData> Traverse(
this PipelineContextData value,
Boolean omitKeys)
{
yield return value;
if (value is ArrayContextData || value is DictionaryContextData)
{
var state = new TraversalState(null, value);
while (state != null)
{
if (state.MoveNext(omitKeys))
{
value = state.Current;
yield return value;
if (value is ArrayContextData || value is DictionaryContextData)
{
state = new TraversalState(state, value);
}
}
else
{
state = state.Parent;
}
}
}
}
internal static JToken ToJToken(this PipelineContextData value)
{
JToken result;
if (value is StringContextData str)
{
result = str.Value ?? String.Empty;
}
else if (value is BooleanContextData booleanValue)
{
result = booleanValue.Value;
}
else if (value is NumberContextData num)
{
result = num.Value;
}
else if (value is ArrayContextData array)
{
var jarray = new JArray();
foreach (var item in array)
{
jarray.Add(item.ToJToken()); // Recurse
}
result = jarray;
}
else if (value is DictionaryContextData dictionary)
{
var jobject = new JObject();
foreach (var pair in dictionary)
{
var key = pair.Key ?? String.Empty;
var value2 = pair.Value.ToJToken(); // Recurse
if (value2 != null)
{
jobject[key] = value2;
}
}
result = jobject;
}
else
{
throw new InvalidOperationException("Internal error reading the template. Expected a string, an array, or a dictionary");
}
return result;
}
internal static TemplateToken ToTemplateToken(this PipelineContextData data)
{
if (data is null)
{
return new NullToken(null, null, null);
}
switch (data.Type)
{
case PipelineContextDataType.Dictionary:
var dictionary = data.AssertDictionary("dictionary");
var mapping = new MappingToken(null, null, null);
if (dictionary.Count > 0)
{
foreach (var pair in dictionary)
{
var key = new StringToken(null, null, null, pair.Key);
var value = pair.Value.ToTemplateToken();
mapping.Add(key, value);
}
}
return mapping;
case PipelineContextDataType.Array:
var array = data.AssertArray("array");
var sequence = new SequenceToken(null, null, null);
if (array.Count > 0)
{
foreach (var item in array)
{
sequence.Add(item.ToTemplateToken());
}
}
return sequence;
case PipelineContextDataType.String:
var stringData = data as StringContextData;
return new StringToken(null, null, null, stringData.Value);
case PipelineContextDataType.Boolean:
var booleanData = data as BooleanContextData;
return new BooleanToken(null, null, null, booleanData.Value);
case PipelineContextDataType.Number:
var numberData = data as NumberContextData;
return new NumberToken(null, null, null, numberData.Value);
default:
throw new NotSupportedException($"Unexpected {nameof(PipelineContextDataType)} type '{data.Type}'");
}
}
private sealed class TraversalState
{
public TraversalState(
TraversalState parent,
PipelineContextData data)
{
Parent = parent;
m_data = data;
}
public Boolean MoveNext(Boolean omitKeys)
{
switch (m_data.Type)
{
case PipelineContextDataType.Array:
var array = m_data.AssertArray("array");
if (++m_index < array.Count)
{
Current = array[m_index];
return true;
}
else
{
Current = null;
return false;
}
case PipelineContextDataType.Dictionary:
var dictionary = m_data.AssertDictionary("dictionary");
// Return the value
if (m_isKey)
{
m_isKey = false;
Current = dictionary[m_index].Value;
return true;
}
if (++m_index < dictionary.Count)
{
// Skip the key, return the value
if (omitKeys)
{
m_isKey = false;
Current = dictionary[m_index].Value;
return true;
}
// Return the key
m_isKey = true;
Current = new StringContextData(dictionary[m_index].Key);
return true;
}
Current = null;
return false;
default:
throw new NotSupportedException($"Unexpected {nameof(PipelineContextData)} type '{m_data.Type}'");
}
}
private PipelineContextData m_data;
private Int32 m_index = -1;
private Boolean m_isKey;
public PipelineContextData Current;
public TraversalState Parent;
}
}
}

View File

@@ -0,0 +1,200 @@
using System;
using System.Reflection;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
/// <summary>
/// JSON serializer for ContextData objects
/// </summary>
internal sealed class PipelineContextDataJsonConverter : VssSecureJsonConverter
{
public override Boolean CanWrite
{
get
{
return true;
}
}
public override Boolean CanConvert(Type objectType)
{
return typeof(PipelineContextData).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object existingValue,
JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
return new StringContextData(reader.Value.ToString());
case JsonToken.Boolean:
return new BooleanContextData((Boolean)reader.Value);
case JsonToken.Float:
return new NumberContextData((Double)reader.Value);
case JsonToken.Integer:
return new NumberContextData((Double)(Int64)reader.Value);
case JsonToken.StartObject:
break;
default:
return null;
}
Int32? type = null;
JObject value = JObject.Load(reader);
if (!value.TryGetValue("t", StringComparison.OrdinalIgnoreCase, out JToken typeValue))
{
type = PipelineContextDataType.String;
}
else if (typeValue.Type == JTokenType.Integer)
{
type = (Int32)typeValue;
}
else
{
return existingValue;
}
Object newValue = null;
switch (type)
{
case PipelineContextDataType.String:
newValue = new StringContextData(null);
break;
case PipelineContextDataType.Array:
newValue = new ArrayContextData();
break;
case PipelineContextDataType.Dictionary:
newValue = new DictionaryContextData();
break;
case PipelineContextDataType.Boolean:
newValue = new BooleanContextData(false);
break;
case PipelineContextDataType.Number:
newValue = new NumberContextData(0);
break;
case PipelineContextDataType.CaseSensitiveDictionary:
newValue = new CaseSensitiveDictionaryContextData();
break;
default:
throw new NotSupportedException($"Unexpected {nameof(PipelineContextDataType)} '{type}'");
}
if (value != null)
{
using (JsonReader objectReader = value.CreateReader())
{
serializer.Populate(objectReader, newValue);
}
}
return newValue;
}
public override void WriteJson(
JsonWriter writer,
Object value,
JsonSerializer serializer)
{
base.WriteJson(writer, value, serializer);
if (Object.ReferenceEquals(value, null))
{
writer.WriteNull();
}
else if (value is StringContextData stringData)
{
writer.WriteValue(stringData.Value);
}
else if (value is BooleanContextData boolData)
{
writer.WriteValue(boolData.Value);
}
else if (value is NumberContextData numberData)
{
writer.WriteValue(numberData.Value);
}
else if (value is ArrayContextData arrayData)
{
writer.WriteStartObject();
writer.WritePropertyName("t");
writer.WriteValue(PipelineContextDataType.Array);
if (arrayData.Count > 0)
{
writer.WritePropertyName("a");
writer.WriteStartArray();
foreach (var item in arrayData)
{
serializer.Serialize(writer, item);
}
writer.WriteEndArray();
}
writer.WriteEndObject();
}
else if (value is DictionaryContextData dictionaryData)
{
writer.WriteStartObject();
writer.WritePropertyName("t");
writer.WriteValue(PipelineContextDataType.Dictionary);
if (dictionaryData.Count > 0)
{
writer.WritePropertyName("d");
writer.WriteStartArray();
foreach (var pair in dictionaryData)
{
writer.WriteStartObject();
writer.WritePropertyName("k");
writer.WriteValue(pair.Key);
writer.WritePropertyName("v");
serializer.Serialize(writer, pair.Value);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
writer.WriteEndObject();
}
else if (value is CaseSensitiveDictionaryContextData caseSensitiveDictionaryData)
{
writer.WriteStartObject();
writer.WritePropertyName("t");
writer.WriteValue(PipelineContextDataType.CaseSensitiveDictionary);
if (caseSensitiveDictionaryData.Count > 0)
{
writer.WritePropertyName("d");
writer.WriteStartArray();
foreach (var pair in caseSensitiveDictionaryData)
{
writer.WriteStartObject();
writer.WritePropertyName("k");
writer.WriteValue(pair.Key);
writer.WritePropertyName("v");
serializer.Serialize(writer, pair.Value);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
writer.WriteEndObject();
}
else
{
throw new NotSupportedException($"Unexpected type '{value.GetType().Name}'");
}
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
internal static class PipelineContextDataType
{
internal const Int32 String = 0;
internal const Int32 Array = 1;
internal const Int32 Dictionary = 2;
internal const Int32 Boolean = 3;
internal const Int32 Number = 4;
internal const Int32 CaseSensitiveDictionary = 5;
}
}

View File

@@ -0,0 +1,76 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class StringContextData : PipelineContextData, IString
{
public StringContextData(String value)
: base(PipelineContextDataType.String)
{
m_value = value;
}
public String Value
{
get
{
if (m_value == null)
{
m_value = String.Empty;
}
return m_value;
}
}
public override PipelineContextData Clone()
{
return new StringContextData(m_value);
}
public override JToken ToJToken()
{
return (JToken)m_value;
}
String IString.GetString()
{
return Value;
}
public override String ToString()
{
return Value;
}
public static implicit operator String(StringContextData data)
{
return data.Value;
}
public static implicit operator StringContextData(String data)
{
return new StringContextData(data);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_value?.Length == 0)
{
m_value = null;
}
}
[DataMember(Name = "s", EmitDefaultValue = false)]
private String m_value;
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using GitHub.DistributedTask.ObjectTemplating;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
internal static class TemplateMemoryExtensions
{
internal static void AddBytes(
this TemplateMemory memory,
PipelineContextData value,
Boolean traverse)
{
var bytes = CalculateBytes(memory, value, traverse);
memory.AddBytes(bytes);
}
internal static Int32 CalculateBytes(
this TemplateMemory memory,
PipelineContextData value,
Boolean traverse)
{
var enumerable = traverse ? value.Traverse() : new[] { value } as IEnumerable<PipelineContextData>;
var result = 0;
foreach (var item in enumerable)
{
// This measurement doesn't have to be perfect
// https://codeblog.jonskeet.uk/2011/04/05/of-memory-and-strings/
switch (item?.Type)
{
case PipelineContextDataType.String:
var str = item.AssertString("string").Value;
checked
{
result += TemplateMemory.MinObjectSize + TemplateMemory.StringBaseOverhead + ((str?.Length ?? 0) * sizeof(Char));
}
break;
case PipelineContextDataType.Array:
case PipelineContextDataType.Dictionary:
case PipelineContextDataType.Boolean:
case PipelineContextDataType.Number:
// Min object size is good enough. Allows for base + a few fields.
checked
{
result += TemplateMemory.MinObjectSize;
}
break;
case null:
checked
{
result += IntPtr.Size;
}
break;
default:
throw new NotSupportedException($"Unexpected pipeline context data type '{item.Type}'");
}
}
return result;
}
}
}

View File

@@ -0,0 +1,78 @@
using System;
using System.ComponentModel;
using GitHub.DistributedTask.ObjectTemplating.Tokens;
namespace GitHub.DistributedTask.Pipelines.ContextData
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static class TemplateTokenExtensions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static StringContextData ToContextData(this LiteralToken literal)
{
var token = literal as TemplateToken;
var contextData = token.ToContextData();
return contextData.AssertString("converted literal token");
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static ArrayContextData ToContextData(this SequenceToken sequence)
{
var token = sequence as TemplateToken;
var contextData = token.ToContextData();
return contextData.AssertArray("converted sequence token");
}
[EditorBrowsable(EditorBrowsableState.Never)]
public static PipelineContextData ToContextData(this TemplateToken token)
{
switch (token.Type)
{
case TokenType.Mapping:
var mapping = token as MappingToken;
var dictionary = new DictionaryContextData();
if (mapping.Count > 0)
{
foreach (var pair in mapping)
{
var keyLiteral = pair.Key.AssertString("dictionary context data key");
var key = keyLiteral.Value;
var value = pair.Value.ToContextData();
dictionary.Add(key, value);
}
}
return dictionary;
case TokenType.Sequence:
var sequence = token as SequenceToken;
var array = new ArrayContextData();
if (sequence.Count > 0)
{
foreach (var item in sequence)
{
array.Add(item.ToContextData());
}
}
return array;
case TokenType.Null:
return null;
case TokenType.Boolean:
var boolean = token as BooleanToken;
return new BooleanContextData(boolean.Value);
case TokenType.Number:
var number = token as NumberToken;
return new NumberContextData(number.Value);
case TokenType.String:
var stringToken = token as StringToken;
return new StringContextData(stringToken.Value);
default:
throw new NotSupportedException($"Unexpected {nameof(TemplateToken)} type '{token.Type}'");
}
}
}
}