public static InterfaceDescriptor Get(Type type)
 {
     if (!Cache.TryGetValue(type, out var desc))
     {
         Cache.TryAdd(type, desc = new InterfaceDescriptor(type));
     }
     return(desc);
 }
예제 #2
0
        private static EntangledTypeProxyDescriptor ConstructLocalProxy <T>() where T : class/*, IEntangledObject*/
        {
            var typeInfo = typeof(T).GetTypeInfo();

            if (!typeInfo.IsInterface || !typeInfo.IsPublic)
            {
                throw new ArgumentException("The provided type must be a public interface");
            }

            var elo = typeof(EntangledLocalObjectBase);

            var desc = new InterfaceDescriptor(typeof(T));
            var guid = typeInfo.GUID;
            var type = DynamicAssembly.DynamicModule.DefineType($"T{guid.ToString()}", TypeAttributes.Class | TypeAttributes.Public,
                                                                elo);

            type.AddInterfaceImplementation(typeof(T));

            var executeMethod =
                elo.GetMethod("ExecuteMethod",
                              BindingFlags.Public | BindingFlags.Instance);

            var executeMethodVoid =
                elo.GetMethod("ExecuteMethodVoid",
                              BindingFlags.Public | BindingFlags.Instance);

            var executeMethodSync     = elo.GetMethod("ExecuteMethodSync", BindingFlags.Public | BindingFlags.Instance);
            var executeMethodVoidSync =
                elo.GetMethod("ExecuteMethodVoidSync", BindingFlags.Public | BindingFlags.Instance);


            type.FillBaseConstructors(elo);

            foreach (var methods in desc.Methods)
            {
                int methodOverloadId = 0;
                foreach (var method in methods.Value)
                {
                    var parameters = method.Method.GetParameters();
                    var m          = type.DefineMethod(method.Method.Name, MethodAttributes.Public | MethodAttributes.Virtual,
                                                       method.Method.CallingConvention, method.Method.ReturnType,
                                                       method.Method.GetParameters().Select(p => p.ParameterType).ToArray());
                    var i = m.GetILGenerator();

                    i.Emit(OpCodes.Ldarg_0);
                    i.EmitLdci4(methodOverloadId++);
                    i.Emit(OpCodes.Ldstr, method.Method.Name);

                    //emit the object[] array to hold the parameters

                    if ((parameters?.Length ?? 0) == 0)
                    {
                        i.Emit(OpCodes.Ldnull);
                    }
                    else
                    {
                        i.EmitLdci4((byte)parameters.Length);
                        i.Emit(OpCodes.Newarr, typeof(object));
                        for (byte l = 0; l < parameters.Length; l++)
                        {
                            i.Emit(OpCodes.Dup);
                            i.EmitLdci4(l);
                            i.EmitLdarg((byte)(l + 1));
                            if (parameters[l].ParameterType.GetTypeInfo().IsValueType)
                            {
                                i.Emit(OpCodes.Box, parameters[l].ParameterType);
                            }

                            i.Emit(OpCodes.Stelem_Ref);
                        }
                    }

                    if (method.RealReturnType == typeof(void))
                    {
                        i.Emit(OpCodes.Call, method.IsAsync ? executeMethodVoid : executeMethodVoidSync);
                    }
                    else
                    {
                        i.Emit(OpCodes.Call, (method.IsAsync ? executeMethod : executeMethodSync).MakeGenericMethod(method.RealReturnType));
                    }

                    i.Emit(OpCodes.Ret);

                    //important: OVERRIDE THE INTERFACE [abstract] IMPLEMENTATION
                    type.DefineMethodOverride(m, method.Method);
                }
            }
            //implement getters

            var generatedProperties = new Queue <string>();

            foreach (var propItem in desc.Properties)
            {
                var prop     = propItem.Value.Property;
                var baseProp = typeof(EntangledLocalObjectBase).GetProperty(propItem.Key,
                                                                            BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public);
                if (baseProp != null && !baseProp.GetGetMethod().IsAbstract)
                {
                    continue;
                }

                var definedGetter = prop.GetGetMethod();

                var field = type.DefineField($"_{prop.Name}", prop.PropertyType, FieldAttributes.Private);
                propItem.Value.BackingField = field;
                var getProp = type.DefineProperty(prop.Name, PropertyAttributes.None, prop.PropertyType, null);
                var getter  = type.DefineMethod($"get_{prop.Name}",
                                                MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.Virtual,
                                                definedGetter.ReturnType, null);

                var il = getter.GetILGenerator();
                il.Emit(OpCodes.Ldarg_0);
                il.Emit(OpCodes.Ldfld, field);
                il.Emit(OpCodes.Ret);

                getProp.SetGetMethod(getter);

                type.DefineMethodOverride(getter, definedGetter);

                generatedProperties.Enqueue(propItem.Key);
            }

            foreach (var ev in desc.Events)
            {
                type.ImplementEvent(ev.Value.Event.DeclaringType.GetTypeInfo(), ev.Key);
            }


            var result =
                new EntangledTypeProxyDescriptor {
                GeneratedType = type.CreateTypeInfo().AsType(), Interface = desc
            };

            foreach (var ev in desc.Events)
            {
                ev.Value.BackingField =
                    result.GeneratedType.GetField(ev.Key, BindingFlags.NonPublic | BindingFlags.Instance);
                ev.Value.InvokerDelegate = CreateEventInvokerDelegate(type, ev.Value, typeof(T));
            }
            while (generatedProperties.Any())
            {
                var prop = generatedProperties.Dequeue();
                desc.Properties[prop].BackingField = result.GeneratedType.GetField($"_{prop}",
                                                                                   BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
            }

            return(result);
        }