private Func <T, IEndpoint, IDictionary <string, string>, Task <string> > BindAction(string path)
            {
                var target     = Expression.Parameter(typeof(T));
                var endpoint   = Expression.Parameter(typeof(IEndpoint));
                var parameters = Expression.Parameter(typeof(IDictionary <string, string>));

                Expression current = target;
                var        entries = path.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);

                for (int i = 0; i < entries.Length; ++i)
                {
                    var property = current.Type.GetTypeHierarchy().Select(x => x.GetProperty(entries[i], BindingFlags.Public | BindingFlags.Instance)).FirstOrDefault(x => x != null);
                    if (property != null)
                    {
                        current = Expression.Property(current, property);
                        continue;
                    }

                    if (i != entries.Length - 1)
                    {
                        current = ThrowExpression(string.Format("member '{0}' not found", entries[i]));
                        break;
                    }

                    if (entries[i] == TinyProtocol.DetachCommand)
                    {
                        break;
                    }

                    var indexer = typeof(IDictionary <string, string>).GetProperty("Item");
                    var method  = current.Type.GetTypeHierarchy()
                                  .SelectMany(x => x.GetMethods(BindingFlags.Public | BindingFlags.Instance))
                                  .Where(x => !x.IsGenericMethod)
                                  .FirstOrDefault(x => x.Name == entries[i] && x.GetParameters().All(p => TinyProtocol.Check(p.ParameterType).CanDeserialize()));
                    if (method == null)
                    {
                        current = ThrowExpression(string.Format("member '{0}' not found", entries[i]));
                        break;
                    }

                    current = Expression.Call(
                        current,
                        method,
                        method.GetParameters().Select(x => DeserializeParameter(Expression.Property(parameters, indexer, Expression.Constant(x.Name)), endpoint, x.ParameterType)).ToArray());
                }

                var signature = new AsyncTypeSignature(current.Type);

                if (TinyProtocol.Check(signature.ReturnType).CanSerialize())
                {
                    if (signature.ReturnType == typeof(void))
                    {
                        if (signature.IsAsync)
                        {
                            current = Expression.Call(typeof(DispatcherHelpers).GetMethod("WrapTask"), current);
                        }
                        else
                        {
                            current = Expression.Block(current, Expression.Call(typeof(Task).GetMethod("FromResult").MakeGenericMethod(typeof(string)), Expression.Constant(string.Empty)));
                        }
                    }
                    else
                    {
                        current = Expression.Call(typeof(DispatcherHelpers).GetMethod(signature.IsAsync ? "WrapValueAsync" : "WrapValue").MakeGenericMethod(signature.ReturnType), endpoint, current);
                    }
                }
                else
                {
                    throw new Exception("cannot invoke method - unsupported return type");
                }

                return(Expression.Lambda <Func <T, IEndpoint, IDictionary <string, string>, Task <string> > >(
                           current,
                           target,
                           endpoint,
                           parameters).Compile());
            }
        public static Type BuildProxyType(Type interfaceType)
        {
            var typeBuilder = ProxiesModule.DefineType(interfaceType.Name + "_Proxy" + _counter++, TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Sealed, typeof(ProxyBase), new[] { interfaceType });
            var constructorParameterTypes = new[] { typeof(IEndpoint), typeof(string) };
            var baseConstructor           = typeof(ProxyBase).GetConstructor(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, constructorParameterTypes, null);

            var constructorBuilder     = typeBuilder.DefineExpressionConstructor(baseConstructor);
            var constructorExpressions = new List <Expression>();

            foreach (var method in interfaceType.GetPublicMethods())
            {
                if (method.IsSpecialName || !method.IsVirtual)
                {
                    continue;
                }

                if (method.IsGenericMethod)
                {
                    var parameters     = method.GetParameters();
                    var parameterTypes = parameters.Select(x => x.ParameterType).ToArray();
                    var methodBuilder  = typeBuilder.DefineMethod(method.Name, method.Attributes & ~MethodAttributes.Abstract, method.CallingConvention, method.ReturnType, parameterTypes);
                    methodBuilder.DefineGenericParameters(method.GetGenericArguments().Select(x => x.Name).ToArray());
                    var il = methodBuilder.GetILGenerator();
                    il.Emit(OpCodes.Ldstr, "cannot invoke generic method");
                    il.Emit(OpCodes.Newobj, ExceptionConstructor);
                    il.Emit(OpCodes.Throw);
                    if (method.ReturnType != typeof(void))
                    {
                        il.Emit(OpCodes.Ldloc, il.DeclareLocal(method.ReturnType));
                    }
                    il.Emit(OpCodes.Ret);

                    continue;
                }

                var signature = new AsyncTypeSignature(method.ReturnType);
                var builder   = typeBuilder.DefineExpressionMethod(method);

                var dict  = Expression.Parameter(typeof(IDictionary <string, string>));
                var block = new List <Expression>();
                block.Add(Expression.Assign(dict, builder.This.CallMember(CreateQuery)));

                string errorMessage = null;
                if (builder.Parameters.All(x => TinyProtocol.Check(x.Type).CanSerialize()))
                {
                    block.AddRange(builder.Parameters.Select(x => dict.CallMember(
                                                                 AddParameter,
                                                                 Expression.Constant(x.Name),
                                                                 x.Serialize(builder.This.MemberField(typeof(ProxyBase).GetField("Endpoint", BindingFlags.Instance | BindingFlags.NonPublic))))));

                    if (TinyProtocol.Check(signature.ReturnType).CanDeserialize())
                    {
                        Expression call;
                        call = signature.ReturnType == typeof(void)
                            ? builder.This.CallMember(ExecuteQuery, Expression.Constant(method.Name), dict)
                            : builder.This.CallMember(ExecuteQueryRet.MakeGenericMethod(signature.ReturnType), Expression.Constant(method.Name), dict);

                        block.Add(signature.IsAsync ? call : Wait(call));
                    }
                    else
                    {
                        errorMessage = string.Format("incompatible return type '{0}'", signature.ReturnType);
                    }
                }
                else
                {
                    errorMessage = "cannot serialize all parameter types";
                }

                if (errorMessage != null)
                {
                    block.Add(Expression.Throw(Expression.New(ExceptionConstructor, Expression.Constant(errorMessage))));
                    block.Add(Expression.Default(method.ReturnType));
                }

                builder.Implement(Expression.Block(
                                      method.ReturnType,
                                      new[] { dict },
                                      block));
            }

            foreach (var property in interfaceType.GetPublicProperies())
            {
                var builder = typeBuilder.DefineExpressionProperty(property);
                if (property.CanRead)
                {
                    ImplementGetter(typeBuilder, builder, property);
                }

                if (property.CanWrite)
                {
                    ImplementSetter(builder, property);
                }
            }

            constructorExpressions.Add(Expression.Empty());
            constructorBuilder.Implement(Expression.Block(typeof(void), constructorExpressions));
            return(typeBuilder.CreateType());
        }