Esempio n. 1
0
        internal Type Build(Type parent, Type target, IIntrospect introspector)
        {
            Assertion.IsNotNull("parent", parent);
            Assertion.IsNotNull("target", target);

            var ident = Ident.Create(parent, target);

            Type type;

            if (Built.TryGetValue(ident, out type))
            {
                return(type);
            }

            lock (Built)
            {
                if (Built.TryGetValue(ident, out type))
                {
                    return(type);
                }

                return(Built[ident] = Build(ident, introspector));
            }
        }
Esempio n. 2
0
        Type Build(Ident ident, IIntrospect introspector)
        {
            var parent = ident.Item1;
            var target = ident.Item2;

            Assertion.IsNotNull("introspector", introspector);
            Assertion.IsAccessible("parent", parent);
            Assertion.IsAccessible("target", target);

            Assertion.IsClass("parent", parent);
            Assertion.ExtendsSkeleton("parent", parent);
            Assertion.IsNotSealed("parent", parent);
            Assertion.IsInterface("target", target);

            var methods = Methods(target).Where(ReflectionExtensions.IsMethod);

            var maps = parent.GetInterfaces()
                       .Select(parent.GetInterfaceMap)
                       .SelectMany(ZipMap)
                       .ToDictionary(x => x.Item1, x => x.Item2);

            var name = parent.Derived(target);

            var typB = ModB.DefineType(name:       name,
                                       attr:       TypeAttributes.Class,
                                       parent:     parent,
                                       interfaces: new [] { target });

            foreach (var method in methods)
            {
                MethodInfo over;
                if (maps.TryGetValue(method, out over) && !over.IsAbstract)
                {
                    continue;
                }

                Assertion.IsAsync(method);

                var call = introspector.Call(parent, method);

                Assertion.HasValidDescription(method, call);

                call.Headers = call.Headers ?? new Headers();
                call.Body    = call.Body ?? new Body();
                call.Static  = call.Static ?? new Static();

                var args = method.GetParameters();

                var metB = typB.DefineMethod(name:              method.DeclaringType.Name + "." + method.Name,
                                             attributes:        method.Attributes & ~MethodAttributes.Abstract,
                                             callingConvention: method.CallingConvention,
                                             returnType:        method.ReturnType,
                                             parameterTypes:    args.Select(x => x.ParameterType).ToArray());

                var ilG = metB.GetILGenerator();

                typB.DefineMethodOverride(metB, method);

                if (null != over)
                {
                    typB.DefineMethodOverride(metB, over);
                }

                // Separate parameters
                var bodyP = call.Body.Keys.ToArray();
                var headP = call.Headers.Keys.ToArray();
                var restP = args.Except(bodyP.Union(headP)).ToArray();

                var caTok = args.FirstOrDefault(x => typeof(CancellationToken?).IsAssignableFrom(x.ParameterType));

                // Decide immediately if we can tail call from SendAsync
                var thrmT = typeof(Task <HttpResponseMessage>);
                var tail  = thrmT == method.ReturnType;
                var ctHM  = typeof(HttpMethod).GetConstructor(new [] { typeof(string) });

                ilG.Emit(OpCodes.Ldarg_0);
                if (!tail)
                {
                    ilG.Emit(OpCodes.Dup);
                }
                ilG.Emit(OpCodes.Ldstr, call.Verb.Method);
                ilG.Emit(OpCodes.Newobj, ctHM);
                ilG.EmitString(call.Template);

                var dict = typeof(Params);
                var ctor = dict.GetConstructor(EmpT);
                var madd = dict.GetProperty("Item").SetMethod;

                // Construct parameters dictionary
                ilG.Emit(OpCodes.Newobj, ctor);
                foreach (var arg in restP)
                {
                    ilG.Emit(OpCodes.Dup);
                    ilG.Emit(OpCodes.Ldstr, arg.Name);
                    ilG.Emit(OpCodes.Ldarg, arg.Position + 1);

                    if (arg.ParameterType.IsValueType)
                    {
                        ilG.Emit(OpCodes.Box, arg.ParameterType);
                    }

                    ilG.Emit(OpCodes.Call, madd);
                }

                // Construct headers dictionary
                ilG.Emit(OpCodes.Newobj, ctor);

                // Pack static headers, but ignore the ones we would replace
                foreach (var head in call.Static)
                {
                    ilG.Emit(OpCodes.Dup);
                    ilG.Emit(OpCodes.Ldstr, head.Key);
                    ilG.Emit(OpCodes.Ldc_I4, head.Value.Length);
                    ilG.Emit(OpCodes.Newarr, typeof(string));

                    for (int i = 0, end = head.Value.Length; i < end; ++i)
                    {
                        ilG.Emit(OpCodes.Dup);
                        ilG.Emit(OpCodes.Ldc_I4, i);
                        ilG.Emit(OpCodes.Ldstr, head.Value[i]);
                        ilG.Emit(OpCodes.Stelem, typeof(string));
                    }

                    ilG.Emit(OpCodes.Call, madd);
                }

                // Pack parameter headers
                foreach (var head in headP)
                {
                    ilG.Emit(OpCodes.Dup);
                    ilG.Emit(OpCodes.Ldstr, call.Headers[head]);
                    ilG.Emit(OpCodes.Ldarg, head.Position + 1);

                    if (head.ParameterType.IsValueType)
                    {
                        ilG.Emit(OpCodes.Box, head.ParameterType);
                    }

                    ilG.Emit(OpCodes.Call, madd);
                }

                var delM = GetMethod(parent, "SendAsync", null, SendAsyncParams());
                // Build content
                if (0 == bodyP.Length)
                {
                    ilG.Emit(OpCodes.Ldnull);
                }
                else
                {
                    var imp = Imp;

                    var ctb = typeof(SkeletonClient).GetMethod("ContentBuilder", BindingFlags.NonPublic | BindingFlags.Instance);
                    ilG.Emit(OpCodes.Ldarg_0);
                    ilG.Emit(OpCodes.Callvirt, ctb);

                    imp.Process(ilG, call);
                }

                // Load cancellation token
                if (caTok != null)
                {
                    ilG.Emit(OpCodes.Ldarg, caTok.Position + 1);
                    ilG.EmitNullConversion(caTok.ParameterType);
                }
                else
                {
                    LocalBuilder caTokF = ilG.DeclareLocal(typeof(CancellationToken?));
                    ilG.Emit(OpCodes.Ldloca, caTokF);
                    ilG.Emit(OpCodes.Initobj, typeof(CancellationToken?));
                    ilG.Emit(OpCodes.Ldloc, caTokF);
                }

                // Call SendAsync
                if (tail)
                {
                    ilG.Emit(OpCodes.Tailcall);
                }
                ilG.EmitCall(delM);

                // Convert return type if necessary
                if (!tail)
                {
                    var conM = method.ReturnType.IsGenericType
                        ? GetMethod(parent, "Return", method.ReturnType.GetGenericArguments(), new [] { thrmT })
                        : GetMethod(parent, "Return", null, new [] { thrmT });

                    ilG.Emit(OpCodes.Tailcall);
                    ilG.EmitCall(conM);
                }
                ilG.Emit(OpCodes.Ret);
            }

            // Generate constructors
            foreach (var ctor in parent.GetConstructors(BindingFlags.Public | BindingFlags.Instance))
            {
                var ctorP = ctor.GetParameters();
                var ctorB = typB.DefineConstructor(MethodAttributes.Public,
                                                   CallingConventions.Standard | CallingConventions.HasThis,
                                                   ctorP.Select(x => x.ParameterType).ToArray());
                var ilG = ctorB.GetILGenerator();
                ilG.Emit(OpCodes.Ldarg_0);
                foreach (var param in ctorP)
                {
                    var p = ctorB.DefineParameter(param.Position + 1, param.Attributes, param.Name);
                    ilG.Emit(OpCodes.Ldarg, p.Position);
                }
                ilG.Emit(OpCodes.Call, ctor);
                ilG.Emit(OpCodes.Ret);
            }

            return(typB.CreateType());
        }