Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/Framework/Framework/Binding/ActiveDotvvmProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ namespace DotVVM.Framework.Binding
/// <summary> An abstract DotvvmProperty which contains code to be executed when the assigned control is being rendered. </summary>
public abstract class ActiveDotvvmProperty : DotvvmProperty
{
internal ActiveDotvvmProperty(string name, Type declaringType, bool isValueInherited) : base(name, declaringType, isValueInherited)
{
}

public abstract void AddAttributesToRender(IHtmlWriter writer, IDotvvmRequestContext context, DotvvmControl control);


Expand Down
17 changes: 8 additions & 9 deletions src/Framework/Framework/Binding/BindingHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ public static string FormatKnockoutScript(this ParametrizedCode code, DotvvmBind
/// Gets Internal.PathFragmentProperty or DataContext.KnockoutExpression. Returns null if none of these is set.
/// </summary>
public static string? GetDataContextPathFragment(this DotvvmBindableObject currentControl) =>
currentControl.properties.TryGet(Internal.PathFragmentProperty, out var pathFragment) && pathFragment is string pathFragmentStr ? pathFragmentStr :
currentControl.properties.TryGet(DotvvmBindableObject.DataContextProperty, out var dataContext) && dataContext is IValueBinding binding ?
currentControl.properties.TryGet(DotvvmPropertyIdAssignment.PropertyIds.Internal_PathFragment, out var pathFragment) && pathFragment is string pathFragmentStr ? pathFragmentStr :
currentControl.properties.TryGet(DotvvmPropertyIdAssignment.PropertyIds.DotvvmBindableObject_DataContext, out var dataContext) && dataContext is IValueBinding binding ?
binding.GetProperty<SimplePathExpressionBindingProperty>()
.Code.FormatKnockoutScript(currentControl, binding) :
null;
Expand All @@ -88,16 +88,16 @@ internal static (int stepsUp, DotvvmBindableObject target) FindDataContextTarget
if (bindingContext == null || controlContext == null || controlContext.Equals(bindingContext)) return (0, control);

var changes = 0;
foreach (var a in control.GetAllAncestors(includingThis: true))
for (var ancestor = control; ancestor is {}; ancestor = ancestor.Parent)
{
var ancestorContext = a.GetDataContextType(inherit: false);
var ancestorContext = ancestor.GetDataContextType(inherit: false);
if (bindingContext.Equals(ancestorContext))
return (changes, a);
return (changes, ancestor);

// count only client-side data contexts (DataContext={resource:} is skipped in JS)
if (a.properties.TryGet(DotvvmBindableObject.DataContextProperty, out var ancestorRuntimeContext))
if (ancestor.properties.TryGet(DotvvmPropertyIdAssignment.PropertyIds.DotvvmBindableObject_DataContext, out var ancestorRuntimeContext))
{
if (a.properties.TryGet(Internal.IsServerOnlyDataContextProperty, out var isServerOnly) && isServerOnly != null)
if (ancestor.properties.GetOrNull(DotvvmPropertyIdAssignment.PropertyIds.Internal_IsServerOnlyDataContext) is {} isServerOnly)
{
if (isServerOnly is false)
changes++;
Expand Down Expand Up @@ -173,9 +173,8 @@ public static T ExecDelegate<T>(this BindingDelegate<T> func, DotvvmBindableObje
// this has O(h^2) complexity because GetValue calls another GetDataContexts,
// but this function is used rarely - for exceptions, manually created bindings, ...
// Normal bindings have specialized code generated in BindingCompiler
if (c.IsPropertySet(DotvvmBindableObject.DataContextProperty, inherit: false))
if (c.properties.Contains(DotvvmPropertyIdAssignment.PropertyIds.DotvvmBindableObject_DataContext))
{
Debug.Assert(c.properties.Contains(DotvvmBindableObject.DataContextProperty), "Control claims that DataContextProperty is set, but it's not present in the properties dictionary.");
yield return c.GetValue(DotvvmBindableObject.DataContextProperty);
count--;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace DotVVM.Framework.Binding
/// </summary>
public class CompileTimeOnlyDotvvmProperty : DotvvmProperty
{
public CompileTimeOnlyDotvvmProperty()
private CompileTimeOnlyDotvvmProperty(string name, Type declaringType) : base(name, declaringType, isValueInherited: false)
{
}

Expand All @@ -37,7 +37,7 @@ public override bool IsSet(DotvvmBindableObject control, bool inherit = true)
/// </summary>
public static CompileTimeOnlyDotvvmProperty Register<TPropertyType, TDeclaringType>(string propertyName)
{
var property = new CompileTimeOnlyDotvvmProperty();
var property = new CompileTimeOnlyDotvvmProperty(propertyName, typeof(TDeclaringType));
return (CompileTimeOnlyDotvvmProperty)Register<TPropertyType, TDeclaringType>(propertyName, property: property);
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/Framework/Framework/Binding/DelegateActionProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ namespace DotVVM.Framework.Binding
/// <summary> DotvvmProperty which calls the function passed in the Register method, when the assigned control is being rendered. </summary>
public sealed class DelegateActionProperty<TValue>: ActiveDotvvmProperty
{
private Action<IHtmlWriter, IDotvvmRequestContext, DotvvmProperty, DotvvmControl> func;
private readonly Action<IHtmlWriter, IDotvvmRequestContext, DotvvmProperty, DotvvmControl> func;

public DelegateActionProperty(Action<IHtmlWriter, IDotvvmRequestContext, DotvvmProperty, DotvvmControl> func)
public DelegateActionProperty(Action<IHtmlWriter, IDotvvmRequestContext, DotvvmProperty, DotvvmControl> func, string name, Type declaringType, bool isValueInherited) : base(name, declaringType, isValueInherited)
{
this.func = func;
}
Expand All @@ -27,7 +27,8 @@ public override void AddAttributesToRender(IHtmlWriter writer, IDotvvmRequestCon

public static DelegateActionProperty<TValue> Register<TDeclaringType>(string name, Action<IHtmlWriter, IDotvvmRequestContext, DotvvmProperty, DotvvmControl> func, [AllowNull] TValue defaultValue = default(TValue))
{
return (DelegateActionProperty<TValue>)DotvvmProperty.Register<TValue, TDeclaringType>(name, defaultValue, false, new DelegateActionProperty<TValue>(func));
var property = new DelegateActionProperty<TValue>(func, name, typeof(TDeclaringType), isValueInherited: false);
return (DelegateActionProperty<TValue>)DotvvmProperty.Register<TValue, TDeclaringType>(name, defaultValue, false, property);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,27 +76,30 @@ public static (LambdaExpression getter, LambdaExpression setter) CreatePropertyG
var valueParameter = Expression.Parameter(type, "value");
var ctor = typeof(VirtualPropertyGroupDictionary<>)
.MakeGenericType(propType)
.GetConstructor(new [] { typeof(DotvvmBindableObject), typeof(DotvvmPropertyGroup) })!;
.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, [ typeof(DotvvmBindableObject), typeof(ushort), typeof(bool) ], null)!;
var createMethod = typeof(VirtualPropertyGroupDictionary<>)
.MakeGenericType(propType)
.GetMethod(
typeof(ValueOrBinding).IsAssignableFrom(elementType) ? nameof(VirtualPropertyGroupDictionary<int>.CreatePropertyDictionary) :
nameof(VirtualPropertyGroupDictionary<int>.CreateValueDictionary),
BindingFlags.Public | BindingFlags.Static
BindingFlags.NonPublic | BindingFlags.Static,
binder: null,
[ typeof(DotvvmBindableObject), typeof(ushort) ],
modifiers: null
)!;
var enumerableType = typeof(IEnumerable<>).MakeGenericType(typeof(KeyValuePair<,>).MakeGenericType(typeof(string), elementType));
var copyFromMethod =
typeof(VirtualPropertyGroupDictionary<>)
.MakeGenericType(propType)
.GetMethod("CopyFrom", new [] { enumerableType, typeof(bool) })!;
.GetMethod("CopyFrom", [ enumerableType, typeof(bool) ])!;
return (
Lambda(
Convert(Call(createMethod, currentControlParameter, Constant(pgroup)), type),
Convert(Call(createMethod, currentControlParameter, Constant(pgroup.Id)), type),
currentControlParameter
),
Lambda(
Call(
New(ctor, currentControlParameter, Constant(pgroup)),
New(ctor, currentControlParameter, Constant(pgroup.Id), Constant(pgroup.IsBindingProperty)),
copyFromMethod,
Convert(valueParameter, enumerableType),
Constant(true) // clear
Expand All @@ -105,36 +108,42 @@ public static (LambdaExpression getter, LambdaExpression setter) CreatePropertyG
valueParameter
)
);

}

static readonly ConstructorInfo DotvvmPropertyIdConstructor = typeof(DotvvmPropertyId).GetConstructor(new [] { typeof(uint) }).NotNull();

public static (LambdaExpression getter, LambdaExpression setter) CreatePropertyAccessors(Type type, DotvvmProperty property)
{
if (property is DotvvmPropertyAlias propertyAlias)
return CreatePropertyAccessors(type, propertyAlias.Aliased);

// if the property does not override GetValue/SetValue, we'll use
// control.properties dictionary directly to avoid virtual method calls
var canUseDirectAccess =
!property.IsValueInherited && (
property.GetType() == typeof(DotvvmProperty) ||
property.GetType().GetMethod(nameof(DotvvmProperty.GetValue), new [] { typeof(DotvvmBindableObject), typeof(bool) })!.DeclaringType == typeof(DotvvmProperty) &&
property.GetType().GetMethod(nameof(DotvvmProperty.SetValue), new [] { typeof(DotvvmBindableObject), typeof(object) })!.DeclaringType == typeof(DotvvmProperty));
var canUseDirectAccess = !property.IsValueInherited && DotvvmPropertyIdAssignment.TypeCanUseAnyDirectAccess(property.GetType());

var valueParameter = Expression.Parameter(type, "value");
var unwrappedType = type.UnwrapNullableType();

var defaultObj = TypeConversion.BoxToObject(Constant(property.DefaultValue));
// try to access the readonly static field, as .NET can optimize that better than whatever Linq.Expression Constant compiles to
var propertyExpr =
property.AttributeProvider is FieldInfo field && field.IsStatic && field.IsInitOnly && field.GetValue(null) == property
? Field(null, field)
: (Expression)Constant(property);
var propertyIdExpr = New(DotvvmPropertyIdConstructor, Constant(property.Id.Id, typeof(uint)));

var boxedValueParameter = TypeConversion.BoxToObject(valueParameter);
var setValueRaw =
canUseDirectAccess
? Call(typeof(Helpers), nameof(Helpers.SetValueDirect), Type.EmptyTypes, currentControlParameter, Constant(property), boxedValueParameter)
: Call(currentControlParameter, nameof(DotvvmBindableObject.SetValueRaw), Type.EmptyTypes, Constant(property), boxedValueParameter);
? Call(typeof(Helpers), nameof(Helpers.SetValueDirect), Type.EmptyTypes, currentControlParameter, propertyIdExpr, defaultObj, boxedValueParameter)
: Call(currentControlParameter, nameof(DotvvmBindableObject.SetValueRaw), Type.EmptyTypes, propertyExpr, boxedValueParameter);

if (typeof(IBinding).IsAssignableFrom(type))
{
var getValueRaw =
canUseDirectAccess
? Call(typeof(Helpers), nameof(Helpers.GetValueRawDirect), Type.EmptyTypes, currentControlParameter, Constant(property))
: Call(currentControlParameter, nameof(DotvvmBindableObject.GetValueRaw), Type.EmptyTypes, Constant(property), Constant(property.IsValueInherited));
? Call(typeof(Helpers), nameof(Helpers.GetValueRawDirect), Type.EmptyTypes, currentControlParameter, propertyIdExpr, defaultObj)
: Call(currentControlParameter, nameof(DotvvmBindableObject.GetValueRaw), Type.EmptyTypes, propertyExpr, Constant(property.IsValueInherited));
return (
Lambda(
Convert(getValueRaw, type),
Expand Down Expand Up @@ -173,11 +182,17 @@ public static (LambdaExpression getter, LambdaExpression setter) CreatePropertyA
Expression.Call(
getValueOrBindingMethod,
currentControlParameter,
Constant(property)),
canUseDirectAccess ? propertyIdExpr : propertyExpr,
defaultObj),
currentControlParameter
),
Expression.Lambda(
Expression.Call(setValueOrBindingMethod, currentControlParameter, Expression.Constant(property), valueParameter),
Expression.Call(
setValueOrBindingMethod,
currentControlParameter,
canUseDirectAccess ? propertyIdExpr : propertyExpr,
defaultObj,
valueParameter),
currentControlParameter, valueParameter
)
);
Expand All @@ -191,13 +206,13 @@ public static (LambdaExpression getter, LambdaExpression setter) CreatePropertyA
Expression getValue;
if (canUseDirectAccess && unwrappedType.IsValueType)
{
getValue = Call(typeof(Helpers), nameof(Helpers.GetStructValueDirect), new Type[] { unwrappedType }, currentControlParameter, Constant(property));
getValue = Call(typeof(Helpers), nameof(Helpers.GetStructValueDirect), [ unwrappedType ], currentControlParameter, propertyIdExpr, Constant(property.DefaultValue, type.MakeNullableType()));
if (!type.IsNullable())
getValue = Expression.Property(getValue, "Value");
}
else
{
getValue = Call(currentControlParameter, getValueMethod, Constant(property), Constant(property.IsValueInherited));
getValue = Call(currentControlParameter, getValueMethod, propertyExpr, Constant(property.IsValueInherited));
getValue = Convert(getValue, type);
}
return (
Expand Down
Loading
Loading