Esempio n. 1
0
        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);
        }
Esempio n. 2
0
        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());
        }