void LightEmit(RHC rhc, ObjExpr objx, CljILGen ilg) { //emitting a Fn means constructing an instance, feeding closed-overs from enclosing scope, if any //objx arg is enclosing objx, not this // Create the function instance LocalBuilder fnLocal = ilg.DeclareLocal(CompiledType); if (CompiledType == typeof(RestFnImpl)) { ilg.EmitInt(_variadicMethod.RequiredArity); ilg.EmitNew(Compiler.Ctor_RestFnImpl_1); } else { ilg.EmitNew(Compiler.Ctor_AFnImpl); } ilg.Emit(OpCodes.Stloc, fnLocal); //ilg.EmitString(String.Format("Creating fn {0}", Name)); //ilg.Emit(OpCodes.Call, typeof(System.Console).GetMethod("WriteLine", new Type[] { typeof(string) })); // Set up the methods for (ISeq s = RT.seq(_methods); s != null; s = s.next()) { FnMethod method = (FnMethod)s.first(); int key = GetMethodKey(method); string fieldName = IsVariadic && method.IsVariadic ? "_fnDo" + (key - 1) // because key is arity+1 for variadic : "_fn" + key; FieldInfo fi = CompiledType.GetField(fieldName); ilg.Emit(OpCodes.Ldloc, fnLocal); EmitGetDynMethod(key, ilg); ilg.EmitType(fi.FieldType); ilg.Emit(OpCodes.Ldloc, fnLocal); ilg.Emit(OpCodes.Callvirt, Method_DynamicMethod_CreateDelegate); ilg.Emit(OpCodes.Castclass, fi.FieldType); ilg.EmitFieldSet(fi); } // setup the constants and locals ilg.Emit(OpCodes.Ldloc, fnLocal); if (Constants.count() > 0) { EmitGetCompiledConstants(ilg); } else { ilg.EmitInt(0); ilg.EmitArray(typeof(Object[])); } if (Closes.count() > 0) { int maxIndex = Closes.Max(c => ((LocalBinding)c.key()).Index); ilg.EmitInt(maxIndex + 1); ilg.Emit(OpCodes.Newarr, typeof(object)); for (ISeq s = RT.keys(Closes); s != null; s = s.next()) { LocalBinding lb = (LocalBinding)s.first(); ilg.Emit(OpCodes.Dup); ilg.EmitInt(lb.Index); objx.EmitLocal(ilg, lb); ilg.EmitStoreElement(typeof(object)); } } else { ilg.EmitInt(0); ilg.EmitArray(typeof(Object[])); } // Create the closure ilg.EmitNew(Compiler.Ctor_Closure_2); // Assign the clojure ilg.EmitCall(Compiler.Method_IFnClosure_SetClosure); // Leave the instance on the stack. ilg.Emit(OpCodes.Ldloc, fnLocal); }
/// <summary> /// Constructor. Compiles a rules assembly from the given source files. /// </summary> /// <param name="Scope">The scope of items created by this assembly</param> /// <param name="BaseDirs">The base directories for this assembly</param> /// <param name="Plugins">All the plugins included in this assembly</param> /// <param name="ModuleFileToContext">List of module files to compile</param> /// <param name="TargetFiles">List of target files to compile</param> /// <param name="AssemblyFileName">The output path for the compiled assembly</param> /// <param name="bContainsEngineModules">Whether this assembly contains engine modules. Used to initialize the default value for ModuleRules.bTreatAsEngineModule.</param> /// <param name="DefaultBuildSettings">Optional override for the default build settings version for modules created from this assembly.</param> /// <param name="bReadOnly">Whether the modules and targets in this assembly are installed, and should be created with the bUsePrecompiled flag set</param> /// <param name="bSkipCompile">Whether to skip compiling this assembly</param> /// <param name="Parent">The parent rules assembly</param> internal RulesAssembly(RulesScope Scope, List <DirectoryReference> BaseDirs, IReadOnlyList <PluginInfo> Plugins, Dictionary <FileReference, ModuleRulesContext> ModuleFileToContext, List <FileReference> TargetFiles, FileReference AssemblyFileName, bool bContainsEngineModules, BuildSettingsVersion?DefaultBuildSettings, bool bReadOnly, bool bSkipCompile, RulesAssembly Parent) { this.Scope = Scope; this.BaseDirs = BaseDirs; this.Plugins = Plugins; this.ModuleFileToContext = ModuleFileToContext; this.bContainsEngineModules = bContainsEngineModules; this.DefaultBuildSettings = DefaultBuildSettings; this.bReadOnly = bReadOnly; this.Parent = Parent; // Find all the source files HashSet <FileReference> AssemblySourceFiles = new HashSet <FileReference>(); AssemblySourceFiles.UnionWith(ModuleFileToContext.Keys); AssemblySourceFiles.UnionWith(TargetFiles); // Compile the assembly if (AssemblySourceFiles.Count > 0) { List <string> PreprocessorDefines = GetPreprocessorDefinitions(); CompiledAssembly = DynamicCompilation.CompileAndLoadAssembly(AssemblyFileName, AssemblySourceFiles, PreprocessorDefines: PreprocessorDefines, DoNotCompile: bSkipCompile); } // Setup the module map foreach (FileReference ModuleFile in ModuleFileToContext.Keys) { string ModuleName = ModuleFile.GetFileNameWithoutAnyExtensions(); if (!ModuleNameToModuleFile.ContainsKey(ModuleName)) { ModuleNameToModuleFile.Add(ModuleName, ModuleFile); } } // Setup the target map foreach (FileReference TargetFile in TargetFiles) { string TargetName = TargetFile.GetFileNameWithoutAnyExtensions(); if (!TargetNameToTargetFile.ContainsKey(TargetName)) { TargetNameToTargetFile.Add(TargetName, TargetFile); } } // Write any deprecation warnings for methods overriden from a base with the [ObsoleteOverride] attribute. Unlike the [Obsolete] attribute, this ensures the message // is given because the method is implemented, not because it's called. if (CompiledAssembly != null) { foreach (Type CompiledType in CompiledAssembly.GetTypes()) { foreach (MethodInfo Method in CompiledType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) { ObsoleteOverrideAttribute Attribute = Method.GetCustomAttribute <ObsoleteOverrideAttribute>(true); if (Attribute != null) { FileReference Location; if (!TryGetFileNameFromType(CompiledType, out Location)) { Location = new FileReference(CompiledAssembly.Location); } Log.TraceWarning("{0}: warning: {1}", Location, Attribute.Message); } } if (CompiledType.BaseType == typeof(ModuleRules)) { ConstructorInfo Constructor = CompiledType.GetConstructor(new Type[] { typeof(TargetInfo) }); if (Constructor != null) { FileReference Location; if (!TryGetFileNameFromType(CompiledType, out Location)) { Location = new FileReference(CompiledAssembly.Location); } Log.TraceWarning("{0}: warning: Module constructors should take a ReadOnlyTargetRules argument (rather than a TargetInfo argument) and pass it to the base class constructor from 4.15 onwards. Please update the method signature.", Location); } } } } }
/// <summary> /// Constructor. Compiles a rules assembly from the given source files. /// </summary> /// <param name="Plugins">All the plugins included in this assembly</param> /// <param name="ModuleFiles">List of module files to compile</param> /// <param name="TargetFiles">List of target files to compile</param> /// <param name="ModuleFileToPluginInfo">Mapping of module file to the plugin that contains it</param> /// <param name="AssemblyFileName">The output path for the compiled assembly</param> /// <param name="Parent">The parent rules assembly</param> public RulesAssembly(IReadOnlyList <PluginInfo> Plugins, List <FileReference> ModuleFiles, List <FileReference> TargetFiles, Dictionary <FileReference, PluginInfo> ModuleFileToPluginInfo, FileReference AssemblyFileName, RulesAssembly Parent) { this.Plugins = Plugins; this.ModuleFileToPluginInfo = ModuleFileToPluginInfo; this.Parent = Parent; // Find all the source files List <FileReference> AssemblySourceFiles = new List <FileReference>(); AssemblySourceFiles.AddRange(ModuleFiles); AssemblySourceFiles.AddRange(TargetFiles); // Compile the assembly if (AssemblySourceFiles.Count > 0) { List <string> PreprocessorDefines = new List <string>(); PreprocessorDefines.Add("WITH_FORWARDED_MODULE_RULES_CTOR"); PreprocessorDefines.Add("WITH_FORWARDED_TARGET_RULES_CTOR"); // Define macros for the UE4 version, starting with 4.17 BuildVersion Version; if (BuildVersion.TryRead(out Version)) { for (int MinorVersion = 17; MinorVersion <= Version.MinorVersion; MinorVersion++) { PreprocessorDefines.Add(String.Format("UE_4_{0}_OR_LATER", MinorVersion)); } } CompiledAssembly = DynamicCompilation.CompileAndLoadAssembly(AssemblyFileName, AssemblySourceFiles, PreprocessorDefines: PreprocessorDefines); } // Setup the module map foreach (FileReference ModuleFile in ModuleFiles) { string ModuleName = ModuleFile.GetFileNameWithoutAnyExtensions(); if (!ModuleNameToModuleFile.ContainsKey(ModuleName)) { ModuleNameToModuleFile.Add(ModuleName, ModuleFile); } } // Setup the target map foreach (FileReference TargetFile in TargetFiles) { string TargetName = TargetFile.GetFileNameWithoutAnyExtensions(); if (!TargetNameToTargetFile.ContainsKey(TargetName)) { TargetNameToTargetFile.Add(TargetName, TargetFile); } } // Write any deprecation warnings for methods overriden from a base with the [ObsoleteOverride] attribute. Unlike the [Obsolete] attribute, this ensures the message // is given because the method is implemented, not because it's called. if (CompiledAssembly != null) { foreach (Type CompiledType in CompiledAssembly.GetTypes()) { foreach (MethodInfo Method in CompiledType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)) { ObsoleteOverrideAttribute Attribute = Method.GetCustomAttribute <ObsoleteOverrideAttribute>(true); if (Attribute != null) { FileReference Location; if (!TryGetFileNameFromType(CompiledType, out Location)) { Location = new FileReference(CompiledAssembly.Location); } Log.TraceWarning("{0}: warning: {1}", Location, Attribute.Message); } } if (CompiledType.BaseType == typeof(ModuleRules)) { ConstructorInfo Constructor = CompiledType.GetConstructor(new Type[] { typeof(TargetInfo) }); if (Constructor != null) { FileReference Location; if (!TryGetFileNameFromType(CompiledType, out Location)) { Location = new FileReference(CompiledAssembly.Location); } Log.TraceWarning("{0}: warning: Module constructors should take a ReadOnlyTargetRules argument (rather than a TargetInfo argument) and pass it to the base class constructor from 4.15 onwards. Please update the method signature.", Location); } } } } }