private static Expression CreateBindingExpression(Type type, ReadOnlyMemory <char> expr, ParameterExpression scope, ParameterExpression instance) { Expression body = Expression.Convert(instance, type); var currType = type; var pos = 0; var depth = 0; var delim = ".".AsMemory(); while (expr.TryReadPart(delim, out ReadOnlyMemory <char> member, ref pos)) { try { if (member.IndexOf('(') >= 0) { throw new BindingExpressionException( $"Calling methods in '{expr}' is not allowed in binding expressions, use a filter instead.", member.ToString(), expr.ToString()); } var indexerPos = member.IndexOf('['); if (indexerPos >= 0) { var prop = member.LeftPart('['); var indexer = member.RightPart('['); indexer.Span.ParseJsExpression(out var token); if (token is JsCallExpression) { throw new BindingExpressionException($"Only constant binding expressions are supported: '{expr}'", member.ToString(), expr.ToString()); } var value = JsToken.UnwrapValue(token); var valueExpr = value == null ? (Expression)Expression.Call( typeof(SharpPageUtils).GetStaticMethod(nameof(EvaluateBinding)), scope, Expression.Constant(token)) : Expression.Constant(value); if (currType == typeof(string)) { body = CreateStringIndexExpression(body, token, scope, valueExpr, ref currType); } else if (currType.IsArray) { if (token != null) { var evalAsInt = typeof(SharpPageUtils).GetStaticMethod(nameof(EvaluateBindingAs)) .MakeGenericMethod(typeof(int)); body = Expression.ArrayIndex(body, Expression.Call(evalAsInt, scope, Expression.Constant(token))); } else { body = Expression.ArrayIndex(body, valueExpr); } } else if (depth == 0) { var pi = AssertProperty(currType, "Item", expr); currType = pi.PropertyType; if (token != null) { var indexType = pi.GetGetMethod()?.GetParameters().FirstOrDefault()?.ParameterType; if (indexType != typeof(object)) { var evalAsInt = typeof(SharpPageUtils).GetStaticMethod(nameof(EvaluateBindingAs)) .MakeGenericMethod(indexType); valueExpr = Expression.Call(evalAsInt, scope, Expression.Constant(token)); } } body = Expression.Property(body, "Item", valueExpr); } else { var pi = AssertProperty(currType, prop.ToString(), expr); currType = pi.PropertyType; body = Expression.PropertyOrField(body, prop.ToString()); if (currType == typeof(string)) { body = CreateStringIndexExpression(body, token, scope, valueExpr, ref currType); } else { var indexMethod = currType.GetMethod("get_Item", new[] { value.GetType() }); body = Expression.Call(body, indexMethod, valueExpr); currType = indexMethod.ReturnType; } } } else { if (depth >= 1) { var memberName = member.ToString(); if (typeof(IDictionary).IsAssignableFrom(currType)) { var pi = AssertProperty(currType, "Item", expr); currType = pi.PropertyType; body = Expression.Property(body, "Item", Expression.Constant(memberName)); } else { body = Expression.PropertyOrField(body, memberName); var pi = currType.GetProperty(memberName); if (pi != null) { currType = pi.PropertyType; } else { var fi = currType.GetField(memberName); if (fi != null) { currType = fi.FieldType; } } } } } depth++; } catch (BindingExpressionException) { throw; } catch (Exception e) { throw new BindingExpressionException($"Could not compile '{member}' from expression '{expr}'", member.ToString(), expr.ToString(), e); } } return(body); }