/// <summary>Adapts the var tag base for compiling.</summary> /// <param name="ccse">The compiled CSE.</param> /// <param name="tab">The TagArgumentBit.</param> /// <param name="i">The command index.</param> /// <param name="values">Related adaptation values.</param> public override TagReturnType Adapt(CompiledCommandStackEntry ccse, TagArgumentBit tab, int i, CILAdaptationValues values) { string vn = tab.Bits[0].Variable.ToString().ToLowerFast(); CommandEntry entry = ccse.Entries[i]; if (!entry.VarLookup.TryGetValue(vn, out SingleCILVariable locVar)) { throw new ErrorInducedException("Var tag cannot compile: unknown variable name input '" + vn + "' (That variable name cannot be found. Have you declared it in this script section? Consider using the 'require' command.)"); } tab.Start = tab.TagSystem.LVar; tab.Bits[0].Key = "\0lvar"; tab.Bits[0].Handler = null; tab.Bits[0].OriginalInput = "[" + tab.Bits[0].Variable.ToString() + "]"; tab.Bits[0].Variable = new Argument() { WasQuoted = false, Bits = new ArgumentBit[] { new TextArgumentBit(locVar.Index, tab.Engine) } }; return(locVar.Type); }
/// <summary>Adapts the template tab base for compiling.</summary> /// <param name="ccse">The compiled CSE.</param> /// <param name="tab">The TagArgumentBit.</param> /// <param name="i">The command index.</param> /// <param name="values">Related adaptation values.</param> public virtual TagReturnType Adapt(CompiledCommandStackEntry ccse, TagArgumentBit tab, int i, CILAdaptationValues values) { return(default);
/// <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); }
/// <summary>Adapts the var tag base for compiling.</summary> /// <param name="ccse">The compiled CSE.</param> /// <param name="tab">The TagArgumentBit.</param> /// <param name="i">The command index.</param> /// <param name="values">Related adaptation values.</param> public override TagReturnType Adapt(CompiledCommandStackEntry ccse, TagArgumentBit tab, int i, CILAdaptationValues values) { int index = (int)((tab.Bits[0].Variable.Bits[0] as TextArgumentBit).InputValue as IntegerTag).Internal; return(values.LocalVariableType(index)); }