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,146 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.DistributedTask.Expressions2.Sdk.Functions;
using GitHub.DistributedTask.Pipelines.ObjectTemplating;
using GitHub.Services.WebApi.Internal;
using Container = GitHub.DistributedTask.Expressions2.Sdk.Container;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class BasicExpressionToken : ExpressionToken
{
internal BasicExpressionToken(
Int32? fileId,
Int32? line,
Int32? column,
String expression)
: base(TokenType.BasicExpression, fileId, line, column, null)
{
m_expression = expression;
}
internal String Expression
{
get
{
if (m_expression == null)
{
m_expression = String.Empty;
}
return m_expression;
}
}
public override TemplateToken Clone(Boolean omitSource)
{
return omitSource ? new BasicExpressionToken(null, null, null, m_expression) : new BasicExpressionToken(FileId, Line, Column, m_expression);
}
public override String ToString()
{
return $"{TemplateConstants.OpenExpression} {m_expression} {TemplateConstants.CloseExpression}";
}
public override String ToDisplayString()
{
var expressionParser = new ExpressionParser();
var expressionNode = expressionParser.ValidateSyntax(Expression, null);
if (expressionNode is Format formatNode)
{
// Make sure our first item is indeed a literal string so we can format it.
if (formatNode.Parameters.Count > 1 &&
formatNode.Parameters.First() is Literal literalValueNode &&
literalValueNode.Kind == ValueKind.String)
{
// Get all other Parameters san the formatted string to pass into the formatter
var formatParameters = formatNode.Parameters.Skip(1).Select(x => this.ConvertFormatParameterToExpression(x)).ToArray();
if (formatParameters.Length > 0)
{
String formattedString = String.Empty;
try
{
formattedString = String.Format(CultureInfo.InvariantCulture, (formatNode.Parameters[0] as Literal).Value as String, formatParameters);
}
catch (FormatException) { }
catch (ArgumentNullException) { } // If this operation fails, revert to default display name
if (!String.IsNullOrEmpty(formattedString))
{
return TrimDisplayString(formattedString);
}
}
}
}
return base.ToDisplayString();
}
internal StringToken EvaluateStringToken(
TemplateContext context,
out Int32 bytes)
{
return EvaluateStringToken(context, Expression, out bytes);
}
internal MappingToken EvaluateMappingToken(
TemplateContext context,
out Int32 bytes)
{
return EvaluateMappingToken(context, Expression, out bytes);
}
internal SequenceToken EvaluateSequenceToken(
TemplateContext context,
out Int32 bytes)
{
return EvaluateSequenceToken(context, Expression, out bytes);
}
internal TemplateToken EvaluateTemplateToken(
TemplateContext context,
out Int32 bytes)
{
return EvaluateTemplateToken(context, Expression, out bytes);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_expression?.Length == 0)
{
m_expression = null;
}
}
private String ConvertFormatParameterToExpression(ExpressionNode node)
{
var nodeString = node.ConvertToExpression();
// If the node is a container, see if it starts with '(' and ends with ')' so we can simplify the string
// Should only simplify if only one '(' or ')' exists in the string
// We are trying to simplify the case (a || b) to a || b
// But we should avoid simplifying ( a && b
if (node is Container &&
nodeString.Length > 2 &&
nodeString[0] == ExpressionConstants.StartParameter &&
nodeString[nodeString.Length - 1] == ExpressionConstants.EndParameter &&
nodeString.Count(character => character == ExpressionConstants.StartParameter) == 1 &&
nodeString.Count(character => character == ExpressionConstants.EndParameter) == 1)
{
nodeString = nodeString = nodeString.Substring(1, nodeString.Length - 2);
}
return String.Concat(TemplateConstants.OpenExpression, " ", nodeString, " ", TemplateConstants.CloseExpression);
}
[DataMember(Name = "expr", EmitDefaultValue = false)]
private String m_expression;
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class BooleanToken : LiteralToken, IBoolean
{
public BooleanToken(
Int32? fileId,
Int32? line,
Int32? column,
Boolean value)
: base(TokenType.Boolean, fileId, line, column)
{
m_value = value;
}
public Boolean Value => m_value;
public override TemplateToken Clone(Boolean omitSource)
{
return omitSource ? new BooleanToken(null, null, null, m_value) : new BooleanToken(FileId, Line, Column, m_value);
}
public override String ToString()
{
return m_value ? "true" : "false";
}
Boolean IBoolean.GetBoolean()
{
return Value;
}
[DataMember(Name = "bool", EmitDefaultValue = false)]
private Boolean m_value;
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
/// <summary>
/// Base class for all template expression tokens
/// </summary>
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class ExpressionToken : ScalarToken
{
internal ExpressionToken(
Int32 templateType,
Int32? fileId,
Int32? line,
Int32? column,
String directive)
: base(templateType, fileId, line, column)
{
Directive = directive;
}
[DataMember(Name = "directive", EmitDefaultValue = false)]
internal String Directive { get; }
internal static Boolean IsValidExpression(
String expression,
String[] allowedContext,
out Exception ex)
{
// Create dummy allowed contexts
INamedValueInfo[] namedValues = null;
if (allowedContext?.Length > 0)
{
namedValues = allowedContext.Select(x => new NamedValueInfo<ContextValueNode>(x)).ToArray();
}
// Parse
Boolean result;
ExpressionNode root = null;
try
{
root = new ExpressionParser().CreateTree(expression, null, namedValues, null) as ExpressionNode;
result = true;
ex = null;
}
catch (Exception exception)
{
result = false;
ex = exception;
}
return result;
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class InsertExpressionToken : ExpressionToken
{
internal InsertExpressionToken(
Int32? fileId,
Int32? line,
Int32? column)
: base(TokenType.InsertExpression, fileId, line, column, TemplateConstants.InsertDirective)
{
}
public override TemplateToken Clone(Boolean omitSource)
{
return omitSource ? new InsertExpressionToken(null, null, null) : new InsertExpressionToken(FileId, Line, Column);
}
public override String ToString()
{
return $"{TemplateConstants.OpenExpression} insert {TemplateConstants.CloseExpression}";
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class LiteralToken : ScalarToken
{
public LiteralToken(
Int32 tokenType,
Int32? fileId,
Int32? line,
Int32? column)
: base(tokenType, fileId, line, column)
{
}
}
}

View File

@@ -0,0 +1,245 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[JsonObject]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class MappingToken : TemplateToken, IEnumerable<KeyValuePair<ScalarToken, TemplateToken>>, IReadOnlyObject
{
public MappingToken(
Int32? fileId,
Int32? line,
Int32? column)
: base(TokenType.Mapping, fileId, line, column)
{
}
internal Int32 Count => m_items?.Count ?? 0;
// IReadOnlyObject (for expressions)
Int32 IReadOnlyObject.Count
{
get
{
InitializeDictionary();
return m_dictionary.Count;
}
}
// IReadOnlyObject (for expressions)
IEnumerable<String> IReadOnlyObject.Keys
{
get
{
InitializeDictionary();
foreach (var key in m_dictionary.Keys)
{
yield return key as String;
}
}
}
// IReadOnlyObject (for expressions)
IEnumerable<Object> IReadOnlyObject.Values
{
get
{
InitializeDictionary();
foreach (var value in m_dictionary.Values)
{
yield return value;
}
}
}
public KeyValuePair<ScalarToken, TemplateToken> this[Int32 index]
{
get
{
return m_items[index];
}
set
{
m_items[index] = value;
m_dictionary = null;
}
}
// IReadOnlyObject (for expressions)
Object IReadOnlyObject.this[String key]
{
get
{
InitializeDictionary();
return m_dictionary[key];
}
}
public void Add(IEnumerable<KeyValuePair<ScalarToken, TemplateToken>> items)
{
foreach (var item in items)
{
Add(item);
}
}
public void Add(KeyValuePair<ScalarToken, TemplateToken> item)
{
if (m_items == null)
{
m_items = new List<KeyValuePair<ScalarToken, TemplateToken>>();
}
m_items.Add(item);
m_dictionary = null;
}
public void Add(
ScalarToken key,
TemplateToken value)
{
Add(new KeyValuePair<ScalarToken, TemplateToken>(key, value));
}
public override TemplateToken Clone(Boolean omitSource)
{
var result = omitSource ? new MappingToken(null, null, null) : new MappingToken(FileId, Line, Column);
if (m_items?.Count > 0)
{
foreach (var pair in m_items)
{
result.Add(pair.Key?.Clone() as ScalarToken, pair.Value?.Clone());
}
}
return result;
}
public IEnumerator<KeyValuePair<ScalarToken, TemplateToken>> GetEnumerator()
{
if (m_items?.Count > 0)
{
return m_items.GetEnumerator();
}
else
{
return (new List<KeyValuePair<ScalarToken, TemplateToken>>(0)).GetEnumerator();
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
if (m_items?.Count > 0)
{
return m_items.GetEnumerator();
}
else
{
return (new KeyValuePair<ScalarToken, TemplateToken>[0]).GetEnumerator();
}
}
public void Insert(
Int32 index,
KeyValuePair<ScalarToken, TemplateToken> item)
{
if (m_items == null)
{
m_items = new List<KeyValuePair<ScalarToken, TemplateToken>>();
}
m_items.Insert(index, item);
m_dictionary = null;
}
public void Insert(
Int32 index,
ScalarToken key,
TemplateToken value)
{
Insert(index, new KeyValuePair<ScalarToken, TemplateToken>(key, value));
}
public void RemoveAt(Int32 index)
{
m_items.RemoveAt(index);
m_dictionary = null;
}
// IReadOnlyObject (for expressions)
Boolean IReadOnlyObject.ContainsKey(String key)
{
InitializeDictionary();
return m_dictionary.Contains(key);
}
// IReadOnlyObject (for expressions)
IEnumerator IReadOnlyObject.GetEnumerator()
{
InitializeDictionary();
return m_dictionary.GetEnumerator();
}
// IReadOnlyObject (for expressions)
Boolean IReadOnlyObject.TryGetValue(
String key,
out Object value)
{
InitializeDictionary();
if (!m_dictionary.Contains(key))
{
value = null;
return false;
}
value = m_dictionary[key];
return true;
}
/// <summary>
/// Initializes the dictionary used for the expressions IReadOnlyObject interface
/// </summary>
private void InitializeDictionary()
{
if (m_dictionary == null)
{
m_dictionary = new OrderedDictionary(StringComparer.OrdinalIgnoreCase);
if (m_items?.Count > 0)
{
foreach (var pair in m_items)
{
if (pair.Key is StringToken stringToken &&
!m_dictionary.Contains(stringToken.Value))
{
m_dictionary.Add(stringToken.Value, pair.Value);
}
}
}
}
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_items?.Count == 0)
{
m_items = null;
}
}
[DataMember(Name = "map", EmitDefaultValue = false)]
private List<KeyValuePair<ScalarToken, TemplateToken>> m_items;
private IDictionary m_dictionary;
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class NullToken : LiteralToken, INull
{
public NullToken(
Int32? fileId,
Int32? line,
Int32? column)
: base(TokenType.Null, fileId, line, column)
{
}
public override TemplateToken Clone(Boolean omitSource)
{
return omitSource ? new NullToken(null, null, null) : new NullToken(FileId, Line, Column);
}
public override String ToString()
{
return String.Empty;
}
}
}

View File

@@ -0,0 +1,45 @@
using System;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class NumberToken : LiteralToken, INumber
{
public NumberToken(
Int32? fileId,
Int32? line,
Int32? column,
Double value)
: base(TokenType.Number, fileId, line, column)
{
m_value = value;
}
public Double Value => m_value;
public override TemplateToken Clone(Boolean omitSource)
{
return omitSource ? new NumberToken(null, null, null, m_value) : new NumberToken(FileId, Line, Column, m_value);
}
public override String ToString()
{
return m_value.ToString("G15", CultureInfo.InvariantCulture);
}
Double INumber.GetNumber()
{
return Value;
}
[DataMember(Name = "num", EmitDefaultValue = false)]
private Double m_value;
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.ComponentModel;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class ScalarToken : TemplateToken
{
protected ScalarToken(
Int32 type,
Int32? fileId,
Int32? line,
Int32? column)
: base(type, fileId, line, column)
{
}
public virtual String ToDisplayString()
{
return TrimDisplayString(ToString());
}
protected String TrimDisplayString(String displayString)
{
var firstLine = displayString.TrimStart(' ', '\t', '\r', '\n');
var firstNewLine = firstLine.IndexOfAny(new[] { '\r', '\n' });
if (firstNewLine >= 0)
{
firstLine = firstLine.Substring(0, firstNewLine);
}
return firstLine;
}
}
}

View File

@@ -0,0 +1,151 @@
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;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[JsonObject]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class SequenceToken : TemplateToken, IEnumerable<TemplateToken>, IReadOnlyArray
{
public SequenceToken(
Int32? fileId,
Int32? line,
Int32? column)
: base(TokenType.Sequence, fileId, line, column)
{
}
public Int32 Count => m_items?.Count ?? 0;
public TemplateToken this[Int32 index]
{
get
{
return m_items[index];
}
set
{
m_items[index] = value;
}
}
// IReadOnlyArray (for expressions)
Object IReadOnlyArray.this[Int32 index]
{
get
{
return m_items[index];
}
}
public void Add(TemplateToken value)
{
if (m_items == null)
{
m_items = new List<TemplateToken>();
}
m_items.Add(value);
}
public override TemplateToken Clone(Boolean omitSource)
{
var result = omitSource ? new SequenceToken(null, null, null) : new SequenceToken(FileId, Line, Column);
if (m_items?.Count > 0)
{
foreach (var item in m_items)
{
result.Add(item?.Clone());
}
}
return result;
}
public IEnumerator<TemplateToken> GetEnumerator()
{
if (m_items?.Count > 0)
{
return m_items.GetEnumerator();
}
else
{
return (new TemplateToken[0] as IEnumerable<TemplateToken>).GetEnumerator();
}
}
IEnumerator IEnumerable.GetEnumerator()
{
if (m_items?.Count > 0)
{
return m_items.GetEnumerator();
}
else
{
return (new TemplateToken[0] as IEnumerable<TemplateToken>).GetEnumerator();
}
}
// IReadOnlyArray (for expressions)
IEnumerator IReadOnlyArray.GetEnumerator()
{
if (m_items?.Count > 0)
{
return m_items.GetEnumerator();
}
else
{
return (new TemplateToken[0] as IEnumerable<TemplateToken>).GetEnumerator();
}
}
public void Insert(
Int32 index,
TemplateToken item)
{
if (m_items == null)
{
m_items = new List<TemplateToken>();
}
m_items.Insert(index, item);
}
public void InsertRange(
Int32 index,
IEnumerable<TemplateToken> items)
{
if (m_items == null)
{
m_items = new List<TemplateToken>();
}
m_items.InsertRange(index, items);
}
public void RemoveAt(Int32 index)
{
m_items.RemoveAt(index);
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_items?.Count == 0)
{
m_items = null;
}
}
[DataMember(Name = "seq", EmitDefaultValue = false)]
private List<TemplateToken> m_items;
}
}

View File

@@ -0,0 +1,64 @@
using System;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
[DataContract]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public sealed class StringToken : LiteralToken, IString
{
public StringToken(
Int32? fileId,
Int32? line,
Int32? column,
String value)
: base(TokenType.String, fileId, line, column)
{
m_value = value;
}
public String Value
{
get
{
if (m_value == null)
{
m_value = String.Empty;
}
return m_value;
}
}
public override TemplateToken Clone(Boolean omitSource)
{
return omitSource ? new StringToken(null, null, null, m_value) : new StringToken(FileId, Line, Column, m_value);
}
public override String ToString()
{
return m_value ?? String.Empty;
}
String IString.GetString()
{
return Value;
}
[OnSerializing]
private void OnSerializing(StreamingContext context)
{
if (m_value?.Length == 0)
{
m_value = null;
}
}
[DataMember(Name = "lit", EmitDefaultValue = false)]
private String m_value;
}
}

View File

@@ -0,0 +1,290 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.Serialization;
using GitHub.DistributedTask.Expressions2;
using GitHub.DistributedTask.Expressions2.Sdk;
using GitHub.Services.WebApi.Internal;
using Newtonsoft.Json;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
/// <summary>
/// Base class for all template tokens
/// </summary>
[DataContract]
[JsonConverter(typeof(TemplateTokenJsonConverter))]
[ClientIgnore]
[EditorBrowsable(EditorBrowsableState.Never)]
public abstract class TemplateToken
{
protected TemplateToken(
Int32 type,
Int32? fileId,
Int32? line,
Int32? column)
{
Type = type;
FileId = fileId;
Line = line;
Column = column;
}
[IgnoreDataMember]
internal Int32? FileId { get; set; }
[DataMember(Name = "line", EmitDefaultValue = false)]
internal Int32? Line { get; }
[DataMember(Name = "col", EmitDefaultValue = false)]
internal Int32? Column { get; }
[DataMember(Name = "type", EmitDefaultValue = false)]
internal Int32 Type { get; }
public TemplateToken Clone()
{
return Clone(false);
}
public abstract TemplateToken Clone(Boolean omitSource);
protected StringToken EvaluateStringToken(
TemplateContext context,
String expression,
out Int32 bytes)
{
var originalBytes = context.Memory.CurrentBytes;
try
{
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
var options = new EvaluationOptions
{
MaxMemory = context.Memory.MaxBytes,
};
var result = tree.Evaluate(context.TraceWriter.ToExpressionTraceWriter(), null, context, options);
if (result.Raw is LiteralToken literalToken)
{
var stringToken = new StringToken(FileId, Line, Column, literalToken.ToString());
context.Memory.AddBytes(stringToken);
return stringToken;
}
if (!result.IsPrimitive)
{
context.Error(this, "Expected a string");
return CreateStringToken(context, expression);
}
var stringValue = result.Kind == ValueKind.Null ? String.Empty : result.ConvertToString();
return CreateStringToken(context, stringValue);
}
finally
{
bytes = context.Memory.CurrentBytes - originalBytes;
}
}
protected SequenceToken EvaluateSequenceToken(
TemplateContext context,
String expression,
out Int32 bytes)
{
var originalBytes = context.Memory.CurrentBytes;
try
{
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
var options = new EvaluationOptions
{
MaxMemory = context.Memory.MaxBytes,
};
var result = tree.Evaluate(context.TraceWriter.ToExpressionTraceWriter(), null, context, options);
var templateToken = ConvertToTemplateToken(context, result);
if (templateToken is SequenceToken sequence)
{
return sequence;
}
context.Error(this, TemplateStrings.ExpectedSequence());
return CreateSequenceToken(context);
}
finally
{
bytes = context.Memory.CurrentBytes - originalBytes;
}
}
protected MappingToken EvaluateMappingToken(
TemplateContext context,
String expression,
out Int32 bytes)
{
var originalBytes = context.Memory.CurrentBytes;
try
{
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
var options = new EvaluationOptions
{
MaxMemory = context.Memory.MaxBytes,
};
var result = tree.Evaluate(context.TraceWriter.ToExpressionTraceWriter(), null, context, options);
var templateToken = ConvertToTemplateToken(context, result);
if (templateToken is MappingToken mapping)
{
return mapping;
}
context.Error(this, TemplateStrings.ExpectedMapping());
return CreateMappingToken(context);
}
finally
{
bytes = context.Memory.CurrentBytes - originalBytes;
}
}
protected TemplateToken EvaluateTemplateToken(
TemplateContext context,
String expression,
out Int32 bytes)
{
var originalBytes = context.Memory.CurrentBytes;
try
{
var tree = new ExpressionParser().CreateTree(expression, null, context.GetExpressionNamedValues(), context.ExpressionFunctions);
var options = new EvaluationOptions
{
MaxMemory = context.Memory.MaxBytes,
};
var result = tree.Evaluate(context.TraceWriter.ToExpressionTraceWriter(), null, context, options);
return ConvertToTemplateToken(context, result);
}
finally
{
bytes = context.Memory.CurrentBytes - originalBytes;
}
}
private TemplateToken ConvertToTemplateToken(
TemplateContext context,
EvaluationResult result)
{
// Literal
if (TryConvertToLiteralToken(context, result, out LiteralToken literal))
{
return literal;
}
// Known raw types
else if (!Object.ReferenceEquals(result.Raw, null))
{
if (result.Raw is SequenceToken sequence)
{
context.Memory.AddBytes(sequence, true);
return sequence;
}
else if (result.Raw is MappingToken mapping)
{
context.Memory.AddBytes(mapping, true);
return mapping;
}
}
// Leverage the expression SDK to traverse the object
if (result.TryGetCollectionInterface(out Object collection))
{
if (collection is IReadOnlyObject dictionary)
{
var mapping = CreateMappingToken(context);
foreach (KeyValuePair<String, Object> pair in dictionary)
{
var keyToken = CreateStringToken(context, pair.Key);
var valueResult = EvaluationResult.CreateIntermediateResult(null, pair.Value);
var valueToken = ConvertToTemplateToken(context, valueResult);
mapping.Add(keyToken, valueToken);
}
return mapping;
}
else if (collection is IReadOnlyArray list)
{
var sequence = CreateSequenceToken(context);
foreach (var item in list)
{
var itemResult = EvaluationResult.CreateIntermediateResult(null, item);
var itemToken = ConvertToTemplateToken(context, itemResult);
sequence.Add(itemToken);
}
return sequence;
}
}
throw new ArgumentException(TemplateStrings.UnableToConvertToTemplateToken(result.Value?.GetType().FullName));
}
private Boolean TryConvertToLiteralToken(
TemplateContext context,
EvaluationResult result,
out LiteralToken literal)
{
if (result.Raw is LiteralToken literal2)
{
context.Memory.AddBytes(literal2);
literal = literal2;
return true;
}
switch (result.Kind)
{
case ValueKind.Null:
literal = new NullToken(FileId, Line, Column);
break;
case ValueKind.Boolean:
literal = new BooleanToken(FileId, Line, Column, (Boolean)result.Value);
break;
case ValueKind.Number:
literal = new NumberToken(FileId, Line, Column, (Double)result.Value);
break;
case ValueKind.String:
literal = new StringToken(FileId, Line, Column, (String)result.Value);
break;
default:
literal = null;
return false;
}
context.Memory.AddBytes(literal);
return true;
}
private StringToken CreateStringToken(
TemplateContext context,
String value)
{
var result = new StringToken(FileId, Line, Column, value);
context.Memory.AddBytes(result);
return result;
}
private SequenceToken CreateSequenceToken(TemplateContext context)
{
var result = new SequenceToken(FileId, Line, Column);
context.Memory.AddBytes(result);
return result;
}
private MappingToken CreateMappingToken(TemplateContext context)
{
var result = new MappingToken(FileId, Line, Column);
context.Memory.AddBytes(result);
return result;
}
}
}

View File

@@ -0,0 +1,221 @@
using System;
using System.Collections.Generic;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
public static class TemplateTokenExtensions
{
internal static BooleanToken AssertBoolean(
this TemplateToken value,
string objectDescription)
{
if (value is BooleanToken booleanToken)
{
return booleanToken;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(BooleanToken)}' was expected.");
}
internal static NullToken AssertNull(
this TemplateToken value,
string objectDescription)
{
if (value is NullToken nullToken)
{
return nullToken;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(NullToken)}' was expected.");
}
internal static NumberToken AssertNumber(
this TemplateToken value,
string objectDescription)
{
if (value is NumberToken numberToken)
{
return numberToken;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(NumberToken)}' was expected.");
}
internal static StringToken AssertString(
this TemplateToken value,
string objectDescription)
{
if (value is StringToken stringToken)
{
return stringToken;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(StringToken)}' was expected.");
}
internal static MappingToken AssertMapping(
this TemplateToken value,
string objectDescription)
{
if (value is MappingToken mapping)
{
return mapping;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(MappingToken)}' was expected.");
}
internal static void AssertNotEmpty(
this MappingToken mapping,
string objectDescription)
{
if (mapping.Count == 0)
{
throw new ArgumentException($"Unexpected empty mapping when reading '{objectDescription}'");
}
}
internal static ScalarToken AssertScalar(
this TemplateToken value,
string objectDescription)
{
if (value is ScalarToken scalar)
{
return scalar;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(ScalarToken)}' was expected.");
}
internal static SequenceToken AssertSequence(
this TemplateToken value,
string objectDescription)
{
if (value is SequenceToken sequence)
{
return sequence;
}
throw new ArgumentException($"Unexpected type '{value?.GetType().Name}' encountered while reading '{objectDescription}'. The type '{nameof(SequenceToken)}' was expected.");
}
internal static void AssertUnexpectedValue(
this LiteralToken literal,
string objectDescription)
{
throw new ArgumentException($"Error while reading '{objectDescription}'. Unexpected value '{literal.ToString()}'");
}
/// <summary>
/// Returns all tokens (depth first)
/// </summary>
public static IEnumerable<TemplateToken> Traverse(this TemplateToken token)
{
return Traverse(token, omitKeys: false);
}
/// <summary>
/// Returns all tokens (depth first)
/// </summary>
public static IEnumerable<TemplateToken> Traverse(
this TemplateToken token,
bool omitKeys)
{
if (token != null)
{
yield return token;
if (token is SequenceToken || token is MappingToken)
{
var state = new TraversalState(null, token);
while (state != null)
{
if (state.MoveNext(omitKeys))
{
token = state.Current;
yield return token;
if (token is SequenceToken || token is MappingToken)
{
state = new TraversalState(state, token);
}
}
else
{
state = state.Parent;
}
}
}
}
}
private sealed class TraversalState
{
public TraversalState(
TraversalState parent,
TemplateToken token)
{
Parent = parent;
m_token = token;
}
public bool MoveNext(bool omitKeys)
{
switch (m_token.Type)
{
case TokenType.Sequence:
var sequence = m_token as SequenceToken;
if (++m_index < sequence.Count)
{
Current = sequence[m_index];
return true;
}
else
{
Current = null;
return false;
}
case TokenType.Mapping:
var mapping = m_token as MappingToken;
// Return the value
if (m_isKey)
{
m_isKey = false;
Current = mapping[m_index].Value;
return true;
}
if (++m_index < mapping.Count)
{
// Skip the key, return the value
if (omitKeys)
{
m_isKey = false;
Current = mapping[m_index].Value;
return true;
}
// Return the key
m_isKey = true;
Current = mapping[m_index].Key;
return true;
}
Current = null;
return false;
default:
throw new NotSupportedException($"Unexpected token type '{m_token.Type}'");
}
}
private TemplateToken m_token;
private int m_index = -1;
private bool m_isKey;
public TemplateToken Current;
public TraversalState Parent;
}
}
}

View File

@@ -0,0 +1,332 @@
using System;
using System.Reflection;
using GitHub.Services.WebApi;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
/// <summary>
/// JSON serializer for TemplateToken objects
/// </summary>
internal sealed class TemplateTokenJsonConverter : VssSecureJsonConverter
{
public override Boolean CanWrite
{
get
{
return true;
}
}
public override Boolean CanConvert(Type objectType)
{
return typeof(TemplateToken).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
}
public override Object ReadJson(
JsonReader reader,
Type objectType,
Object existingValue,
JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
return new StringToken(null, null, null, reader.Value.ToString());
case JsonToken.Boolean:
return new BooleanToken(null, null, null, (Boolean)reader.Value);
case JsonToken.Float:
return new NumberToken(null, null, null, (Double)reader.Value);
case JsonToken.Integer:
return new NumberToken(null, null, null, (Double)(Int64)reader.Value);
case JsonToken.Null:
return new NullToken(null, null, null);
case JsonToken.StartObject:
break;
default:
return null;
}
Int32? type = null;
JObject value = JObject.Load(reader);
if (!value.TryGetValue("type", StringComparison.OrdinalIgnoreCase, out JToken typeValue))
{
type = TokenType.String;
}
else if (typeValue.Type == JTokenType.Integer)
{
type = (Int32)typeValue;
}
else
{
return existingValue;
}
Object newValue = null;
switch (type)
{
case TokenType.Null:
newValue = new NullToken(null, null, null);
break;
case TokenType.Boolean:
newValue = new BooleanToken(null, null, null, default(Boolean));
break;
case TokenType.Number:
newValue = new NumberToken(null, null, null, default(Double));
break;
case TokenType.String:
newValue = new StringToken(null, null, null, null);
break;
case TokenType.BasicExpression:
newValue = new BasicExpressionToken(null, null, null, null);
break;
case TokenType.InsertExpression:
newValue = new InsertExpressionToken(null, null, null);
break;
case TokenType.Sequence:
newValue = new SequenceToken(null, null, null);
break;
case TokenType.Mapping:
newValue = new MappingToken(null, null, null);
break;
}
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 (value is TemplateToken token)
{
switch (token.Type)
{
case TokenType.Null:
if (token.Line == null && token.Column == null)
{
writer.WriteNull();
}
else
{
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
writer.WriteEndObject();
}
return;
case TokenType.Boolean:
var booleanToken = token as BooleanToken;
if (token.Line == null && token.Column == null)
{
writer.WriteValue(booleanToken.Value);
}
else
{
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
writer.WritePropertyName("bool");
writer.WriteValue(booleanToken.Value);
writer.WriteEndObject();
}
return;
case TokenType.Number:
var numberToken = token as NumberToken;
if (token.Line == null && token.Column == null)
{
writer.WriteValue(numberToken.Value);
}
else
{
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
writer.WritePropertyName("num");
writer.WriteValue(numberToken.Value);
writer.WriteEndObject();
}
return;
case TokenType.String:
var stringToken = token as StringToken;
if (token.Line == null && token.Column == null)
{
writer.WriteValue(stringToken.Value);
}
else
{
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
writer.WritePropertyName("lit");
writer.WriteValue(stringToken.Value);
writer.WriteEndObject();
}
return;
case TokenType.BasicExpression:
var basicExpressionToken = token as BasicExpressionToken;
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
if (!String.IsNullOrEmpty(basicExpressionToken.Expression))
{
writer.WritePropertyName("expr");
writer.WriteValue(basicExpressionToken.Expression);
}
writer.WriteEndObject();
return;
case TokenType.InsertExpression:
var insertExpressionToken = token as InsertExpressionToken;
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
writer.WritePropertyName("directive");
writer.WriteValue(insertExpressionToken.Directive);
writer.WriteEndObject();
return;
case TokenType.Sequence:
var sequenceToken = token as SequenceToken;
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
if (sequenceToken.Count > 0)
{
writer.WritePropertyName("seq");
writer.WriteStartArray();
foreach (var item in sequenceToken)
{
serializer.Serialize(writer, item);
}
writer.WriteEndArray();
}
writer.WriteEndObject();
return;
case TokenType.Mapping:
var mappingToken = token as MappingToken;
writer.WriteStartObject();
writer.WritePropertyName("type");
writer.WriteValue(token.Type);
if (token.Line != null)
{
writer.WritePropertyName("line");
writer.WriteValue(token.Line);
}
if (token.Line != null)
{
writer.WritePropertyName("col");
writer.WriteValue(token.Column);
}
if (mappingToken.Count > 0)
{
writer.WritePropertyName("map");
writer.WriteStartArray();
foreach (var item in mappingToken)
{
serializer.Serialize(writer, item);
}
writer.WriteEndArray();
}
writer.WriteEndObject();
return;
}
}
throw new NotSupportedException($"Unexpected type '{value?.GetType().FullName}' when serializing template token");
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
namespace GitHub.DistributedTask.ObjectTemplating.Tokens
{
internal static class TokenType
{
internal const Int32 String = 0;
internal const Int32 Sequence = 1;
internal const Int32 Mapping = 2;
internal const Int32 BasicExpression = 3;
internal const Int32 InsertExpression = 4;
internal const Int32 Boolean = 5;
internal const Int32 Number = 6;
internal const Int32 Null = 7;
}
}