/// <summary> /// Adds a new method with has return to the type, with the specified name. /// </summary> /// <typeparam name="T">The type of the tag.</typeparam> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <param name="returnType">The return type</param> /// <returns></returns> public static MethodBuilder CreateReutrnMethod <T>(this CompileContext ctx, Type returnType) { if (returnType.FullName == "System.Void") { return(CreateReutrnMethod(ctx.TypeBuilder, $"Execute{typeof(T).Name}{ctx.Seed}", returnType)); } return(CreateReutrnMethod(ctx.TypeBuilder, $"Get{typeof(T).Name}{ctx.Seed}", returnType)); }
/// <summary> /// calling the tag. /// </summary> /// <param name="il">The <see cref="ILGenerator"/></param> /// <param name="ctx">The <see cref="CompileContext"/></param> /// <param name="tag">The <see cref="ITag"/></param> /// <param name="before">The action.</param> /// <param name="completed">The action of the completed.</param> public static void CallTag(this ILGenerator il, CompileContext ctx, ITag tag, Action <ILGenerator, bool, bool> before,//hasReturn,call Action <ILGenerator, Type> completed) { if (tag is EndTag _ || tag is CommentTag _) { return; } if (tag is TextTag textTag) { if (string.IsNullOrEmpty(textTag.Text)) { return; } before?.Invoke(il, true, false); var text = textTag.ToString(ctx.OutMode); il.Emit(OpCodes.Ldstr, text); completed?.Invoke(il, typeof(string)); return; } if (tag is ITypeTag typeTag) { if (typeTag.Value == null) { return; } before?.Invoke(il, true, false); Type returnType = il.CallTypeTag(typeTag); completed?.Invoke(il, returnType); return; } if (tag is SetTag setTag) { ctx.Set(setTag.Name, ctx.GuessType(setTag.Value)); } var m = ctx.CompileTag(tag); if (m.ReturnType.FullName != "System.Void") { before?.Invoke(il, true, true); il.Emit(OpCodes.Call, m); completed?.Invoke(il, m.ReturnType); } else { before?.Invoke(il, false, true); il.Emit(OpCodes.Call, m); completed?.Invoke(il, null); } }
/// <summary> /// Gets the <see cref="Type"/> with the specified tag. /// </summary> /// <param name="name">The tag name of the type to get.</param> /// <param name="tag">The tag of the type to get.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns>The type with the specified tag, if found; otherwise, null.</returns> public Type GetType(string name, ITag tag, CompileContext ctx) { if (dict.TryGetValue(name, out var func)) { var type = func(tag, ctx); if (type != null) { return(type); } } throw new CompileException(tag, $"[{name}]:\"{tag.ToSource()}\" is not defined!"); }
/// <summary> /// Compiles the specified tag into a method. /// </summary> /// <param name="name">The name of the tag.</param> /// <param name="tag">The <see cref="ITag"/>.</param> /// <param name="context">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static MethodInfo Compile(string name, ITag tag, CompileContext context) { string tagKey = GetTagKey(tag); if (tagKey != null && context.Methods.TryGetValue(tagKey, out MethodInfo mi)) { return(mi); } var func = context.CompileBuilder.Build(name); return(func(tag, context)); }
/// <summary> /// /// </summary> /// <typeparam name="T"></typeparam> /// <param name="tag"></param> /// <param name="ctx"></param> /// <returns></returns> internal static MethodInfo IfCompile <T>(this CompileContext ctx, T tag) where T : ElseifTag { var stringBuilderType = typeof(StringBuilder); var t = tag; var type = ctx.GuessType(t); var mb = ctx.CreateReutrnMethod <T>(type); var il = mb.GetILGenerator(); var labelEnd = il.DefineLabel(); il.DeclareLocal(type); il.DeclareLocal(stringBuilderType); il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); il.Emit(OpCodes.Stloc_1); for (var i = 0; i < t.Children.Count; i++) { il.CallTag(ctx, t.Children[i], (nil, hasReturn, needCall) => { if (hasReturn) { nil.Emit(OpCodes.Ldloc_1); } if (needCall) { nil.Emit(OpCodes.Ldarg_0); nil.Emit(OpCodes.Ldarg_1); } }, (nil, returnType) => { if (returnType == null) { return; } nil.StringAppend(ctx, returnType); nil.Emit(OpCodes.Pop); }); } il.Emit(OpCodes.Ldloc_1); il.Call(stringBuilderType, stringBuilderType.GetMethodInfo("ToString", Type.EmptyTypes)); il.Emit(OpCodes.Stloc_0); il.MarkLabel(labelEnd); il.Emit(OpCodes.Ldloc_0); il.Emit(OpCodes.Ret); return(mb.GetBaseDefinition()); }
/// <summary> /// Compiles the specified tag into a method. /// </summary> /// <param name="name">The name of the tag.</param> /// <param name="tag">The <see cref="ITag"/>.</param> /// <param name="context">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static MethodInfo Compile(this CompileContext context, string name, ITag tag) { string tagKey = GetTagKey(tag); if (tagKey != null && context.Methods.TryGetValue(tagKey, out MethodInfo mi)) { return(mi); } var func = context.CompileBuilder.Build(name); var method = func(tag, context); if (tagKey != null) { context.Methods[tagKey] = method; } return(method); }
/// <summary> /// Compile the array into a dynamic class. /// </summary> /// <param name="tags">The array of the tag.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> private static ICompilerResult Compile(this CompileContext ctx, ITag[] tags) { var baseType = typeof(CompilerResult); var typeBuilder = ObjectBuilder.DefineType(baseType.GetInterface(nameof(ICompilerResult)), baseType, $"{baseType.Namespace}.Template{ctx.Name.GetHashCode()}"); var targetMethod = baseType.GetMethodInfo("Render", new Type[] { typeof(TextWriter), typeof(TemplateContext) }); var method = typeBuilder.DefineMethod(targetMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, targetMethod.ReturnType, new Type[] { typeof(TextWriter), typeof(TemplateContext) }); var methodGenerator = method.GetILGenerator(); ctx.TypeBuilder = typeBuilder; var il = ctx.Generator = methodGenerator; var labelPass = il.DefineLabel(); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Ldnull); il.Emit(OpCodes.Ceq); il.Emit(OpCodes.Brfalse_S, labelPass); il.Emit(OpCodes.Ldstr, "writer cannot be null!"); il.Emit(OpCodes.Newobj, typeof(System.ArgumentNullException).GetConstructor(new Type[] { typeof(string) })); il.Emit(OpCodes.Throw); il.MarkLabel(labelPass); for (var i = 0; i < tags.Length; i++) { CompileToRender(ctx, tags[i]); } il.Emit(OpCodes.Nop); il.Emit(OpCodes.Ret); Type type = #if NETSTANDARD2_0 ctx.TypeBuilder.AsType(); #else ctx.TypeBuilder.CreateType(); #endif if (type == null) { return(null); } var instance = type.CreateInstance <ICompilerResult>(); return(instance); }
/// <summary> /// /// </summary> /// <param name="tag"></param> /// <param name="context"></param> public static void CompileToRender(ITag tag, CompileContext context) { var tagType = tag.GetType(); if (tag is EndTag _ || tag is CommentTag _) { return; } if (tag is TextTag textTag) { var text = textTag.Text; if (context.StripWhiteSpace) { text = text?.Trim(); } if (!string.IsNullOrEmpty(text)) { context.Generator.Emit(OpCodes.Ldarg_1); context.Generator.Emit(OpCodes.Ldstr, text); context.Generator.Emit(OpCodes.Callvirt, typeof(TextWriter).GetMethod("Write", new Type[] { typeof(string) })); } return; } if (tag is ITypeTag value) { context.Generator.Emit(OpCodes.Ldarg_1); if (value.Value == null) { context.Generator.Emit(OpCodes.Ldstr, string.Empty); } else { context.Generator.Emit(OpCodes.Ldstr, value.Value.ToString()); } context.Generator.Emit(OpCodes.Callvirt, typeof(TextWriter).GetMethod("Write", new Type[] { typeof(string) })); return; } CompileToRender(tag, context, tagType); }
/// <summary> /// Appends the string representation of a specified object to <see cref="StringBuilder"/>. /// </summary> /// <param name="il">The <see cref="ILGenerator"/></param> /// <param name="c">The <see cref="CompileContext"/></param> /// <param name="returnType"></param>\ public static void StringAppend(this ILGenerator il, CompileContext c, Type returnType) { var stringBuilderType = typeof(StringBuilder); MethodInfo appendMethod; switch (returnType.FullName) { case "System.Object": case "System.String": case "System.Decimal": case "System.Single": case "System.UInt64": case "System.Int64": case "System.UInt32": case "System.Boolean": case "System.Double": case "System.Char": case "System.UInt16": case "System.Int16": case "System.Byte": case "System.SByte": case "System.Int32": appendMethod = DynamicHelpers.GetMethod(stringBuilderType, "Append", new Type[] { returnType }); break; default: if (returnType.IsValueType) { var p = il.DeclareLocal(returnType); il.Emit(OpCodes.Stloc, p.LocalIndex); LoadVariable(il, returnType, p.LocalIndex); il.Call(returnType, DynamicHelpers.GetMethod(typeof(object), "ToString", Type.EmptyTypes)); appendMethod = DynamicHelpers.GetMethod(stringBuilderType, "Append", new Type[] { typeof(string) }); } else { appendMethod = DynamicHelpers.GetMethod(stringBuilderType, "Append", new Type[] { typeof(object) }); } break; } il.Emit(OpCodes.Callvirt, appendMethod); }
private static void CompileToRender(this CompileContext context, ITag tag, Type tagType) { if (tag == null) { return; } MethodInfo method = null; try { if (tag is ReferenceTag) { method = Compile(context, (tag as ReferenceTag).Child); } else { method = Compile(context, tag); } if (method == null) { throw new CompileException(tag, $"Cannot compile the {tag.GetType().Name} on {tag.ToSource()}[line:{tag.FirstToken?.BeginLine ?? 0},col:{tag.FirstToken?.BeginColumn ?? 0}]:"); } } catch (System.Exception exception) { if (context.ThrowExceptions) { throw; } context.Generator.Emit(OpCodes.Ldarg_2); context.Generator.Emit(OpCodes.Ldstr, exception.ToString()); context.Generator.Emit(OpCodes.Newobj, typeof(CompileException).GetConstructor(new Type[] { typeof(string) })); //context.Generator.Emit(OpCodes.Throw); context.Generator.Emit(OpCodes.Callvirt, typeof(TemplateContext).GetMethod("AddError", new Type[] { typeof(System.Exception) })); return; } CompileToRender(context, tag, tagType, method); }
/// <summary> /// /// </summary> /// <param name="il"></param> /// <param name="ctx"></param> /// <param name="tags"></param> /// <returns></returns> internal static void BlockCompile(this CompileContext ctx, ILGenerator il, ITag[] tags) { var stringBuilderType = typeof(StringBuilder); il.DeclareLocal(stringBuilderType); il.DeclareLocal(typeof(string)); il.Emit(OpCodes.Newobj, stringBuilderType.GetConstructor(Type.EmptyTypes)); il.Emit(OpCodes.Stloc_0); for (var i = 0; i < tags.Length; i++) { il.CallTag(ctx, tags[i], (nil, hasReturn, needCall) => { if (hasReturn) { nil.Emit(OpCodes.Ldloc_0); } if (needCall) { nil.Emit(OpCodes.Ldarg_0); nil.Emit(OpCodes.Ldarg_1); } }, (nil, returnType) => { if (returnType == null) { return; } nil.StringAppend(ctx, returnType); nil.Emit(OpCodes.Pop); }); } il.Emit(OpCodes.Ldloc_0); il.Call(stringBuilderType, stringBuilderType.GetMethodInfo("ToString", Type.EmptyTypes)); il.Emit(OpCodes.Stloc_1); il.Emit(OpCodes.Ldloc_1); }
/// <summary> /// Compiles the specified tag into a method. /// </summary> /// <param name="tag">The <see cref="ITag"/>.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static MethodInfo CompileTag(this CompileContext ctx, ITag tag) { return(TagCompiler.Compile(tag, ctx)); }
private static void CompileToRender(ITag tag, CompileContext context, Type tagType, MethodInfo method) { var type = method.ReturnType; var isAsync = false; var taskType = typeof(System.Threading.Tasks.Task); MethodInfo taskAwaiterMethod = null; MethodInfo resultMethod = null; var mb = context.CreateRenderMethod(tagType.Name); var il = mb.GetILGenerator(); var exBlock = il.BeginExceptionBlock(); var lableThrow = il.DefineLabel(); var labelPass = il.DefineLabel(); var index = 0; if (DynamicHelpers.IsMatchType(type, taskType)) { isAsync = true; taskAwaiterMethod = DynamicHelpers.GetMethod(method.ReturnType, "GetAwaiter", Type.EmptyTypes); resultMethod = DynamicHelpers.GetMethod(taskAwaiterMethod.ReturnType, "GetResult", Type.EmptyTypes); type = resultMethod.ReturnType; } if (type.FullName != "System.Void") { il.DeclareLocal(type); index = 1; il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, method); if (isAsync) { il.DeclareLocal(taskAwaiterMethod.ReturnType); il.Emit(OpCodes.Callvirt, taskAwaiterMethod); il.Emit(OpCodes.Stloc, index); il.Emit(OpCodes.Ldloca, index); il.Emit(OpCodes.Call, resultMethod); index++; } il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldarg_1); MethodInfo writeMethod; switch (type.FullName) { case "System.Object": case "System.String": case "System.Decimal": case "System.Single": case "System.UInt64": case "System.Int64": case "System.UInt32": case "System.Int32": case "System.Boolean": case "System.Double": case "System.Char": il.Emit(OpCodes.Ldloc_0); writeMethod = typeof(TextWriter).GetMethod("Write", new Type[] { type }); break; default: if (type.IsValueType) { il.Emit(OpCodes.Ldloca_S, 0); il.Call(type, typeof(object).GetMethod("ToString", Type.EmptyTypes)); writeMethod = typeof(TextWriter).GetMethod("Write", new Type[] { typeof(string) }); } else { il.Emit(OpCodes.Ldloc_0); writeMethod = typeof(TextWriter).GetMethod("Write", new Type[] { typeof(object) }); } break; } il.Emit(OpCodes.Callvirt, writeMethod); } else { if (isAsync) { il.DeclareLocal(taskAwaiterMethod.ReturnType); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, method); il.Emit(OpCodes.Callvirt, taskAwaiterMethod); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloca, 0); il.Emit(OpCodes.Call, resultMethod); index = 1; } else { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, method); } } il.DeclareLocal(typeof(System.Exception)); il.DeclareLocal(typeof(bool)); il.BeginCatchBlock(typeof(System.Exception)); il.Emit(OpCodes.Stloc, index); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Callvirt, DynamicHelpers.GetPropertyGetMethod(typeof(Context), "ThrowExceptions")); il.Emit(OpCodes.Stloc, index + 1); il.Emit(OpCodes.Ldloc, index + 1); il.Emit(OpCodes.Brfalse, lableThrow); il.Emit(OpCodes.Ldloc, index); il.Emit(OpCodes.Throw); il.MarkLabel(lableThrow); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldloc, index); il.Emit(OpCodes.Callvirt, DynamicHelpers.GetMethod(typeof(TemplateContext), "AddError", new Type[] { typeof(System.Exception) })); //il.Emit(OpCodes.Leave_S, labelPass); il.EndExceptionBlock(); il.MarkLabel(labelPass); il.Emit(OpCodes.Ret); context.Generator.Emit(OpCodes.Ldarg_0); context.Generator.Emit(OpCodes.Ldarg_1); context.Generator.Emit(OpCodes.Ldarg_2); context.Generator.Emit(OpCodes.Call, mb.GetBaseDefinition()); }
/// <summary> /// Gets the <see cref="Type"/> with the specified tag. /// </summary> /// <param name="tag">The tag of the type to get.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public Type GetType(ITag tag, CompileContext ctx) { return(GetType(tag.GetType().Name, tag, ctx)); }
/// <summary> /// Compiles the specified tag into a method. /// </summary> /// <param name="tag">The <see cref="ITag"/>.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static MethodInfo Compile(ITag tag, CompileContext ctx) { return(Compile(tag.GetType().Name, tag, ctx)); }
/// <summary> /// Gets the <see cref="Type"/> with the specified tag. /// </summary> /// <param name="tag">The tag of the type to get.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static Type GuessType(this CompileContext ctx, ITag tag) { return(ctx.TypeGuesser.GetType(tag, ctx)); }
/// <summary> /// Compiles the specified tag into a method. /// </summary> /// <param name="tag">The <see cref="ITag"/>.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static MethodInfo CompileTag(this CompileContext ctx, ITag tag) { return(Compile(ctx, tag)); }
/// <summary> /// Compile the text into a dynamic class. /// </summary> /// <param name="content">the context of the text</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static ICompilerResult Compile(this CompileContext ctx, string content) { var tags = TemplateContextExtensions.Lexer(ctx, content); return(Compile(ctx, tags)); }
private static void CompileToRender(this CompileContext context, ITag tag, Type tagType, MethodInfo method) { var type = method.ReturnType; var isAsync = false; var taskType = typeof(System.Threading.Tasks.Task); MethodInfo taskAwaiterMethod = null; MethodInfo resultMethod = null; var mb = context.CreateRenderMethod(tagType.Name); var il = mb.GetILGenerator(); var exBlock = il.BeginExceptionBlock(); //var lableThrow = il.DefineLabel(); var labelPass = il.DefineLabel(); if (type.IsMatchType(taskType)) { isAsync = true; taskAwaiterMethod = method.ReturnType.GetMethodInfo("GetAwaiter", Type.EmptyTypes); resultMethod = taskAwaiterMethod.ReturnType.GetMethodInfo("GetResult", Type.EmptyTypes); type = resultMethod.ReturnType; } if (type.FullName != "System.Void") { il.DeclareLocal(type); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, method); if (isAsync) { var returnVar = il.DeclareLocal(taskAwaiterMethod.ReturnType); il.Emit(OpCodes.Callvirt, taskAwaiterMethod); il.Emit(OpCodes.Stloc, returnVar.LocalIndex); il.Emit(OpCodes.Ldloca, returnVar.LocalIndex); il.Emit(OpCodes.Call, resultMethod); } il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldarg_1); MethodInfo writeMethod; switch (type.FullName) { case "System.Object": case "System.String": case "System.Decimal": case "System.Single": case "System.UInt64": case "System.Int64": case "System.UInt32": case "System.Int32": case "System.Boolean": case "System.Double": case "System.Char": il.Emit(OpCodes.Ldloc_0); writeMethod = typeof(TextWriter).GetMethod("Write", new Type[] { type }); break; default: if (type.IsValueType) { il.Emit(OpCodes.Ldloca_S, 0); il.Call(type, typeof(object).GetMethod("ToString", Type.EmptyTypes)); writeMethod = typeof(TextWriter).GetMethod("Write", new Type[] { typeof(string) }); } else { il.Emit(OpCodes.Ldloc_0); writeMethod = typeof(TextWriter).GetMethod("Write", new Type[] { typeof(object) }); } break; } il.Emit(OpCodes.Callvirt, writeMethod); } else { if (isAsync) { il.DeclareLocal(taskAwaiterMethod.ReturnType); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, method); il.Emit(OpCodes.Callvirt, taskAwaiterMethod); il.Emit(OpCodes.Stloc_0); il.Emit(OpCodes.Ldloca, 0); il.Emit(OpCodes.Call, resultMethod); } else { il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Call, method); } } var exceptionVar = il.DeclareLocal(typeof(System.Exception)); //il.DeclareLocal(typeof(bool)); il.BeginCatchBlock(typeof(System.Exception)); il.Emit(OpCodes.Stloc, exceptionVar.LocalIndex); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_2); il.Emit(OpCodes.Ldloc, exceptionVar.LocalIndex); il.Emit(OpCodes.Ldc_I4, tag.FirstToken?.BeginLine ?? 0); il.Emit(OpCodes.Ldc_I4, tag.FirstToken?.BeginColumn ?? 0); if (context.Debug) { il.Emit(OpCodes.Ldstr, tag.ToSource()); } else { il.Emit(OpCodes.Ldnull); } il.Emit(OpCodes.Call, typeof(CompilerResult).GetMethodInfo("ThrowException", null)); il.EndExceptionBlock(); il.MarkLabel(labelPass); il.Emit(OpCodes.Ret); context.Generator.Emit(OpCodes.Ldarg_0); context.Generator.Emit(OpCodes.Ldarg_1); context.Generator.Emit(OpCodes.Ldarg_2); context.Generator.Emit(OpCodes.Call, mb.GetBaseDefinition()); }
/// <summary> /// Adds a new render method to the type, with the specified name. /// </summary> /// <param name="name">The name of the method.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns>The <see cref="MethodBuilder"/>.</returns> public static MethodBuilder CreateRenderMethod(this CompileContext ctx, string name) { return(ctx.TypeBuilder.DefineMethod($"Render{name}{ctx.Seed}", MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.Standard | CallingConventions.HasThis, typeof(void), new Type[] { typeof(TextWriter), typeof(TemplateContext) })); }
/// <summary> /// Adds a new method to the type, with the specified name. /// </summary> /// <typeparam name="T">The type of the tag.</typeparam> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static MethodBuilder CreateRenderMethod <T>(this CompileContext ctx) { return(CreateRenderMethod(ctx, typeof(T).Name)); }
/// <summary> /// Compiles the specified tag into a method. /// </summary> /// <param name="tag">The <see cref="ITag"/>.</param> /// <param name="ctx">The <see cref="CompileContext"/>.</param> /// <returns></returns> public static MethodInfo Compile(this CompileContext ctx, ITag tag) { return(Compile(ctx, tag.GetType().Name, tag)); }