internal static int[] GetSlotMapsToInterface(RedwoodType from, RedwoodType to) { // We have to map to an interface, otherwise we're just // shoving things where they don't belong if (!to.IsInterface) { throw new NotImplementedException(); } // We want to fill up every slot up to the overloads, which are // packed at the end of the class // TODO: This is a bad way of counting overloads. It checks whether // a given overload index is "self referential", meaning that it's the // only option, in which case it would not constitute a "LambdaGroup" int numOverloads = to.overloadsMap .Where(kv => kv.Key != kv.Value.Item2[0]) .Count(); int[] slotsToUse = new int[to.numSlots - numOverloads]; foreach (KeyValuePair <string, int> member in to.slotMap) { string name = member.Key; int slot = member.Value; RedwoodType slotType = to.slotTypes[slot]; if (slotType != null && slotType.CSharpType == typeof(LambdaGroup)) { Tuple <RedwoodType[][], int[]> overloadSlots = to.overloadsMap[slot]; for (int i = 0; i < overloadSlots.Item2.Length; i++) { int overloadMemberSlot = overloadSlots.Item2[i]; RedwoodType[] overloadArgTypes = overloadSlots.Item1[i]; slotsToUse[overloadMemberSlot] = from.GetSlotNumberForOverload(name, overloadArgTypes); } } else if (slotType != null && typeof(Lambda).IsAssignableFrom(slotType.CSharpType)) { slotsToUse[slot] = from.GetSlotNumberForOverload( name, slotType.GenericArguments .SkipLast(1) .ToArray() ); } else { // TODO: This case should only be hit for the this keyword slotsToUse[slot] = from.slotMap[name]; } } return(slotsToUse); }
internal static Type EmitInterfaceProxyType(RedwoodType type, Type @interface) { TypeBuilder tb = interfaceModules.DefineType( @interface.Name + "RedwoodProxy", TypeAttributes.Public | TypeAttributes.AutoClass | TypeAttributes.AnsiClass, typeof(object), new Type[] { @interface }); FieldBuilder fb = tb.DefineField("proxy", typeof(object[]), FieldAttributes.Private); ConstructorBuilder cb = tb.DefineConstructor( MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.HasThis, new Type[] { typeof(object[]) } ); ILGenerator constructorGenerator = cb.GetILGenerator(); // Call our base constructor ConstructorInfo objectConstructor = typeof(object).GetConstructor(new Type[] { }); constructorGenerator.Emit(OpCodes.Ldarg_0); constructorGenerator.Emit(OpCodes.Call, objectConstructor); // Load this, then load arg 1, then set the local field constructorGenerator.Emit(OpCodes.Ldarg_0); constructorGenerator.Emit(OpCodes.Ldarg_1); constructorGenerator.Emit(OpCodes.Stfld, fb); constructorGenerator.Emit(OpCodes.Ret); MethodInfo runMethod = typeof(Lambda).GetMethod("Run"); foreach (MethodInfo method in @interface.GetMethods()) { Type[] paramTypes = method .GetParameters() .Select(param => param.ParameterType) .ToArray(); // The method is on an interface so it is guaranteed // to have the abstract tag so we have to reverse that MethodBuilder mb = tb.DefineMethod( method.Name, method.Attributes ^ MethodAttributes.Abstract, method.ReturnType, paramTypes ); int numberOfArguments = method.GetParameters().Length; // The index into our proxy object array holding the overload int index = type.GetSlotNumberForOverload( method.Name, paramTypes .Select(type => RedwoodType.GetForCSharpType(type)) .ToArray() ); ILGenerator generator = mb.GetILGenerator(); generator.DeclareLocal(typeof(int)); // Let's load the proxy object because we're going // to be accessing on it a little later generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldfld, fb); // Now go fishing for our overload generator.Emit(OpCodes.Ldc_I4, index); generator.Emit(OpCodes.Ldelem_Ref); // ILSpy says we do this check now -- I guess the actual throw // is handled by the runtime? generator.Emit(OpCodes.Isinst, typeof(Lambda)); // Create an array to pass as the params argument generator.Emit(OpCodes.Ldc_I4, numberOfArguments); generator.Emit(OpCodes.Newarr, typeof(object)); // Set each argument on the params array for (int i = 0; i < numberOfArguments; i++) { // Get a copy of the array generator.Emit(OpCodes.Dup); // Index generator.Emit(OpCodes.Ldc_I4, i); // Element generator.Emit(OpCodes.Ldarg, i + 1); // Since we're going into an object array, we need // to box our value types if (paramTypes[i].IsValueType) { // TODO: is there a separate boxed type from value type? generator.Emit(OpCodes.Box, paramTypes[i]); } // Set generator.Emit(OpCodes.Stelem_Ref); } generator.Emit(OpCodes.Callvirt, runMethod); if (method.ReturnType.IsValueType) { generator.Emit(OpCodes.Unbox_Any, method.ReturnType); } // For some reason, the compiler likes to store and load before // returning, so lets do it here too // TODO: does this look different for non-value types? generator.Emit(OpCodes.Stloc_0); generator.Emit(OpCodes.Ldloc_0); generator.Emit(OpCodes.Ret); } return(tb.CreateType()); }