/// <summary>Configures the <see cref="ILGeneratorTracker"/> for FS output handling.</summary> public static ILGeneratorTracker ConfigureTracker(this ILGeneratorTracker tracker, ScriptEngine engine) { tracker.BaseCode = TextStyle.Base; tracker.EmphasizeCode = TextStyle.Separate; tracker.MinorCode = TextStyle.Minor; tracker.Error = engine.Context.BadOutput; return(tracker); }
/// <summary>Adapts the tag base to CIL.</summary> /// <param name="ilgen">IL Generator.</param> /// <param name="tab">The TagArgumentBit.</param> /// <param name="values">Related adaptation values.</param> /// <returns>Whether any adaptation was done.</returns> public override bool AdaptToCIL(ILGeneratorTracker ilgen, TagArgumentBit tab, CILAdaptationValues values) { int index = (int)((tab.Bits[0].Variable.Bits[0] as TextArgumentBit).InputValue as IntegerTag).Internal; ilgen.Emit(OpCodes.Ldarg_0); // Load argument: TagData ilgen.Emit(OpCodes.Ldfld, TagData.Field_TagData_Runnable); // Load TagData.Runnable ilgen.Emit(OpCodes.Ldfld, values.LocalVariableData(index).Field); // Load Runnable.Var return(true); }
public static TagReturnType Compiler_Tag_As(ILGeneratorTracker ilgen, TagArgumentBit tab, int bit, TagReturnType prevType) { ilgen.Emit(OpCodes.Ldfld, Field_DynamicTag_Internal); // Load field "Internal" on the input DynamicTag instance. ilgen.Emit(OpCodes.Ldarg_0); // Load argument: TagData. string type_name = tab.Bits[bit].Variable.ToString(); TagType varType = tab.Engine.TagSystem.Types.RegisteredTypes[type_name.ToLowerFast()]; ilgen.Emit(OpCodes.Call, varType.CreatorMethod); // Run the creator method for the type on the input tag. return(new TagReturnType(varType, false)); }
/// <summary>Generates a compiled call to the TagArgumentBit.</summary> /// <param name="ilgen">The IL Generator.</param> /// <param name="tab_loc">The TagArgumentBit helper local-variable location.</param> /// <param name="load_Error">The OpCode to load the error object.</param> /// <param name="load_Runnable">The OpCode to load the runnable object.</param> /// <param name="obj_loc">The TemplateObject helper local-variable location.</param> /// <param name="return_raw">Whether a raw value should be returned.</param> public void GenerateCall(ILGeneratorTracker ilgen, int tab_loc, OpCode load_Error, OpCode load_Runnable, int obj_loc, bool return_raw) { ilgen.Emit(OpCodes.Stloc, tab_loc); // Store the TAB to the proper location Label exceptionLabel = default; if (Data.HasFallback) { exceptionLabel = ilgen.BeginExceptionBlock(); // try { } ilgen.Emit(OpCodes.Ldloc, tab_loc); // Load the tag onto stack ilgen.Emit(load_Error); // Load the error object onto stack ilgen.Emit(load_Runnable); // Load the runnable object onto stack. ilgen.Emit(OpCodes.Call, TagArgumentBit_PrepParse); // Call the PrepParse method (pulls TagArgumentBit + Error + CSE from stack) ilgen.Emit(OpCodes.Ldloc, tab_loc); // Load the tag onto stack ilgen.Emit(OpCodes.Ldfld, TagArgumentBit_Data); // Read 'data' (from current tab) ilgen.Emit(load_Runnable); // Load the runnable object onto stack. ilgen.Emit(load_Error); // Load the error object onto stack. ilgen.Emit(OpCodes.Call, GetResultMethod, 3); // Call the GetResultMethod (takes three params: (TagData, CompiledCommandRunnable, Action<string>), and returns a TemplateObject). if (CompiledReturnType.IsRaw && !return_raw) { ilgen.Emit(OpCodes.Newobj, CompiledReturnType.Type.RawInternalConstructor); // Handle raw translation if needed. } ilgen.Emit(OpCodes.Stloc, obj_loc); // Store the TemplateObject where it belongs if (Data.HasFallback) { ilgen.Emit(OpCodes.Leave, exceptionLabel); // } ilgen.BeginCatchBlock(typeof(TagErrorInducedException)); // catch (Exception ex) { ilgen.Emit(OpCodes.Pop); // pop the exception off stack ilgen.Emit(OpCodes.Ldloc, tab_loc); // Load the tag onto stack ilgen.Emit(OpCodes.Ldfld, TagArgumentBit_Data); // Read 'data' (from current tab) ilgen.Emit(OpCodes.Ldfld, TagData.Field_Fallback); // Read 'data'.Fallback field ilgen.Emit(load_Error); // Load the error object onto stack ilgen.Emit(load_Runnable); // Load the runnable object onto stack. ilgen.Emit(OpCodes.Callvirt, ArgumentCompiler.Argument_Parse); // Virtual call the Argument.Parse method, which returns a TemplateObject ilgen.Emit(OpCodes.Ldloc, tab_loc); // Load the tag onto stack ilgen.Emit(OpCodes.Ldfld, TagArgumentBit_Data); // Read 'data' (from current tab) ilgen.Emit(OpCodes.Call, CompiledReturnType.Type.CreatorMethod); // Validate type if (CompiledReturnType.IsRaw && return_raw) { ilgen.Emit(OpCodes.Ldfld, CompiledReturnType.Type.RawInternalField); // Handle raw translation if needed. } ilgen.Emit(OpCodes.Stloc, obj_loc); // Store the TemplateObject where it belongs ilgen.EndExceptionBlock(); // } } }
/// <summary>Creates a variable setter for the given variable.</summary> /// <param name="variable">The variable.</param> /// <returns>The setter action.</returns> public static Action <CompiledCommandRunnable, TemplateObject> CreateVariableSetter(SingleCILVariable variable) { DynamicMethod genMethod = new("script_" + variable.Field.DeclaringType.Name + "_var_" + variable.Index + "_setter", typeof(void), SETTER_ACTION_PARAMS); ILGeneratorTracker ILGen = new ILGeneratorTracker(genMethod.GetILGenerator(), SETTER_ACTION_PARAMS, genMethod.Name).ConfigureTracker(variable.Type.Type.Engine); ILGen.Emit(OpCodes.Ldarg_0); // Load argument: runnable ILGen.Emit(OpCodes.Castclass, variable.Field.DeclaringType); // Jank patch for type misinterpretation in CILL validator ILGen.Emit(OpCodes.Ldarg_1); // Load argument: input variable value ILGen.Emit(OpCodes.Ldsfld, TagData.FIELD_TAGDATA_SIMPLE_ERROR); // Grab a blank TagData ILGen.Emit(OpCodes.Call, variable.Type.Type.CreatorMethod); // Ensure type if (variable.Type.IsRaw) { ILGen.Emit(OpCodes.Ldfld, variable.Type.Type.RawInternalField); // Handle raw if needed. } ILGen.Emit(OpCodes.Stfld, variable.Field); // Store to field. ILGen.Emit(OpCodes.Ret); // Return. return(genMethod.CreateDelegate(typeof(Action <CompiledCommandRunnable, TemplateObject>)) as Action <CompiledCommandRunnable, TemplateObject>); }
/// <summary>Adapts the tag base to CIL.</summary> /// <param name="ilgen">IL Generator.</param> /// <param name="tab">The TagArgumentBit.</param> /// <param name="values">Related adaptation values.</param> /// <returns>Whether any adaptation was done.</returns> public virtual bool AdaptToCIL(ILGeneratorTracker ilgen, TagArgumentBit tab, CILAdaptationValues values) { return(false); }
/// <summary>Compiles a command script.</summary> /// <param name="script">The command script to compile.</param> /// <returns>The compiled result.</returns> public static CompiledCommandStackEntry Compile(CommandScript script) { string tname = "__script__" + IDINCR++ + "__" + NameTrimMatcher.TrimToMatches(script.Name); CompiledCommandStackEntry Created = new() { Entries = script.CommandArray, Script = script, AssemblyName = tname }; AssemblyName asmname = new(tname) { Name = tname }; AssemblyBuilder asmbuild = AssemblyBuilder.DefineDynamicAssembly(asmname, AssemblyBuilderAccess.RunAndCollect); ModuleBuilder modbuild = asmbuild.DefineDynamicModule(tname); CompiledCommandStackEntry ccse = Created; ccse.AdaptedILPoints = new Label[ccse.Entries.Length + 1]; TypeBuilder typebuild_c = modbuild.DefineType(tname + "__CENTRAL", TypeAttributes.Class | TypeAttributes.Public, typeof(CompiledCommandRunnable)); MethodBuilder methodbuild_c = typebuild_c.DefineMethod("Run", MethodAttributes.Public | MethodAttributes.Virtual, typeof(void), RUN_METHOD_PARAMETERS); ILGeneratorTracker ilgen = new ILGeneratorTracker(methodbuild_c.GetILGenerator(), new Type[] { typebuild_c }.JoinWith(RUN_METHOD_PARAMETERS), $"Script_{tname}").ConfigureTracker(Created.System); ilgen.AddCode(OpCodes.Nop, tname, "--- SCRIPT ---"); CILAdaptationValues values = new() { Entry = ccse, Script = script, ILGen = ilgen, Method = methodbuild_c, DBMode = script.Debug, EntryFields = new FieldInfo[ccse.Entries.Length], ArgumentFields = new FieldInfo[ccse.Entries.Length][], Type = typebuild_c }; for (int i = 0; i < ccse.Entries.Length; i++) { values.EntryFields[i] = typebuild_c.DefineField("_field_entry_" + i, typeof(CommandEntry), FieldAttributes.Public | FieldAttributes.InitOnly); } values.PushVarSet(); for (int i = 0; i < ccse.AdaptedILPoints.Length; i++) { ccse.AdaptedILPoints[i] = ilgen.DefineLabel(); } int tagID = 0; List <TagArgumentBit> toClean = new(); List <ILGeneratorTracker> ILGens #if SAVE = new(); #else = null; #endif values.Trackers = ILGens; List <KeyValuePair <FieldInfo, Object> > specialFieldValues = new(); for (int i = 0; i < ccse.Entries.Length; i++) { CommandEntry curEnt = ccse.Entries[i]; curEnt.CCSE = ccse; curEnt.DBMode = values.DBMode; curEnt.VarLookup = values.CreateVarLookup(); for (int a = 0; a < curEnt.Arguments.Length; a++) { Argument arg = curEnt.Arguments[a]; for (int b = 0; b < arg.Bits.Length; b++) { if (arg.Bits[b] is TagArgumentBit tab) { tagID++; try { GenerateTagData(typebuild_c, ccse, tab, ref tagID, values, i, toClean, (a + 1).ToString(), curEnt, specialFieldValues, ILGens); } catch (TagErrorInducedException ex) { TagException(curEnt, "argument " + TextStyle.Separate + a + TextStyle.Base, tab, ex.SubTagIndex, ex); } catch (ErrorInducedException ex) { TagException(curEnt, "argument " + TextStyle.Separate + a + TextStyle.Base, tab, 0, ex); } } } ILGeneratorTracker compiled = ArgumentCompiler.Compile(arg, Created, values); curEnt.Arguments[a] = arg.TrueForm; ILGens?.Add(compiled); } foreach (KeyValuePair <string, Argument> argPair in curEnt.NamedArguments) { for (int b = 0; b < argPair.Value.Bits.Length; b++) { if (argPair.Value.Bits[b] is TagArgumentBit tab) { tagID++; try { GenerateTagData(typebuild_c, ccse, tab, ref tagID, values, i, toClean, "named " + argPair.Key, curEnt, specialFieldValues, ILGens); } catch (TagErrorInducedException ex) { TagException(curEnt, "named argument '" + TextStyle.Separate + argPair.Key + TextStyle.Base + "'", tab, ex.SubTagIndex, ex); } catch (ErrorInducedException ex) { TagException(curEnt, "named argument '" + TextStyle.Separate + argPair.Key + TextStyle.Base + "'", tab, 0, ex); } } } ILGeneratorTracker compiled = ArgumentCompiler.Compile(argPair.Value, Created, values); ILGens?.Add(compiled); } if (!curEnt.IsCallback) { try { curEnt.Command.PreAdaptToCIL(values, i); } catch (ErrorInducedException ex) { throw new ErrorInducedException("On script line " + curEnt.ScriptLine + " (" + curEnt.CommandLine + "), early compile (PreAdapt) error occured: " + ex.Message); } curEnt.VarLookup = values.CreateVarLookup(); } if (curEnt.NamedArguments.TryGetValue(CommandEntry.SAVE_NAME_ARG_ID, out Argument avarname)) { if (!curEnt.VarLookup.ContainsKey(avarname.ToString())) { throw new ErrorInducedException("Error in command line " + curEnt.ScriptLine + ": (" + curEnt.CommandLine + "): Invalid variable save name: " + avarname.ToString()); } } if (curEnt.IsCallback) { try { curEnt.Command.PreAdaptToCIL(values, i); } catch (ErrorInducedException ex) { throw new ErrorInducedException("On script line " + curEnt.ScriptLine + " (" + curEnt.CommandLine + "), early compile (PreAdapt) error occured: " + ex.Message); } } curEnt.DBMode = values.DBMode; } values.LoadRunnable(); ilgen.Emit(OpCodes.Ldfld, CompiledCommandRunnable.IndexField); ilgen.Emit(OpCodes.Switch, ccse.AdaptedILPoints); for (int i = 0; i < ccse.Entries.Length; i++) { ilgen.Comment($"Begin command code section {i}: {ccse.Entries[i].CommandLine}"); ilgen.MarkLabel(ccse.AdaptedILPoints[i]); try { ccse.Entries[i].Command.AdaptToCIL(values, i); } catch (ErrorInducedException ex) { throw new ErrorInducedException("On script line " + ccse.Entries[i].ScriptLine + " (" + ccse.Entries[i].CommandLine + "), compile error (Adapt) occured: " + ex.Message); } } ilgen.MarkLabel(ccse.AdaptedILPoints[^ 1]); values.MarkCommand(ccse.Entries.Length); ilgen.Emit(OpCodes.Ret); typebuild_c.DefineMethodOverride(methodbuild_c, CompiledCommandRunnable.RunMethod); ConstructorBuilder ctor = typebuild_c.DefineConstructor(MethodAttributes.Public, CallingConventions.HasThis, CONSTRUCTOR_PARAMS); ILGeneratorTracker ctorilgen = new ILGeneratorTracker(ctor.GetILGenerator(), new Type[] { typebuild_c }.JoinWith(CONSTRUCTOR_PARAMS), tname).ConfigureTracker(ccse.System); ILGens?.Add(ctorilgen); ctorilgen.Emit(OpCodes.Ldarg_0); // Load 'this' ctorilgen.Emit(OpCodes.Ldarg_1); // Load: CCSE ctorilgen.Emit(OpCodes.Stfld, CompiledCommandRunnable.EntryField); // Store it to the readonly field. for (int i = 0; i < values.EntryFields.Length; i++) { ctorilgen.Emit(OpCodes.Ldarg_0); // Load 'this' ctorilgen.Emit(OpCodes.Ldarg_2); // Load input array ctorilgen.Emit(OpCodes.Ldc_I4, i); // Load index in the array ctorilgen.Emit(OpCodes.Ldelem_Ref); // Load the value from the array ctorilgen.Emit(OpCodes.Stfld, values.EntryFields[i]); // Store it to the readonly field. FieldInfo[] argFields = values.ArgumentFields[i]; if (argFields != null) { for (int arg = 0; arg < argFields.Length; arg++) { if (argFields[arg] != null) { ctorilgen.Emit(OpCodes.Ldarg_0); // Load 'this' ctorilgen.Emit(OpCodes.Ldarg_0); // Load 'this' ctorilgen.Emit(OpCodes.Ldfld, values.EntryFields[i]); // Load the entry field ctorilgen.Emit(OpCodes.Ldfld, CILAdaptationValues.Entry_ArgumentsField); // Load the arguments field ctorilgen.Emit(OpCodes.Ldc_I4, arg); // Load the argument index ctorilgen.Emit(OpCodes.Ldelem_Ref); // Load the argument value from the array ctorilgen.Emit(OpCodes.Stfld, argFields[arg]); // Store it to the readonly field. } } } } for (int i = 0; i < specialFieldValues.Count; i++) { ctorilgen.Emit(OpCodes.Ldarg_0); // Load 'this' ctorilgen.Emit(OpCodes.Ldarg_3); // Load input array ctorilgen.Emit(OpCodes.Ldc_I4, i); // Load index in the array ctorilgen.Emit(OpCodes.Ldelem_Ref); // Load the value from the array ctorilgen.Emit(OpCodes.Castclass, specialFieldValues[i].Key.FieldType); // Guarantee the type. ctorilgen.Emit(OpCodes.Stfld, specialFieldValues[i].Key); // Store it to the field. } ctorilgen.Emit(OpCodes.Ret); // return Type t_c = typebuild_c.CreateType(); object[] fieldValues = new object[specialFieldValues.Count]; for (int i = 0; i < fieldValues.Length; i++) { fieldValues[i] = specialFieldValues[i].Value; } CompiledCommandRunnable runnable = Activator.CreateInstance(t_c, ccse, ccse.Entries, fieldValues) as CompiledCommandRunnable; ccse.ReferenceCompiledRunnable = runnable; ccse.Variables = values.Variables.ToArray(); foreach (SingleCILVariable variable in ccse.Variables) { variable.Field = variable.Field.DeclaringType.GetField(variable.Field.Name); // Patch runtime field issues } ccse.VariableSetters = new Action <CompiledCommandRunnable, TemplateObject> [ccse.Variables.Length]; runnable.EntryData = new AbstractCommandEntryData[Created.Entries.Length]; runnable.Debug = script.Debug; #if SAVE StringBuilder outp = new(); for (int i = 0; i < ilgen.Codes.Count; i++) { outp.Append(ilgen.Codes[i].Key + ": " + ilgen.Codes[i].Value?.ToString()?.Replace("\n", "\\n")?.Replace("\r", "\\r") + "\n"); } for (int n = 0; n < ILGens.Count; n++) { outp.Append("\n\n\n// -----\n\n\n"); for (int i = 0; i < ILGens[n].Codes.Count; i++) { outp.Append(ILGens[n].Codes[i].Key + ": " + ILGens[n].Codes[i].Value?.ToString()?.Replace("\n", "\\n")?.Replace("\r", "\\r") + "\n"); } } System.IO.File.WriteAllText("script_" + tname + ".il", outp.ToString()); #endif return(Created); }