Last active
December 19, 2015 01:28
-
-
Save gursharan001/5875716 to your computer and use it in GitHub Desktop.
APM wrapper generator for synchronous methods using Task Library and Roslyn
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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