Skip to content

Instantly share code, notes, and snippets.

@gursharan001
Last active December 19, 2015 01:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gursharan001/5875716 to your computer and use it in GitHub Desktop.
Save gursharan001/5875716 to your computer and use it in GitHub Desktop.
APM wrapper generator for synchronous methods using Task Library and Roslyn
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using Roslyn.Compilers;
using Roslyn.Compilers.CSharp;
using Roslyn.Services;
using Roslyn.Services.CSharp;
using Roslyn.Services.Formatting;
namespace GenerateAsyncMethods
{
class Program
{
static List<List<T>> SplitArray<T>(List<T> list, int bucketSize)
{
var buckets = (list.Count() / bucketSize) + 1;
List<List<T>> result = new List<List<T>>();
for (int bucketsTaken = 0; bucketsTaken < buckets; bucketsTaken++)
{
var tempList = list.Skip(bucketsTaken * bucketSize).Take(bucketSize).ToList();
if (tempList.Count() > 0)
result.Add(tempList);
}
return result;
}
static string RecurseType(List<List<string>> typesList)
{
if (typesList.Count() == 0) return string.Empty;
var myTypeList = typesList.Take(1).First();
var subList = RecurseType(typesList.Skip(1).ToList());
var myType = "Tuple<" + String.Join(",", myTypeList) + (string.IsNullOrEmpty(subList) ? string.Empty : "," + subList) + ">";
return myType;
}
static string GetReturnType(MethodDeclarationSyntax m)
{
StringBuilder sb = new StringBuilder();
var rets = new List<string>() { m.ReturnType.Kind != SyntaxKind.VoidKeyword ? m.ReturnType.ToString() : null };
var paramTypeQuery =
from p in m.ParameterList.Parameters
where p.Modifiers.Any(SyntaxKind.RefKeyword) || p.Modifiers.Any(SyntaxKind.OutKeyword)
select p.Type.ToString();
var paramTypes = paramTypeQuery.ToList();
if (m.ReturnType.Kind != SyntaxKind.VoidKeyword)
paramTypes.Insert(0, m.ReturnType.ToString());
var splitTypes = SplitArray(paramTypes, 7);
var typesStr = RecurseType(splitTypes);
sb.Append(typesStr);
System.Console.WriteLine(sb.ToString());
return sb.ToString();
}
static void Main(string[] args)
{
SyntaxTree referenceTree = SyntaxTree.ParseFile(@"..\..\input.cs");
var referenceRoot = (CompilationUnitSyntax)referenceTree.GetRoot();
var referenceMethods = from methodDeclaration in referenceRoot.DescendantNodes().OfType<MethodDeclarationSyntax>()
where methodDeclaration.AttributeLists.Any(alist => alist.Attributes.Any(a => a.Name.ToString() == "WebMethod"))
select methodDeclaration;
var c = Syntax.CompilationUnit();
foreach (MethodDeclarationSyntax m in referenceMethods)
{
c = c
.AddMembers(AddAsyncMethod(m))
.AddMembers(AddBeginForApmMethod(m))
.AddMembers(AddEndForApmMethod(m));
//break;
}
c = c.NormalizeWhitespace();
c = c.WithEndOfFileToken
(
Syntax.Token
(
SyntaxKind.EndOfFileToken
)
);
using (var f = File.Open(@"..\..\output.cs", FileMode.Create, FileAccess.Write))
{
using (var tw = new StreamWriter(f))
{
c.Format(FormattingOptions.GetDefaultOptions());
c.WriteTo(tw);
}
}
}
private static MemberDeclarationSyntax AddEndForApmMethod(MethodDeclarationSyntax m)
{
Expression<Func<string, string>> s = (string str) => !string.IsNullOrEmpty(str) ? "," + str : str;
var prepender = s.Compile();
var restIndexer = "result";
int itemIndexer = m.ReturnType.Kind != SyntaxKind.VoidKeyword ? 2 : 1;
return
Syntax
.MethodDeclaration(
m.ReturnType,
Syntax.ParseToken(string.Format("End{0}", m.Identifier.ToString())))
.WithModifiers(m.Modifiers)
.WithParameterList(
Syntax.ParseParameterList(
string.Format("(IAsyncResult asyncResult{0})",
prepender(
String.Join(",",
(from p in m.ParameterList.Parameters
where p.Modifiers.Any(SyntaxKind.OutKeyword) || p.Modifiers.Any(SyntaxKind.RefKeyword)
select "out " + p.Type.ToString() + " " + p.Identifier.ToString()).ToArray())
)
)
)
)
.WithBody(
Syntax
.Block()
.WithOpenBraceToken(Syntax.Token(SyntaxKind.OpenBraceToken))
.WithStatements(
new SyntaxList<StatementSyntax>()
.Add(
Syntax.ParseStatement(
(m.ParameterList.Parameters.Any(SyntaxKind.RefKeyword) ||
m.ParameterList.Parameters.Any(SyntaxKind.OutKeyword) ||
m.ReturnType.Kind != SyntaxKind.VoidKeyword)
? string.Format("var result = ((Task<{0}>) asyncResult).Result;",
GetReturnType(m)
)
: string.Empty
)
)
.Add(
(from p in m.ParameterList.Parameters
where p.Modifiers.Any(SyntaxKind.OutKeyword) || p.Modifiers.Any(SyntaxKind.RefKeyword)
select Syntax.ParseStatement(
string.Format("{0}={1};"
, p.Identifier.ToString()
,
(
itemIndexer % 7 == 1
? (restIndexer += ".Rest")
: restIndexer
)
+ ".Item" +
(
itemIndexer % 7 == 1
? (
itemIndexer > 1
? (itemIndexer = 2) - 1
: itemIndexer++
)
: itemIndexer++
)
)
)).ToArray()
)
.Add(
Syntax.ParseStatement(
m.ReturnType.Kind != SyntaxKind.VoidKeyword
? "return result.Item1;"
: string.Empty
)
)
)
.WithCloseBraceToken(Syntax.Token(SyntaxKind.CloseBraceToken))
);
}
private static MemberDeclarationSyntax AddBeginForApmMethod(MethodDeclarationSyntax m)
{
Expression<Func<string, string>> s = (string str) => !string.IsNullOrEmpty(str) ? str + "," : str;
var appender = s.Compile();
return
Syntax
.MethodDeclaration(
Syntax.ParseTypeName("IAsyncResult"),
Syntax.ParseToken(string.Format("Begin{0}", m.Identifier.ToString())))
.WithModifiers(m.Modifiers)
.WithParameterList(
Syntax.ParseParameterList(
string.Format("({0}AsyncCallback callback, object state)",
appender(
String.Join(",",
(from p in m.ParameterList.Parameters
where !p.Modifiers.Any(SyntaxKind.OutKeyword)
select p.Type.ToString() + " " + p.Identifier.ToString()).ToArray())
)
)
)
)
.WithBody(
Syntax
.Block()
.WithOpenBraceToken(Syntax.Token(SyntaxKind.OpenBraceToken))
.WithStatements(
Syntax.ParseStatement(
string.Format("return {0}Async({1}).AsApm(callback, state);",
m.Identifier.ToString(),
String.Join(",",
(from p in m.ParameterList.Parameters
where !p.Modifiers.Any(SyntaxKind.OutKeyword)
select p.Identifier.ToString()).ToArray())
)
)
)
.WithCloseBraceToken(Syntax.Token(SyntaxKind.CloseBraceToken))
);
}
private static MethodDeclarationSyntax AddAsyncMethod(MethodDeclarationSyntax m)
{
return Syntax.MethodDeclaration(
Syntax.ParseTypeName("Task<" + GetReturnType(m) + ">"),
Syntax.ParseToken(m.Identifier.ToString() + "Async")
)
.WithModifiers(m.Modifiers)
.WithParameterList(
Syntax.ParseParameterList(
"(" +
String.Join(",",
(from p in m.ParameterList.Parameters
where !p.Modifiers.Any(SyntaxKind.OutKeyword)
select p.Type.ToString() + " " + p.Identifier.ToString()).ToArray()
) +
")")
)
.WithBody(
Syntax
.Block()
.WithOpenBraceToken(Syntax.Token(SyntaxKind.OpenBraceToken))
.WithStatements(
new SyntaxList<StatementSyntax>()
.Add(
(from p in m.ParameterList.Parameters
where p.Modifiers.Any(SyntaxKind.RefKeyword) || p.Modifiers.Any(SyntaxKind.OutKeyword)
select
Syntax.ParseStatement(
p.Type.ToString() + " "
+ p.Identifier.ToString() + (p.Modifiers.Any(SyntaxKind.RefKeyword) ? "1 = " + p.Identifier.ToString() : string.Empty) + ";"
)
).ToArray()
)
.Add(Syntax.ParseStatement("var tcs = new TaskCompletionSource<" + GetReturnType(m) + ">();"))
.Add(Syntax.ParseStatement(
@"ThreadPool.QueueUserWorkItem(_ =>
{
try
{" +
string.Format(
"{0}{1}({2});",
m.ReturnType.Kind != SyntaxKind.VoidKeyword ? "var retVal=" : string.Empty,
m.Identifier.ToString(),
String.Join(",",
(from p in m.ParameterList.Parameters
select string.Format(
"{0} {1}{2}",
p.Modifiers.Any(SyntaxKind.RefKeyword)
? "ref"
: p.Modifiers.Any(SyntaxKind.OutKeyword)
? "out"
: string.Empty,
p.Identifier.ToString(),
p.Modifiers.Any(SyntaxKind.RefKeyword) ? "1" : string.Empty)
).ToArray())) +
string.Format(
"var result={0};",
GetReturnArguments(m)
) +
@"
tcs.SetResult(result);
}
catch (Exception exc)
{
tcs.SetException(exc);
}
});").NormalizeWhitespace())
.Add(Syntax.ParseStatement("return tcs.Task;"))
)
.WithCloseBraceToken(Syntax.Token(SyntaxKind.CloseBraceToken))
);
}
private static string GetReturnArguments(MethodDeclarationSyntax m)
{
StringBuilder sb = new StringBuilder();
var rets = new List<parms>() { m.ReturnType.Kind != SyntaxKind.VoidKeyword ? new parms { type = m.ReturnType.ToString(), identifier = "retVal" } : null };
var paramTypeQuery =
from p in m.ParameterList.Parameters
where p.Modifiers.Any(SyntaxKind.RefKeyword) || p.Modifiers.Any(SyntaxKind.OutKeyword)
select new parms { identifier = p.Identifier.ToString() + (p.Modifiers.Any(SyntaxKind.RefKeyword) ? "1" : string.Empty), type = p.Type.ToString() };
var paramTypes = paramTypeQuery.ToList();
if (rets.Count != 0)
paramTypes.Insert(0, rets[0]);
List<List<parms>> splitTypes = SplitArray(paramTypes, 7);
var typesStr = RecurseParms(splitTypes);
sb.Append(typesStr);
System.Console.WriteLine(sb.ToString());
return sb.ToString();
}
private static string RecurseParms(List<List<parms>> parmsList)
{
if (parmsList.Count() == 0) return null;
var myTempTypeList = parmsList.Select(p1 => p1.Select(p2 => p2.type).ToList()).ToList();
var myTypeList = RecurseType(myTempTypeList);
var myArgsList = (from p in parmsList.Take(1).First()
select p.identifier.ToString()).ToArray();
var subArgsList = RecurseParms(parmsList.Skip(1).ToList());
var myType =
string.Format("new {0}({1})", myTypeList,
String.Join(",", myArgsList) + (string.IsNullOrEmpty(subArgsList) ? string.Empty : "," + subArgsList));
return myType;
}
}
public class parms
{
public string type;
public string identifier;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment