예제 #1
0
        public static (Expression, (ParameterExpression, object)[]) ReplaceBadConstants(Expression expr)
        {
            var v = new UnallowedConstantRemover();

            return(v.Visit(expr), v.Replacements.ToArray());
        }
예제 #2
0
        public (Expression builder, ParameterExpression[] fields) CreateBuilder(DotvvmConfiguration configuration)
        {
            var dict       = new Dictionary <object, Expression>();
            var callStack  = new HashSet <object>();
            var locals     = new List <ParameterExpression>();
            var results    = new List <ParameterExpression>();
            var body       = new List <Expression>();
            var dedupCache = new Dictionary <Expression, ParameterExpression>(ExpressionComparer.Instance);

            //var presetObjects = new Dictionary<object, Expression>();
            MapObject(configuration, DotvvmConfigurationParameter, dict);

            Expression serializeObject(object obj, Type expectedType)
            {
                if (obj == null)
                {
                    return(Expression.Constant(null, expectedType));
                }
                if (dict.TryGetValue(obj, out var rrrr))
                {
                    return(rrrr);
                }
                if (!callStack.Add(obj))
                {
                    throw new NotSupportedException($"Reference cycles are not supported.");
                }
                var objType = obj.GetType();

                ParameterExpression ret(Expression e, bool thisobj = true)
                {
                    if (dedupCache.TryGetValue(e, out var p))
                    {
                        return(p);
                    }
                    p = Expression.Parameter(e.Type);
                    body.Add(Expression.Assign(p, e));
                    if (thisobj)
                    {
                        dict.Add(obj, p);
                    }
                    locals.Add(p);
                    dedupCache.Add(e, p);
                    return(p);
                }

                try
                {
                    if (KnownObjects.TryGetValue(obj, out var result))
                    {
                        return(result);
                    }
                    else if (objType.IsPrimitive || obj is string || obj is MemberInfo)
                    {
                        return(Expression.Constant(obj, objType.GetPublicBaseType()));
                    }
                    else if (obj is System.Collections.IEnumerable collection)
                    {
                        var        element = ReflectionUtils.GetEnumerableType(collection.GetType());
                        Expression expr    = collection.OfType <object>()
                                             .Select(e => serializeObject(e, element))
                                             .Apply(c => Expression.NewArrayInit(element, c));
                        if (!objType.IsArray)
                        {
                            var targetType = objType.Name == "ImmutableList`1" ? typeof(ImmutableList) :
                                             //objType.Name == "ImmutableDictionary" ? typeof(ImmutableDictionary) :
                                             typeof(ImmutableArray);
                            expr = expr.Apply(c => Expression.Call(null, targetType
                                                                   .GetMethods(BindingFlags.Static | BindingFlags.Public)
                                                                   .First(m => (m.Name == "Create" || m.Name == "To" + targetType.Name) && m.GetParameters().FirstOrDefault()?.ParameterType.IsArray == true)
                                                                   .MakeGenericMethod(new[] { element }),
                                                                   c
                                                                   ));
                        }
                        return(Expression.Convert(ret(expr), expectedType));
                    }
                    else if (obj is Expression expression)
                    {
                        return(ret(Expression.Quote(expression)));
                    }
                    else if (obj is Delegate deleg)
                    {
                        if (!translatedDelegates.TryGetValue(deleg, out var method))
                        {
                            throw new NotSupportedException("Could not serialize delegate");
                        }
                        var(sanitizedMethod, parameters) = UnallowedConstantRemover.ReplaceBadConstants(method);
                        return(ret(ExpressionUtils.Replace(Expression.Lambda(sanitizedMethod, parameters.Select(l => l.Item1)), parameters.Select(p => serializeObject(p.Item2, p.Item1.Type)).ToArray())));
                    }
                    else if (obj is IBinding binding)
                    {
                        var properties = BindingCompiler.GetMinimalCloneProperties(binding)
                                         .Select(p => serializeObject(p, p.GetType()))
                                         .Apply(p => Expression.NewArrayInit(typeof(object), p));

                        return(ret(Expression.New(
                                       binding.GetType().GetConstructor(new[] { typeof(BindingCompilationService), typeof(IEnumerable <object>) }),
                                       ret(BindingCompilationService, thisobj: false),
                                       properties
                                       )));
                    }
                    else
                    {
                        var map      = Map(objType.GetPublicBaseType());
                        var ctorArgs = map.CtorArguments.Select(p => serializeObject(Evaluate(p, obj), p.ReturnType)).ToArray();
                        var newObj   = ExpressionUtils.Replace(map.Ctor, ctorArgs);
                        if (!newObj.Type.IsValueType || map.Properties.Any())
                        {
                            newObj = ret(newObj);
                        }
                        foreach (var prop in map.Properties)
                        {
                            body.Add(ExpressionUtils.Replace(prop.Setter, newObj, serializeObject(Evaluate(prop.Getter, obj), prop.Getter.ReturnType)));
                        }
                        return(newObj);
                    }
                }
                finally
                {
                    callStack.Remove(obj);
                }
            }

            foreach (var req in requiredObjects)
            {
                var expr  = serializeObject(req.Key, req.GetType().GetPublicBaseType());
                var field = Expression.Parameter(req.Key.GetType().GetPublicBaseType(), req.Value);
                body.Add(Expression.Assign(field, expr));
                results.Add(field);
            }
            return(Expression.Block(locals, body), results.ToArray());
        }