PageRenderTime 46ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/src/ServiceStack/WebHost.EndPoints/Support/Markdown/Evaluator.cs

http://github.com/ServiceStack/ServiceStack
C# | 441 lines | 357 code | 72 blank | 12 comment | 37 complexity | 78ca1be146936f1a051e0c6a77d8714b MD5 | raw file
Possible License(s): BSD-3-Clause
  1. using System;
  2. using System.CodeDom.Compiler;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using System.Xml;
  8. using System.Text;
  9. using ServiceStack.Common.Utils;
  10. using ServiceStack.Logging;
  11. using ServiceStack.ServiceHost;
  12. using ServiceStack.Text;
  13. namespace ServiceStack.WebHost.Endpoints.Support.Markdown
  14. {
  15. public class EvaluatorExecutionContext
  16. {
  17. public EvaluatorExecutionContext()
  18. {
  19. this.Items = new List<EvaluatorItem>();
  20. this.TypeProperties = new Dictionary<string, Type>();
  21. }
  22. public Type BaseType { get; set; }
  23. public Type[] GenericArgs { get; set; }
  24. public IDictionary<string, Type> TypeProperties { get; set; }
  25. public List<EvaluatorItem> Items { get; private set; }
  26. public Evaluator Build()
  27. {
  28. return new Evaluator(Items, BaseType, GenericArgs, TypeProperties);
  29. }
  30. }
  31. public class Evaluator
  32. {
  33. private static ILog Log = LogManager.GetLogger(typeof (Evaluator));
  34. const string StaticMethodName = "__tmp";
  35. Assembly compiledAssembly;
  36. Type compiledType = null;
  37. object compiled = null;
  38. EmptyCtorDelegate compiledTypeCtorFn;
  39. private Type BaseType { get; set; }
  40. private Type[] GenericArgs { get; set; }
  41. private IDictionary<string, Type> TypeProperties { get; set; }
  42. static readonly List<Assembly> Assemblies = new List<Assembly> {
  43. typeof(string).Assembly, //"system.dll",
  44. // typeof(XmlDocument).Assembly, //"system.xml.dll",
  45. typeof(Expression).Assembly, //"system.core.dll",
  46. typeof(AppHostBase).Assembly, //"ServiceStack.dll",
  47. typeof(JsConfig).Assembly, //"ServiceStack.Text.dll",
  48. typeof(IService<>).Assembly, //"ServiceStack.Interfaces.dll",
  49. typeof(Common.UrnId).Assembly, //"ServiceStack.Common.dll"
  50. };
  51. static readonly List<string> AssemblyNames = new List<string> {
  52. "System",
  53. "System.Text",
  54. // "System.Xml",
  55. "System.Collections",
  56. "System.Collections.Generic",
  57. "System.Linq",
  58. "System.Linq.Expressions",
  59. "ServiceStack.Html",
  60. "ServiceStack.Markdown"
  61. };
  62. public static void AddAssembly(string assemblyName)
  63. {
  64. if (AssemblyNames.Contains(assemblyName)) return;
  65. AssemblyNames.Add(assemblyName);
  66. try
  67. {
  68. var assembly = Assembly.Load(assemblyName);
  69. if (!Assemblies.Contains(assembly))
  70. Assemblies.Add(assembly);
  71. }
  72. catch (Exception ex)
  73. {
  74. Log.Error("Can't load assembly: " + assemblyName, ex);
  75. }
  76. }
  77. public static Type FindType(string typeName)
  78. {
  79. if (typeName == null || typeName.Contains(".")) return null;
  80. var type = Type.GetType(typeName);
  81. if (type != null) return type;
  82. foreach (var assembly in Assemblies)
  83. {
  84. var searchType = assembly.GetName().Name + "." + typeName;
  85. type = assembly.GetType(searchType);
  86. if (type != null)
  87. return type;
  88. }
  89. return null;
  90. }
  91. public Evaluator(IEnumerable<EvaluatorItem> items)
  92. : this(items, null, null, null)
  93. {
  94. }
  95. public Evaluator(IEnumerable<EvaluatorItem> items,
  96. Type baseType, Type[] genericArgs, IDictionary<string, Type> typeProperties)
  97. {
  98. this.BaseType = baseType;
  99. this.GenericArgs = genericArgs ?? new Type[0];
  100. this.TypeProperties = typeProperties;
  101. ConstructEvaluator(items);
  102. }
  103. public Evaluator(Type returnType, string expression, string name)
  104. : this(returnType, expression, name, null) { }
  105. public Evaluator(Type returnType, string expression, string name, IDictionary<string, Type> exprParams)
  106. {
  107. EvaluatorItem[] items =
  108. {
  109. new EvaluatorItem {
  110. ReturnType = returnType,
  111. Expression = expression,
  112. Name = name,
  113. Params = exprParams ?? new Dictionary<string, Type>(),
  114. }
  115. };
  116. ConstructEvaluator(items);
  117. }
  118. public Evaluator(EvaluatorItem item)
  119. {
  120. EvaluatorItem[] items = { item };
  121. ConstructEvaluator(items);
  122. }
  123. public string GetTypeName(Type type)
  124. {
  125. try
  126. {
  127. //Inner classes?
  128. var typeName = type == null
  129. //|| type.FullName == null
  130. ? null
  131. : type.FullName.Replace('+', '.').SplitOnFirst('`')[0];
  132. if (typeName == null) return null;
  133. if (type.IsGenericType()
  134. //TODO: support GenericTypeDefinition properly
  135. && !type.IsGenericTypeDefinition
  136. )
  137. {
  138. var genericArgs = type.GetGenericArguments();
  139. typeName += "<";
  140. var i = 0;
  141. foreach (var genericArg in genericArgs)
  142. {
  143. if (i++ > 0)
  144. typeName += ", ";
  145. typeName += GetTypeName(genericArg);
  146. }
  147. typeName += ">";
  148. }
  149. return typeName;
  150. }
  151. catch (Exception)
  152. {
  153. //Console.WriteLine(ex);
  154. throw;
  155. }
  156. }
  157. private static readonly bool IsVersion4AndUp = Type.GetType("System.Collections.Concurrent.Partitioner") != null;
  158. private static void AddAssembly(CompilerParameters cp, string location)
  159. {
  160. //Error if trying to re-add ref to mscorlib or System.Core for .NET 4.0
  161. if (IsVersion4AndUp &&
  162. (location == typeof(string).Assembly.Location
  163. || location == typeof(Expression).Assembly.Location))
  164. return;
  165. cp.ReferencedAssemblies.Add(location);
  166. }
  167. private void ConstructEvaluator(IEnumerable<EvaluatorItem> items)
  168. {
  169. //var codeCompiler = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v3.5" } });
  170. var codeCompiler = CodeDomProvider.CreateProvider("CSharp");
  171. var cp = new CompilerParameters //(new[] { "mscorlib.dll", "system.core.dll" })
  172. {
  173. GenerateExecutable = false,
  174. GenerateInMemory = true,
  175. };
  176. Assemblies.ForEach(x => AddAssembly(cp, x.Location));
  177. var code = new StringBuilder();
  178. AssemblyNames.ForEach(x =>
  179. code.AppendFormat("using {0};\n", x));
  180. code.Append(
  181. @"
  182. namespace CSharpEval
  183. {
  184. public class _Expr
  185. ");
  186. if (this.BaseType != null)
  187. {
  188. code.Append(" : " + GetTypeName(this.BaseType));
  189. if (GenericArgs.Length > 0)
  190. {
  191. code.Append("<");
  192. var i = 0;
  193. foreach (var genericArg in GenericArgs)
  194. {
  195. if (i++ > 0) code.Append(", ");
  196. code.Append(GetTypeName(genericArg));
  197. ReferenceTypesIfNotExist(cp, Assemblies, genericArg);
  198. }
  199. code.AppendLine(">");
  200. }
  201. ReferenceTypesIfNotExist(cp, Assemblies, this.BaseType);
  202. }
  203. code.AppendLine(" {");
  204. AddPropertiesToTypeIfAny(code);
  205. foreach (var item in items)
  206. {
  207. var sbParams = new StringBuilder();
  208. foreach (var param in item.Params)
  209. {
  210. if (sbParams.Length > 0)
  211. sbParams.Append(", ");
  212. var typeName = GetTypeName(param.Value);
  213. sbParams.AppendFormat("{0} {1}", typeName, param.Key);
  214. var paramType = param.Value;
  215. ReferenceAssembliesIfNotExists(cp, paramType, Assemblies);
  216. }
  217. var isVoid = item.ReturnType == typeof(void);
  218. var returnType = isVoid ? "void" : GetTypeName(item.ReturnType);
  219. code.AppendFormat(" public {0} {1}({2})",
  220. returnType, item.Name, sbParams);
  221. code.AppendLine(" {");
  222. if (isVoid)
  223. {
  224. code.AppendFormat(" {0}; \n", item.Expression);
  225. }
  226. else
  227. {
  228. code.AppendFormat(" return ({0}); \n", item.Expression);
  229. }
  230. code.AppendLine(" }");
  231. }
  232. code.AppendLine(" }");
  233. code.AppendLine("}");
  234. if (IsVersion4AndUp)
  235. {
  236. //var type = Type.GetType("System.Collections.Concurrent.Partitioner");
  237. //if (type != null)
  238. // cp.ReferencedAssemblies.Add(type.Assembly.Location);
  239. if (!Env.IsMono)
  240. {
  241. //cp.ReferencedAssemblies.Add(@"C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.dll");
  242. cp.ReferencedAssemblies.Add(@"C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll");
  243. }
  244. }
  245. var src = code.ToString();
  246. var compilerResults = codeCompiler.CompileAssemblyFromSource(cp, src);
  247. if (compilerResults.Errors.HasErrors)
  248. {
  249. var error = new StringBuilder();
  250. error.Append("Error Compiling Expression: ");
  251. foreach (CompilerError err in compilerResults.Errors)
  252. {
  253. error.AppendFormat("{0}\n", err.ErrorText);
  254. }
  255. throw new Exception("Error Compiling Expression: " + error);
  256. }
  257. compiledAssembly = compilerResults.CompiledAssembly;
  258. compiled = compiledAssembly.CreateInstance("CSharpEval._Expr");
  259. compiledType = compiled.GetType();
  260. compiledTypeCtorFn = Text.ReflectionExtensions.GetConstructorMethodToCache(compiledType);
  261. }
  262. private static void ReferenceTypesIfNotExist(CompilerParameters cp, List<Assembly> assemblies, params Type[] paramTypes)
  263. {
  264. foreach (var paramType in paramTypes)
  265. {
  266. if (!assemblies.Contains(paramType.Assembly))
  267. {
  268. assemblies.Add(paramType.Assembly);
  269. AddAssembly(cp, paramType.Assembly.Location);
  270. }
  271. }
  272. }
  273. private static void ReferenceAssembliesIfNotExists(CompilerParameters cp, Type paramType, List<Assembly> assemblies)
  274. {
  275. var typeAssemblies = new List<Assembly>();
  276. var typeAssembly = paramType.Assembly;
  277. if (!assemblies.Contains(typeAssembly))
  278. typeAssemblies.Add(typeAssembly);
  279. if (paramType.IsGenericType)
  280. {
  281. var genericArgs = paramType.GetGenericArguments();
  282. typeAssemblies.AddRange(genericArgs.Select(x => x.Assembly));
  283. }
  284. foreach (var assembly in typeAssemblies)
  285. {
  286. assemblies.Add(assembly);
  287. if (Common.ReflectionExtensions.IsDynamic(assembly)) continue;
  288. AddAssembly(cp, assembly.Location);
  289. }
  290. }
  291. private void AddPropertiesToTypeIfAny(StringBuilder code)
  292. {
  293. if (this.TypeProperties != null)
  294. {
  295. foreach (var typeProperty in TypeProperties)
  296. {
  297. var name = typeProperty.Key;
  298. var type = typeProperty.Value;
  299. var typeName = GetTypeName(type);
  300. var mi = type.GetMember("Instance", BindingFlags.Static | BindingFlags.Public);
  301. var hasSingleton = mi.Length > 0;
  302. var returnExpr = hasSingleton
  303. ? typeName + ".Instance"
  304. : "new " + typeName + "()";
  305. code.AppendFormat(" public {0} {1} = {2};\n", typeName, name, returnExpr);
  306. }
  307. }
  308. }
  309. public T GetInstance<T>()
  310. {
  311. return (T)compiled;
  312. }
  313. public object CreateInstance()
  314. {
  315. return compiledTypeCtorFn();
  316. }
  317. public MethodInfo GetCompiledMethodInfo(string name)
  318. {
  319. return compiledType.GetMethod(name);
  320. }
  321. public object Evaluate(string name, params object[] exprParams)
  322. {
  323. return Evaluate(compiled, name, exprParams);
  324. }
  325. public object Evaluate(object instance, string name, params object[] exprParams)
  326. {
  327. try
  328. {
  329. var mi = compiledType.GetMethod(name);
  330. return mi.Invoke(instance, exprParams);
  331. }
  332. catch (TargetInvocationException ex)
  333. {
  334. Console.WriteLine(ex.InnerException);
  335. throw ex.InnerException;
  336. }
  337. }
  338. public T Eval<T>(string name, params object[] exprParams)
  339. {
  340. return (T)Evaluate(name, exprParams);
  341. }
  342. public static object Eval(string code)
  343. {
  344. var eval = new Evaluator(typeof(object), code, StaticMethodName);
  345. return eval.Evaluate(StaticMethodName);
  346. }
  347. public static T Eval<T>(string code)
  348. {
  349. var eval = new Evaluator(typeof(T), code, StaticMethodName);
  350. return (T)eval.Evaluate(StaticMethodName);
  351. }
  352. }
  353. public class EvaluatorItem
  354. {
  355. public EvaluatorItem() { }
  356. public EvaluatorItem(Type returnType, string name, string expression, IDictionary<string, Type> exprParams)
  357. {
  358. ReturnType = returnType;
  359. Name = name;
  360. Expression = expression;
  361. Params = exprParams;
  362. }
  363. public Type ReturnType { get; set; }
  364. public string Name { get; set; }
  365. public string Expression { get; set; }
  366. public IDictionary<string, Type> Params { get; set; }
  367. }
  368. }