public static bool CustomValueConverter(AstTransformationContext context, IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result) { if (type.FullName == "System.TimeSpan" && node is XamlAstTextNode tn && !tn.Text.Contains(":")) { var seconds = double.Parse(tn.Text, CultureInfo.InvariantCulture); result = new XamlStaticOrTargetedReturnMethodCallNode(tn, type.FindMethod("FromSeconds", type, false, context.Configuration.WellKnownTypes.Double), new[] { new XamlConstantNode(tn, context.Configuration.WellKnownTypes.Double, seconds) }); return(true); } if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault(); if (scope == null) { throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node); } if (!(node is XamlAstTextNode text)) { throw new XamlX.XamlLoadException("Property should be a text node", node); } result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text.Text, scope.TargetType, text); return(true); } result = null; return(false); }
public bool EmitCall(IXamlPropertySetter setter, IXamlILEmitter emitter) { if (setter is XamlDirectSetter xdirect) { var paramType = setter.Parameters[0]; var expectedParameters = new [] { xdirect.WinUITypes.IXamlDirectObject, xdirect.WinUITypes.XamlPropertyIndex, paramType }; IXamlType xamlDirectType = xdirect.WinUITypes.XamlDirect; var setterMethod = xamlDirectType .FindMethod(m => !m.IsStatic && m.Name.StartsWith("Set") && m.ReturnType == emitter.TypeSystem.FindType("System.Void") && m.Parameters.SequenceEqual(expectedParameters)); using (var objLocal = emitter.LocalsPool.GetLocal(xdirect.WinUITypes.IXamlDirectObject)) using (var valLocal = emitter.LocalsPool.GetLocal(paramType)) { emitter .Stloc(valLocal.Local) .Stloc(objLocal.Local) .EmitCall(xamlDirectType.GetMethod(new FindMethodMethodSignature("GetDefault", xamlDirectType) { IsStatic = true })) .Ldloc(objLocal.Local) .Ldc_I4((int)xdirect.PropertyIndex.GetLiteralValue()) .Ldloc(valLocal.Local) .EmitCall(setterMethod); } return(true); } return(false); }
public static bool CustomValueConverter(AstTransformationContext context, IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result) { if (!(node is XamlAstTextNode textNode)) { result = null; return(false); } var text = textNode.Text; var types = context.GetAvaloniaTypes(); if (type.FullName == "System.TimeSpan") { var tsText = text.Trim(); if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) { // // shorthand seconds format (ie. "0.25") if (!tsText.Contains(":") && double.TryParse(tsText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var seconds)) { timeSpan = TimeSpan.FromSeconds(seconds); } else { throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); } } result = new XamlStaticOrTargetedReturnMethodCallNode(node, type.FindMethod("FromTicks", type, false, types.Long), new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); return(true); } if (type.Equals(types.FontFamily)) { result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return(true); } if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault(); if (scope == null) { throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node); } result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node); return(true); } result = null; return(false); }
public bool EmitCall(IXamlPropertySetter setter, IXamlILEmitter emitter) { if (setter is XamlDirectAdderSetter xdirect) { var paramType = setter.Parameters[0]; IXamlType xamlDirectType = xdirect.WinUITypes.XamlDirect; var getCollectionMethod = xamlDirectType.GetMethod( new FindMethodMethodSignature("GetXamlDirectObjectProperty", xdirect.WinUITypes.IXamlDirectObject, xdirect.WinUITypes.IXamlDirectObject, xdirect.WinUITypes.XamlPropertyIndex)); var addToCollection = xamlDirectType.GetMethod( new FindMethodMethodSignature("AddToCollection", emitter.TypeSystem.GetType("System.Void"), xdirect.WinUITypes.IXamlDirectObject, xdirect.WinUITypes.IXamlDirectObject)); using (var objLocal = emitter.LocalsPool.GetLocal(xdirect.WinUITypes.IXamlDirectObject)) using (var valLocal = emitter.LocalsPool.GetLocal(paramType)) { emitter .Stloc(valLocal.Local) .Stloc(objLocal.Local) .EmitCall(xamlDirectType.GetMethod(new FindMethodMethodSignature("GetDefault", xamlDirectType) { IsStatic = true })) .Dup(); emitter .Ldloc(objLocal.Local) .Ldc_I4((int)xdirect.PropertyIndex.GetLiteralValue()) .EmitCall(getCollectionMethod); if (paramType != xdirect.WinUITypes.IXamlDirectObject) { emitter .EmitCall(xamlDirectType.GetMethod(new FindMethodMethodSignature("GetDefault", xamlDirectType) { IsStatic = true })) .Ldloc(valLocal.Local) .EmitCall(xamlDirectType.FindMethod(m => m.Name == "GetXamlDirectObject")); } else { emitter.Ldloc(valLocal.Local); } emitter .EmitCall(addToCollection); } return(true); } return(false); }
private IXamlMethodBuilder <IXamlILEmitter> ImplementInterfacePropertyGetter(IXamlTypeBuilder <IXamlILEmitter> builder, IXamlType type, string name) { var prefix = type.Namespace + "." + type.Name + "."; var originalGetter = type.FindMethod(m => m.Name == "get_" + name); var gen = builder.DefineMethod(originalGetter.ReturnType, new IXamlType[0], prefix + "get_" + name, false, false, true, originalGetter); builder.DefineProperty(originalGetter.ReturnType, prefix + name, null, gen); return(gen); }
public static bool TryConvert(AstTransformationContext context, IXamlAstValueNode node, string text, IXamlType type, AvaloniaXamlIlWellKnownTypes types, out IXamlAstValueNode result) { if (type.FullName == "System.TimeSpan") { var tsText = text.Trim(); if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) { // // shorthand seconds format (ie. "0.25") if (!tsText.Contains(":") && double.TryParse(tsText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var seconds)) { timeSpan = TimeSpan.FromSeconds(seconds); } else { throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); } } result = new XamlStaticOrTargetedReturnMethodCallNode(node, type.FindMethod("FromTicks", type, false, types.Long), new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); return(true); } if (type.Equals(types.FontFamily)) { result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return(true); } if (type.Equals(types.Thickness)) { try { var thickness = Thickness.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a thickness", node); } } if (type.Equals(types.Point)) { try { var point = Point.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, new[] { point.X, point.Y }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a point", node); } } if (type.Equals(types.Vector)) { try { var vector = Vector.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, new[] { vector.X, vector.Y }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a vector", node); } } if (type.Equals(types.Size)) { try { var size = Size.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, new[] { size.Width, size.Height }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a size", node); } } if (type.Equals(types.Matrix)) { try { var matrix = Matrix.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a matrix", node); } } if (type.Equals(types.CornerRadius)) { try { var cornerRadius = CornerRadius.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a corner radius", node); } } if (type.Equals(types.Color)) { if (!Color.TryParse(text, out Color color)) { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a color", node); } result = new XamlStaticOrTargetedReturnMethodCallNode(node, type.GetMethod( new FindMethodMethodSignature("FromUInt32", type, types.UInt) { IsStatic = true }), new[] { new XamlConstantNode(node, types.UInt, color.ToUint32()) }); return(true); } if (type.Equals(types.GridLength)) { try { var gridLength = GridLength.Parse(text); result = new AvaloniaXamlIlGridLengthAstNode(node, types, gridLength); return(true); } catch { throw new XamlX.XamlLoadException($"Unable to parse \"{text}\" as a grid length", node); } } if (type.Equals(types.Cursor)) { if (TypeSystemHelpers.TryGetEnumValueNode(types.StandardCursorType, text, node, out var enumConstantNode)) { var cursorTypeRef = new XamlAstClrTypeReference(node, types.Cursor, false); result = new XamlAstNewClrObjectNode(node, cursorTypeRef, types.CursorTypeConstructor, new List <IXamlAstValueNode> { enumConstantNode }); return(true); } } if (type.Equals(types.ColumnDefinitions)) { return(ConvertDefinitionList(node, text, types, types.ColumnDefinitions, types.ColumnDefinition, "column definitions", out result)); } if (type.Equals(types.RowDefinitions)) { return(ConvertDefinitionList(node, text, types, types.RowDefinitions, types.RowDefinition, "row definitions", out result)); } if (type.Equals(types.Classes)) { var classes = text.Split(' '); var classNodes = classes.Select(c => new XamlAstTextNode(node, c, types.XamlIlTypes.String)).ToArray(); result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, types.Classes, types.XamlIlTypes.String, classNodes); return(true); } result = null; return(false); }
public static bool CustomValueConverter(AstTransformationContext context, IXamlAstValueNode node, IXamlType type, out IXamlAstValueNode result) { if (!(node is XamlAstTextNode textNode)) { result = null; return(false); } var text = textNode.Text; var types = context.GetAvaloniaTypes(); if (type.FullName == "System.TimeSpan") { var tsText = text.Trim(); if (!TimeSpan.TryParse(tsText, CultureInfo.InvariantCulture, out var timeSpan)) { // // shorthand seconds format (ie. "0.25") if (!tsText.Contains(":") && double.TryParse(tsText, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out var seconds)) { timeSpan = TimeSpan.FromSeconds(seconds); } else { throw new XamlX.XamlLoadException($"Unable to parse {text} as a time span", node); } } result = new XamlStaticOrTargetedReturnMethodCallNode(node, type.FindMethod("FromTicks", type, false, types.Long), new[] { new XamlConstantNode(node, types.Long, timeSpan.Ticks) }); return(true); } if (type.Equals(types.FontFamily)) { result = new AvaloniaXamlIlFontFamilyAstNode(types, text, node); return(true); } if (type.Equals(types.Thickness)) { var thickness = Thickness.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Thickness, types.ThicknessFullConstructor, new[] { thickness.Left, thickness.Top, thickness.Right, thickness.Bottom }); return(true); } if (type.Equals(types.Point)) { var point = Point.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Point, types.PointFullConstructor, new[] { point.X, point.Y }); return(true); } if (type.Equals(types.Vector)) { var vector = Vector.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Vector, types.VectorFullConstructor, new[] { vector.X, vector.Y }); return(true); } if (type.Equals(types.Size)) { var size = Size.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Size, types.SizeFullConstructor, new[] { size.Width, size.Height }); return(true); } if (type.Equals(types.Matrix)) { var matrix = Matrix.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.Matrix, types.MatrixFullConstructor, new[] { matrix.M11, matrix.M12, matrix.M21, matrix.M22, matrix.M31, matrix.M32 }); return(true); } if (type.Equals(types.CornerRadius)) { var cornerRadius = CornerRadius.Parse(text); result = new AvaloniaXamlIlVectorLikeConstantAstNode(node, types, types.CornerRadius, types.CornerRadiusFullConstructor, new[] { cornerRadius.TopLeft, cornerRadius.TopRight, cornerRadius.BottomRight, cornerRadius.BottomLeft }); return(true); } if (type.FullName == "Avalonia.AvaloniaProperty") { var scope = context.ParentNodes().OfType <AvaloniaXamlIlTargetTypeMetadataNode>().FirstOrDefault(); if (scope == null) { throw new XamlX.XamlLoadException("Unable to find the parent scope for AvaloniaProperty lookup", node); } result = XamlIlAvaloniaPropertyHelper.CreateNode(context, text, scope.TargetType, node); return(true); } result = null; return(false); }
public static bool TryConvertValue(AstTransformationContext context, IXamlAstValueNode node, IXamlType type, XamlAstClrProperty propertyContext, out IXamlAstValueNode rv) { rv = null; var cfg = context.Configuration; // Since we are doing a conversion anyway, it makes sense to check for the underlying nullable type if (type.GenericTypeDefinition?.Equals(cfg.WellKnownTypes.NullableT) == true) { type = type.GenericArguments[0]; } if (cfg.CustomValueConverter?.Invoke(context, node, type, out rv) == true) { return(true); } var nodeType = node.Type.GetClrType(); // Implicit type converters if (!nodeType.Equals(cfg.WellKnownTypes.String)) { return(false); } if (node is XamlAstTextNode tn) { if (type.IsEnum) { if (TypeSystemHelpers.TryGetEnumValueNode(type, tn.Text, tn, out var enumConstantNode)) { rv = enumConstantNode; return(true); } } // Well known types if (TypeSystemHelpers.ParseConstantIfTypeAllows(tn.Text, type, tn, out var constantNode)) { rv = constantNode; return(true); } if (type.FullName == "System.Type") { var resolvedType = TypeReferenceResolver.ResolveType(context, tn.Text, false, tn, true); rv = new XamlTypeExtensionNode(tn, resolvedType, type); return(true); } if (cfg.WellKnownTypes.Delegate.IsAssignableFrom(type)) { var invoke = type.FindMethod(m => m.Name == "Invoke"); var rootType = context.RootObject.Type.GetClrType(); var handler = rootType.FindMethod(tn.Text, invoke.ReturnType, false, invoke.Parameters.ToArray()); if (handler != null) { rv = new XamlLoadMethodDelegateNode(tn, context.RootObject, type, handler); return(true); } } } IXamlAstValueNode CreateInvariantCulture() => new XamlStaticOrTargetedReturnMethodCallNode(node, cfg.WellKnownTypes.CultureInfo.Methods.First(x => x.IsPublic && x.IsStatic && x.Name == "get_InvariantCulture"), null); var candidates = type.Methods.Where(m => m.Name == "Parse" && m.ReturnType.Equals(type) && m.Parameters.Count > 0 && m.Parameters[0].Equals(cfg.WellKnownTypes.String)).ToList(); // Types with parse method var parser = candidates.FirstOrDefault(m => m.Parameters.Count == 2 && ( m.Parameters[1].Equals(cfg.WellKnownTypes.CultureInfo) || m.Parameters[1].Equals(cfg.WellKnownTypes.IFormatProvider) ) ) ?? candidates.FirstOrDefault(m => m.Parameters.Count == 1); if (parser != null) { var args = new List <IXamlAstValueNode> { node }; if (parser.Parameters.Count == 2) { args.Add(CreateInvariantCulture()); } rv = new XamlStaticOrTargetedReturnMethodCallNode(node, parser, args); return(true); } if (cfg.TypeMappings.TypeDescriptorContext != null) { IXamlType converterType = null; if (propertyContext?.TypeConverters.TryGetValue(type, out converterType) != true) { var typeConverterAttribute = cfg.GetCustomAttribute(type, cfg.TypeMappings.TypeConverterAttributes).FirstOrDefault(); if (typeConverterAttribute != null) { converterType = TryGetTypeConverterFromCustomAttribute(cfg, typeConverterAttribute); } } if (converterType != null) { var converterMethod = converterType.FindMethod("ConvertFrom", cfg.WellKnownTypes.Object, false, cfg.TypeMappings.TypeDescriptorContext, cfg.WellKnownTypes.CultureInfo, cfg.WellKnownTypes.Object); rv = new XamlAstNeedsParentStackValueNode(node, new XamlAstRuntimeCastNode(node, new XamlStaticOrTargetedReturnMethodCallNode(node, converterMethod, new[] { new XamlAstNewClrObjectNode(node, new XamlAstClrTypeReference(node, converterType, false), null, new List <IXamlAstValueNode>()), new XamlAstContextLocalNode(node, cfg.TypeMappings.TypeDescriptorContext), CreateInvariantCulture(), node }), new XamlAstClrTypeReference(node, type, false))); return(true); } } return(false); }
public static void EmitConvert(XamlEmitContextWithLocals <IXamlILEmitter, XamlILNodeEmitResult> context, IXamlLineInfo node, IXamlType what, IXamlType to, Func <bool, IXamlILEmitter> ld) { if (what.Equals(to)) { ld(false); } else if (what == XamlPseudoType.Null) { if (to.IsValueType) { if (to.GenericTypeDefinition?.Equals(context.Configuration.WellKnownTypes.NullableT) == true) { using (var loc = context.GetLocalOfType(to)) ld(false) .Pop() .Ldloca(loc.Local) .Emit(OpCodes.Initobj, to) .Ldloc(loc.Local); } else { throw new XamlLoadException("Unable to convert {x:Null} to " + to.GetFqn(), node); } } else { ld(false); } } else if (what.IsValueType && to.IsValueType) { if (to.IsNullableOf(what)) { ld(false).Emit(OpCodes.Newobj, to.Constructors.First(c => c.Parameters.Count == 1 && c.Parameters[0].Equals(what))); } else if (what.IsNullableOf(what)) { ld(true) .EmitCall(what.FindMethod(m => m.Name == "get_Value")); } else { throw new XamlLoadException( $"Don't know how to convert value type {what.GetFullName()} to value type {to.GetFullName()}", node); } } else if (!to.IsValueType && what.IsValueType) { if (!to.IsAssignableFrom(what)) { throw new XamlLoadException( $"Don't know how to convert value type {what.GetFullName()} to reference type {to.GetFullName()}", node); } ld(false).Box(what); } else if (to.IsValueType && !what.IsValueType) { if (!(what.Namespace == "System" && what.Name == "Object")) { throw new XamlLoadException( $"Don't know how to convert reference type {what.GetFullName()} to value type {to.GetFullName()}", node); } ld(false).Unbox_Any(to); } else { if (to.IsAssignableFrom(what)) { // Downcast, always safe ld(false); } else if (what.IsInterface || what.IsAssignableFrom(to)) { // Upcast or cast from interface, might throw InvalidCastException ld(false).Emit(OpCodes.Castclass, to); } else { // Types are completely unrelated, e. g. string to List<int> conversion attempt throw new XamlLoadException( $"Don't know how to convert reference type {what.GetFullName()} to reference type {to.GetFullName()}", node); } } }