/// <summary> /// Iterate over all loaded mods and emit new types /// </summary> void Awake() { // Generate overloaded PQSMod types AssemblyGen assembly = new AssemblyGen(Guid.NewGuid().ToString(), new CompilerOptions { OutputPath = Path.GetTempFileName() }); List <Type> modTypes = GetModTypes(); foreach (Type modType in modTypes) { if (typeof(PQSMod).IsAssignableFrom(modType)) { // Get the ModLoader type we want to extend Type loaderType = modTypes.FirstOrDefault(t => t.BaseType != null && t.BaseType.FullName != null && t.BaseType.FullName.StartsWith("Kopernicus.Configuration.ModLoader.ModLoader") && t.BaseType.GetGenericArguments()[0] == modType); if (loaderType == null) { continue; } // Generate the Mod Type TypeGen modGen = assembly.Public.Class($"{modType.Name}Regional", modType); { FieldGen multiplierMap = modGen.Public.Field(typeof(MapSO), "multiplierMap"); FieldGen splitChannels = modGen.Public.Field(typeof(Boolean), "splitChannels"); FieldGen multiplier = modGen.Private.Field(typeof(Color), "multiplier"); FieldGen preBuildColor = modGen.Private.Field(typeof(Color), "preBuildColor"); FieldGen preBuildHeight = modGen.Private.Field(typeof(Double), "preBuildHeight"); // OnVertexBuildHeight CodeGen onVertexBuild = modGen.Public.Override.Method(typeof(void), "OnVertexBuild") .Parameter(typeof(PQS.VertexBuildData), "data"); { ContextualOperand data = onVertexBuild.Arg("data"); onVertexBuild.Assign(multiplier, onVertexBuild.Local(multiplierMap.Invoke( "GetPixelColor", new TypeMapper(), data.Field("u"), data.Field("v")))); onVertexBuild.If(!splitChannels); { onVertexBuild.Assign(multiplier.Field("a", new TypeMapper()), multiplier.Field("r", new TypeMapper())); } onVertexBuild.End(); onVertexBuild.Assign(preBuildColor, data.Field("vertColor")); onVertexBuild.Assign(preBuildHeight, data.Field("vertHeight")); onVertexBuild.Invoke(onVertexBuild.Base(), "OnVertexBuild", data); onVertexBuild.Assign(data.Field("vertColor"), assembly.StaticFactory.Invoke(typeof(Color), "Lerp", preBuildColor, data.Field("vertColor"), multiplier.Field("a", new TypeMapper()))); onVertexBuild.Assign(data.Field("vertHeight"), assembly.StaticFactory.Invoke(typeof(UtilMath), "Lerp", preBuildHeight, data.Field("vertHeight"), multiplier.Field("r", new TypeMapper()))); } // OnVertexBuildHeight CodeGen onVertexBuildHeight = modGen.Public.Override.Method(typeof(void), "OnVertexBuildHeight") .Parameter(typeof(PQS.VertexBuildData), "data"); { ContextualOperand data = onVertexBuildHeight.Arg("data"); onVertexBuildHeight.Assign(multiplier, onVertexBuildHeight.Local(multiplierMap.Invoke( "GetPixelColor", new TypeMapper(), data.Field("u"), data.Field("v")))); onVertexBuildHeight.If(!splitChannels); { onVertexBuildHeight.Assign(multiplier.Field("a", new TypeMapper()), multiplier.Field("r", new TypeMapper())); } onVertexBuildHeight.End(); onVertexBuildHeight.Assign(preBuildColor, data.Field("vertColor")); onVertexBuildHeight.Assign(preBuildHeight, data.Field("vertHeight")); onVertexBuildHeight.Invoke(onVertexBuildHeight.Base(), "OnVertexBuildHeight", data); onVertexBuildHeight.Assign(data.Field("vertColor"), assembly.StaticFactory.Invoke(typeof(Color), "Lerp", preBuildColor, data.Field("vertColor"), multiplier.Field("a", new TypeMapper()))); onVertexBuildHeight.Assign(data.Field("vertHeight"), assembly.StaticFactory.Invoke(typeof(UtilMath), "Lerp", preBuildHeight, data.Field("vertHeight"), multiplier.Field("r", new TypeMapper()))); } } // Generate the Loader Type Type modLoader = typeof(ModLoader <>).MakeGenericType(modGen); TypeGen loaderGen = assembly.Public.Class($"{modType.Name.Replace("PQSMod_", "").Replace("PQS", "")}Regional", modLoader); { PropertyGen multiplierMap = loaderGen.Public.Property(typeof(MapSOParserRGB <MapSO>), "multiplierMap") .Attribute(typeof(ParserTarget), "multiplierMap"); { CodeGen getter = multiplierMap.Getter(); { getter.Return(getter.Base().Property("Mod").Field("multiplierMap")); } CodeGen setter = multiplierMap.Setter(); { setter.Assign(setter.Base().Property("Mod").Field("multiplierMap"), setter.PropertyValue()); } } PropertyGen splitChannels = loaderGen.Public.Property(typeof(NumericParser <Boolean>), "splitChannels") .Attribute(typeof(ParserTarget), "splitChannels"); { CodeGen getter = splitChannels.Getter(); { getter.Return(getter.Base().Property("Mod").Field("splitChannels")); } CodeGen setter = splitChannels.Setter(); { setter.Assign(setter.Base().Property("Mod").Field("splitChannels"), setter.PropertyValue()); } } FieldGen loader = loaderGen.Public.Field(loaderType, "loader") .BeginAttribute(typeof(ParserTarget), "Mod").SetField("AllowMerge", true).End(); CodeGen create_PQS = loaderGen.Public.Override.Method(typeof(void), "Create") .Parameter(typeof(PQS), "pqsVersion"); { ContextualOperand pqsVersion = create_PQS.Arg("pqsVersion"); create_PQS.Invoke(create_PQS.Base(), "Create", pqsVersion); create_PQS.Assign(loader, assembly.ExpressionFactory.New(loaderType)); create_PQS.Invoke(loader, "Create", create_PQS.Base().Property("Mod"), pqsVersion); } CodeGen create_Mod_PQS = loaderGen.Public.Override.Method(typeof(void), "Create") .Parameter(modGen, "_mod") .Parameter(typeof(PQS), "pqsVersion"); { ContextualOperand _mod = create_Mod_PQS.Arg("_mod"); ContextualOperand pqsVersion = create_Mod_PQS.Arg("pqsVersion"); create_Mod_PQS.Invoke(create_Mod_PQS.Base(), "Create", _mod, pqsVersion); create_Mod_PQS.Assign(loader, assembly.ExpressionFactory.New(loaderType)); create_Mod_PQS.Invoke(loader, "Create", create_Mod_PQS.Base().Property("Mod"), pqsVersion); } } } } assembly.Save(); // Hacking into my own mod. Oh well. modTypes.AddRange(assembly.GetAssembly().GetTypes()); typeof(Parser).GetField("_modTypes", BindingFlags.NonPublic | BindingFlags.Static) ?.SetValue(null, modTypes); }
public static void GenIndexer(AssemblyGen ag) { // Class to provide access to a large file // as if it were a byte array. TypeGen FileByteArray = ag.Public.Class("FileByteArray"); { FieldGen stream = FileByteArray.Field <Stream>("stream"); // Holds the underlying stream // used to access the file. // Create a new FileByteArray encapsulating a particular file. CodeGen g = FileByteArray.Public.Constructor().Parameter <string>("fileName"); { g.Assign(stream, Exp.New <FileStream>(g.Arg("fileName"), FileMode.Open)); } // Close the stream. This should be the last thing done // when you are finished. g = FileByteArray.Public.Void("Close"); { g.Invoke(stream, "Close"); g.Assign(stream, null); } // Indexer to provide read/write access to the file. PropertyGen Item = FileByteArray.Public.Indexer <byte>().Index <long>("index"); // long is a 64-bit integer { // Read one byte at offset index and return it. g = Item.Getter(); { Operand buffer = g.Local(Exp.NewArray <byte>(1)); g.Invoke(stream, "Seek", g.Arg("index"), SeekOrigin.Begin); g.Invoke(stream, "Read", buffer, 0, 1); g.Return(buffer[0]); } // Write one byte at offset index and return it. g = Item.Setter(); { Operand buffer = g.Local(Exp.NewInitializedArray <byte>(g.PropertyValue())); g.Invoke(stream, "Seek", g.Arg("index"), SeekOrigin.Begin); g.Invoke(stream, "Write", buffer, 0, 1); } } // Get the total length of the file. FileByteArray.Public.Property <long>("Length").Getter().GetCode() .Return(stream.Invoke("Seek", 0, SeekOrigin.End)); } // Demonstrate the FileByteArray class. // Reverses the bytes in a file. TypeGen Reverse = ag.Public.Class("Reverse"); { CodeGen g = Reverse.Public.Static.Void("Main").Parameter <string[]>("args"); { Operand args = g.Arg("args"); // Check for arguments. g.If(args.ArrayLength() != 1); { g.WriteLine("Usage : Indexer <filename>"); g.Return(); } g.End(); // Check for file existence g.If(!Static.Invoke(typeof(File), "Exists", args[0])); { g.WriteLine("File " + args[0] + " not found."); g.Return(); } g.End(); Operand file = g.Local(Exp.New(FileByteArray, args[0])); Operand len = g.Local(file.Property("Length")); // Swap bytes in the file to reverse it. Operand i = g.Local <long>(); g.For(i.Assign(0), i < len / 2, i.Increment()); { Operand t = g.Local(); // Note that indexing the "file" variable invokes the // indexer on the FileByteStream class, which reads // and writes the bytes in the file. g.Assign(t, file[i]); g.Assign(file[i], file[len - i - 1]); g.Assign(file[len - i - 1], t); } g.End(); g.Invoke(file, "Close"); } } }
/// <summary> /// Create a wrapper class for a generic interface with more general type parameters than the wrapped interface. /// Downcasts to the correct more specific type are generated where necessary. /// This of course breaks type safety, and only calls to the class with the correct orginal types will work. /// Incorrect calls will throw <see cref = "InvalidCastException" />. /// </summary> /// <remarks> /// This is useful during reflection, when you don't want to know about specific types, but you can guarantee /// that a certain call will always be done with objects of the correct type. /// TODO: This non-generic method is only needed since RunSharp can't call generic methods, needed to generate wrappers recursively. /// TODO: Possibly Castle DynamicProxy could replace this if it allows creating 'non-matching' proxies and thus support the downcasting. /// </remarks> /// <param name = "typeToCreate">The less-specific generic type of the wrapper which will be generated.</param> /// <param name = "o">The object to wrap, which should implement the desired interface, with arbitrary type parameters.</param> /// <returns>An instance of the specified type which wraps the given object.</returns> public static object CreateGenericInterfaceWrapper(Type typeToCreate, object o) { Contract.Requires(o.GetType().IsOfGenericType(typeToCreate.GetGenericTypeDefinition())); Contract.Requires(typeToCreate.IsInterface); Type typeToCreateGeneric = typeToCreate.GetGenericTypeDefinition(); Type innerType = o.GetType(); Type innerMatchingType = innerType.GetMatchingGenericType(typeToCreateGeneric); // Implement passed type and redirect all public calls to inner instance. var assembly = new AssemblyGen("Whathecode.System.RunSharp"); TypeGen type = assembly.Public.Class("Wrapped" + typeToCreate.Name, typeof(object), typeToCreate); { const string inner = "inner"; FieldGen innerInstance = type.Private.Field(innerType, "_innerInstance"); FieldGen returnCached = type.Private.Field(typeof(Dictionary <int, object>), "_returnCached"); FieldGen returnWrappers = type.Private.Field(typeof(Dictionary <int, object>), "_returnWrappers"); // Create constructor which takes the wrapped instance as an argument. ConstructorGen constructor = type.Public.Constructor(); { constructor.Parameter(innerType, inner); CodeGen code = constructor.GetCode(); { code.Assign(innerInstance, code.Arg(inner)); code.Assign(returnCached, Exp.New(typeof(Dictionary <int, object>))); code.Assign(returnWrappers, Exp.New(typeof(Dictionary <int, object>))); } } // Create methods. int methodCount = 0; MethodInfo[] innerMethods = innerMatchingType.GetFlattenedInterfaceMethods(ReflectionHelper.FlattenedInstanceMembers).ToArray(); MethodInfo[] toCreateMethods = typeToCreate.GetFlattenedInterfaceMethods(ReflectionHelper.FlattenedInstanceMembers).ToArray(); MethodInfo[] genericMethods = typeToCreateGeneric.GetFlattenedInterfaceMethods(ReflectionHelper.FlattenedInstanceMembers).ToArray(); foreach (var method in innerMethods .Zip(toCreateMethods, genericMethods, (matching, toCreate, generic) => new { Id = methodCount++, Matching = matching, ToCreate = toCreate, Generic = generic }) .Where(z => z.Matching.IsPublic || z.Matching.IsFamily)) { // TODO: Not quite certain why override is required for extended interfaces (DeclaringType != typeTocreate), // but this seems to work. MethodInfo toCreate = method.ToCreate; MethodGen methodGen = toCreate.DeclaringType == typeToCreate ? type.MethodImplementation(typeToCreate, toCreate.ReturnType, toCreate.Name) : type.Public.Override.Method(toCreate.ReturnType, toCreate.Name); { ParameterInfo[] toCreateParameters = toCreate.GetParameters(); var parameters = toCreateParameters .Select(p => { var info = methodGen.BeginParameter(p.ParameterType, p.Name); info.End(); return(info); }).ToArray(); CodeGen code = methodGen.GetCode(); { // Cast arguments to the type of the inner instance. Operand[] args = parameters.Select(p => code.Arg(p.Name)).ToArray(); Operand[] castArgs = { }; if (args.Length > 0) { Type[] parameterTypes = method.Matching.GetParameters().Select(p => p.ParameterType).ToArray(); // TODO: When searching for generic methods, GetMethod returns null. http://stackoverflow.com/questions/4035719/getmethod-for-generic-method // Even when the correct method is found through custom filtering, RunSharp does not seem to be able to create generic methods yet. MethodInfo methodToCall = innerType.GetMethod(toCreate.Name, ReflectionHelper.FlattenedInstanceMembers, parameterTypes); castArgs = methodToCall.GetParameters() .Select((p, index) => args[index].Cast(typeof(object)).Cast(p.ParameterType)).ToArray(); } // Call inner instance and return value when needed. if (toCreate.ReturnType != typeof(void)) { Operand result = innerInstance.Invoke(toCreate.Name, castArgs); // Wrappers will recursively need to be created for generic return types. Type genericReturnType = method.Generic.ReturnType; if (genericReturnType.IsGenericType && genericReturnType.ContainsGenericParameters && genericReturnType.IsInterface) { // Check whether a new result is returned. Operand innerCached = code.Local(typeof(object)); code.If(returnCached.Invoke("TryGetValue", method.Id, innerCached.Ref())); { code.If((innerCached == result).LogicalNot()); { code.Invoke(returnWrappers, "Remove", method.Id); code.Invoke(returnCached, "Remove", method.Id); code.Invoke(returnCached, "Add", method.Id, result); } code.End(); } code.Else(); { code.Invoke(returnCached, "Add", method.Id, result); } code.End(); // Check whether a wrapper needs to be generated. Operand wrappedCached = code.Local(typeof(object)); code.If(returnWrappers.Invoke("TryGetValue", method.Id, wrappedCached.Ref()).LogicalNot()); { Operand proxied = Static.Invoke(typeof(Proxy), "CreateGenericInterfaceWrapper", toCreate.ReturnType, result); code.Assign(wrappedCached, proxied); code.Invoke(returnWrappers, "Add", method.Id, wrappedCached); } code.End(); code.Return(wrappedCached.Cast(toCreate.ReturnType)); } else { // A simple cast will work. // TODO: Throw proper exception when this is known to fail. E.g. generic type which is not an interface? code.Return(result.Cast(toCreate.ReturnType)); } } else { code.Invoke(innerInstance, toCreate.Name, castArgs); } } } } } Type wrapperType = type.GetCompletedType(true); return(Activator.CreateInstance(wrapperType, new[] { o })); }