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()); }