public static void EmitFieldLiteral(IXamlField field, IXamlILEmitter codeGen)
        {
            var ftype = field.FieldType.IsEnum ? field.FieldType.GetEnumUnderlyingType() : field.FieldType;

            if (ftype.Name == "UInt64" || ftype.Name == "Int64")
            {
                codeGen.Emit(OpCodes.Ldc_I8,
                             TypeSystemHelpers.ConvertLiteralToLong(field.GetLiteralValue()));
            }
            else if (ftype.Name == "Double")
            {
                codeGen.Emit(OpCodes.Ldc_R8, (double)field.GetLiteralValue());
            }
            else if (ftype.Name == "Single")
            {
                codeGen.Emit(OpCodes.Ldc_R4, (float)field.GetLiteralValue());
            }
            else if (ftype.Name == "String")
            {
                codeGen.Emit(OpCodes.Ldstr, (string)field.GetLiteralValue());
            }
            else
            {
                codeGen.Emit(OpCodes.Ldc_I4,
                             TypeSystemHelpers.ConvertLiteralToInt(field.GetLiteralValue()));
            }
        }
Example #2
0
        public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
        {
            var type   = TargetType.GetClrType();
            var member = ResolveMember(type);

            if (member is IXamlIlProperty prop)
            {
                codeGen.Emit(OpCodes.Call, prop.Getter);
                return(XamlIlNodeEmitResult.Type(0, prop.Getter.ReturnType));
            }
            else if (member is IXamlIlField field)
            {
                if (field.IsLiteral)
                {
                    TypeSystemHelpers.EmitFieldLiteral(field, codeGen);
                }
                else
                {
                    codeGen.Emit(OpCodes.Ldsfld, field);
                }
                return(XamlIlNodeEmitResult.Type(0, field.FieldType));
            }
            else
            {
                throw new XamlIlLoadException(
                          $"Unable to resolve {Member} as static field, property, constant or enum value", this);
            }
        }
Example #3
0
        public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlCodeGen codeGen)
        {
            var type   = TargetType.GetClrType();
            var member = ResolveMember(type);

            if (member is IXamlIlProperty prop)
            {
                codeGen.Generator.Emit(OpCodes.Call, prop.Getter);
                return(XamlIlNodeEmitResult.Type(prop.Getter.ReturnType));
            }
            else if (member is IXamlIlField field)
            {
                if (field.IsLiteral)
                {
                    var ftype = field.FieldType.IsEnum ? field.FieldType.GetEnumUnderlyingType() : field.FieldType;

                    if (ftype.Name == "UInt64" || ftype.Name == "Int64")
                    {
                        codeGen.Generator.Emit(OpCodes.Ldc_I8,
                                               TypeSystemHelpers.ConvertLiteralToLong(field.GetLiteralValue()));
                    }
                    else if (ftype.Name == "Double")
                    {
                        codeGen.Generator.Emit(OpCodes.Ldc_R8, (double)field.GetLiteralValue());
                    }
                    else if (ftype.Name == "Single")
                    {
                        codeGen.Generator.Emit(OpCodes.Ldc_R4, (float)field.GetLiteralValue());
                    }
                    else if (ftype.Name == "String")
                    {
                        codeGen.Generator.Emit(OpCodes.Ldstr, (string)field.GetLiteralValue());
                    }
                    else
                    {
                        codeGen.Generator.Emit(OpCodes.Ldc_I4,
                                               TypeSystemHelpers.ConvertLiteralToInt(field.GetLiteralValue()));
                    }
                }
                else
                {
                    codeGen.Generator.Emit(OpCodes.Ldsfld, field);
                }
                return(XamlIlNodeEmitResult.Type(field.FieldType));
            }
            else
            {
                throw new XamlIlLoadException(
                          $"Unable to resolve {Member} as static field, property, constant or enum value", this);
            }
        }
Example #4
0
 public XamlIlNodeEmitResult Emit(XamlIlEmitContext context, IXamlIlEmitter codeGen)
 {
     if (Constant is string)
     {
         codeGen.Emit(OpCodes.Ldstr, (string)Constant);
     }
     else if (Constant is long || Constant is ulong)
     {
         codeGen.Emit(OpCodes.Ldc_I8, TypeSystemHelpers.ConvertLiteralToLong(Constant));
     }
     else if (Constant is float f)
     {
         codeGen.Emit(OpCodes.Ldc_R4, f);
     }
     else if (Constant is double d)
     {
         codeGen.Emit(OpCodes.Ldc_R8, d);
     }
     else
     {
         codeGen.Emit(OpCodes.Ldc_I4, TypeSystemHelpers.ConvertLiteralToInt(Constant));
     }
     return(XamlIlNodeEmitResult.Type(0, Type.GetClrType()));
 }
        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);
        }
Example #6
0
        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);
        }
Example #7
0
        private XamlIlNodeEmitResult EmitCore(IXamlIlAstNode value, IXamlIlEmitter codeGen, IXamlIlType expectedType)
        {
            CheckingIlEmitter parent         = null;
            CheckingIlEmitter checkedEmitter = null;

            if (EnableIlVerification)
            {
                parent = codeGen as CheckingIlEmitter;

                parent?.Pause();
                checkedEmitter = new CheckingIlEmitter(codeGen);
            }
#if XAMLIL_DEBUG
            var res = EmitNode(value, checkedEmitter);
#else
            XamlIlNodeEmitResult res;
            try
            {
                res = EmitNode(value, checkedEmitter ?? codeGen);
            }
            catch (Exception e) when(!(e is XmlException))
            {
                throw new XamlIlLoadException(
                          "Internal compiler error while emitting node " + value + ":\n" + e, value);
            }
#endif
            if (EnableIlVerification)
            {
                var expectedBalance = res.ProducedItems - res.ConsumedItems;
                var checkResult     =
                    checkedEmitter.Check(res.ProducedItems - res.ConsumedItems, false);
                if (checkResult != null)
                {
                    throw new XamlIlLoadException($"Error during IL verification: {checkResult}\n{checkedEmitter}\n",
                                                  value);
                }
                parent?.Resume();
                parent?.ExplicitStack(expectedBalance);
            }

            var returnedType = res.ReturnType;

            if (returnedType != null || expectedType != null)
            {
                if (returnedType != null && expectedType == null)
                {
                    throw new XamlIlLoadException(
                              $"Emit of node {value} resulted in {returnedType.GetFqn()} while caller expected void", value);
                }

                if (expectedType != null && returnedType == null)
                {
                    throw new XamlIlLoadException(
                              $"Emit of node {value} resulted in void while caller expected {expectedType.GetFqn()}", value);
                }

                if (!returnedType.Equals(expectedType))
                {
                    XamlIlLocalsPool.PooledLocal local = null;
                    // ReSharper disable once ExpressionIsAlwaysNull
                    // Value is assigned inside the closure in certain conditions

                    TypeSystemHelpers.EmitConvert(this, value, returnedType, expectedType, ldaddr =>
                    {
                        if (ldaddr && returnedType.IsValueType)
                        {
                            // We need to store the value to a temporary variable, since *address*
                            // is required (probably for  method call on the value type)
                            local = GetLocal(returnedType);
                            codeGen
                            .Stloc(local.Local)
                            .Ldloca(local.Local);
                        }
                        // Otherwise do nothing, value is already at the top of the stack
                        return(codeGen);
                    });
                    local?.Dispose();
                }
            }


            return(res);
        }
        public XamlIlNodeEmitResult Emit(IXamlIlAstNode node, XamlIlEmitContext context, IXamlIlEmitter codeGen)
        {
            if (!(node is XamlIlPropertyAssignmentNode an))
            {
                return(null);
            }

            var setters = ValidateAndGetSetters(an);

            for (var c = 0; c < an.Values.Count - 1; c++)
            {
                context.Emit(an.Values[c], codeGen, an.Values[c].Type.GetClrType());
            }

            var value = an.Values.Last();

            var isValueType = value.Type.GetClrType().IsValueType;

            // If there is only one available setter or if value is a value type, always use the first one
            if (setters.Count == 1 || isValueType)
            {
                var setter = an.PossibleSetters.First();
                context.Emit(value, codeGen, setter.Parameters.Last());
                setter.Emit(codeGen);
            }
            else
            {
                var          checkedTypes = new List <IXamlIlType>();
                IXamlIlLabel exit         = codeGen.DefineLabel();
                IXamlIlLabel next         = null;
                var          hadJumps     = false;
                context.Emit(value, codeGen, value.Type.GetClrType());

                foreach (var setter in setters)
                {
                    var type = setter.Parameters.Last();

                    // We have already checked this type or its base type
                    if (checkedTypes.Any(ch => ch.IsAssignableFrom(type)))
                    {
                        continue;
                    }

                    if (next != null)
                    {
                        codeGen.MarkLabel(next);
                        next = null;
                    }

                    IXamlIlLabel Next() => next ?? (next = codeGen.DefineLabel());

                    var checkNext = false;
                    if (setter.BinderParameters.AllowRuntimeNull)
                    {
                        checkedTypes.Add(type);
                    }
                    else
                    {
                        // Check for null; Also don't add this type to the list of checked ones because of the null check
                        codeGen
                        .Dup()
                        .Brfalse(Next());
                        checkNext = true;
                    }

                    // Only do dynamic checks if we know that type is not assignable by downcast
                    if (!type.IsAssignableFrom(value.Type.GetClrType()))
                    {
                        codeGen
                        .Dup()
                        .Isinst(type)
                        .Brfalse(Next());
                        checkNext = true;
                    }

                    if (checkNext)
                    {
                        hadJumps = true;
                    }

                    TypeSystemHelpers.EmitConvert(context, codeGen, value, value.Type.GetClrType(), type);
                    setter.Emit(codeGen);
                    if (hadJumps)
                    {
                        codeGen.Br(exit);
                    }

                    if (!checkNext)
                    {
                        break;
                    }
                }

                if (next != null)
                {
                    codeGen.MarkLabel(next);

                    if (setters.Any(x => !x.BinderParameters.AllowRuntimeNull))
                    {
                        next = codeGen.DefineLabel();
                        codeGen
                        .Dup()
                        .Brtrue(next)
                        .Newobj(context.Configuration.TypeSystem.GetType("System.NullReferenceException")
                                .FindConstructor())
                        .Throw();
                        codeGen.MarkLabel(next);
                    }

                    codeGen
                    .Newobj(context.Configuration.TypeSystem.GetType("System.InvalidCastException")
                            .FindConstructor())
                    .Throw();
                }

                codeGen.MarkLabel(exit);
            }

            return(XamlIlNodeEmitResult.Void(1));
        }
Example #9
0
        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))
            {
                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.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);
        }
Example #10
0
        public XamlIlNodeEmitResult Emit(IXamlIlAstNode node, XamlIlEmitContext context, IXamlIlEmitter codeGen)
        {
            if (!(node is XamlIlMarkupExtensionNode me))
            {
                return(null);
            }
            XamlIlNeedsParentStackCache.Verify(context, node);
            var ilgen = codeGen;
            var so    = context.Configuration.WellKnownTypes.Object;
            var ptype = me.Manipulation?.ParametersWithThis[1] ?? me.Property.PropertyType;
            var rtype = me.ProvideValue?.ReturnType ?? me.Value.Type.GetClrType();
            var needProvideValueTarget = me.ProvideValue != null &&
                                         me.ProvideValue.Parameters.Count != 0 &&
                                         context.RuntimeContext.PropertyTargetObject != null &&
                                         me.Property != null;

            void EmitPropertyDescriptor()
            {
                if (me.Property == null)
                {
                    ilgen.Ldnull();
                }
                else if (context.Configuration.TypeMappings.ProvideValueTargetPropertyEmitter
                         ?.Invoke(context, codeGen, me.Property) == true)
                {
                    return;
                }
                else if (me.Property is XamlIlAstAttachedProperty)
                {
                    ilgen.Ldtoken(me.Property.Getter ?? me.Property.Setter)
                    .Emit(OpCodes.Box, context.Configuration.TypeSystem.GetType("System.RuntimeMethodHandle"));
                }
                else
                {
                    ilgen.Ldstr(me.Property?.Name);
                }
            }

            using (var resultLocalContainer = context.GetLocal(rtype))
            {
                var resultLocal = resultLocalContainer.Local;
                using (var targetObjectLocal = needProvideValueTarget ? context.GetLocal(so) : null)
                {
                    if (needProvideValueTarget)
                    {
                        ilgen
                        .Dup().Stloc(targetObjectLocal.Local);
                    }

                    context.Emit(me.Value, codeGen, me.Value.Type.GetClrType());
                    if (me.ProvideValue?.Parameters.Count > 0)
                    {
                        ilgen
                        .Emit(OpCodes.Ldloc, context.ContextLocal);
                    }

                    if (needProvideValueTarget)
                    {
                        ilgen
                        .Ldloc(context.ContextLocal)
                        .Ldloc(targetObjectLocal.Local)
                        .Stfld(context.RuntimeContext.PropertyTargetObject)
                        .Ldloc(context.ContextLocal);
                        EmitPropertyDescriptor();
                        ilgen
                        .Stfld(context.RuntimeContext.PropertyTargetProperty);
                    }

                    if (me.ProvideValue != null)
                    {
                        ilgen
                        .Emit(OpCodes.Call, me.ProvideValue);
                    }
                    ilgen
                    .Emit(OpCodes.Stloc, resultLocal);

                    if (needProvideValueTarget)
                    {
                        ilgen
                        .Ldloc(context.ContextLocal)
                        .Ldnull()
                        .Stfld(context.RuntimeContext.PropertyTargetObject)
                        .Ldloc(context.ContextLocal)
                        .Ldnull()
                        .Stfld(context.RuntimeContext.PropertyTargetProperty);
                    }
                }

                // At this point we have the target object at the top of the stack and markup extension result in resultLocal

                var exit = ilgen.DefineLabel();

                // This is needed for custom conversions of Binding to object
                var customTypes = context.Configuration.TypeMappings.MarkupExtensionCustomResultTypes;
                // This is needed for properties that accept Binding
                if (
                    me.Property != null &&
                    context.Configuration.TypeMappings.ShouldIgnoreMarkupExtensionCustomResultForProperty !=
                    null)
                {
                    customTypes = customTypes.Where(ct =>
                                                    !context.Configuration.TypeMappings
                                                    .ShouldIgnoreMarkupExtensionCustomResultForProperty(me.Property, ct))
                                  .ToList();
                }


                if (customTypes.Any() && !rtype.IsValueType)
                {
                    void EmitCustomActionCall()
                    {
                        EmitPropertyDescriptor();
                        codeGen
                        .Emit(OpCodes.Ldloc, context.ContextLocal)
                        .Emit(OpCodes.Ldloc, resultLocal);
                        if (rtype.IsValueType)
                        {
                            codeGen.Emit(OpCodes.Box, rtype);
                        }
                        codeGen
                        .Emit(OpCodes.Call, context.Configuration.TypeMappings.MarkupExtensionCustomResultHandler)
                        .Emit(OpCodes.Br, exit);
                    }

                    // Skip conversion attempts and call custom conversion directly
                    if (customTypes.Any(ct => ct.IsAssignableFrom(rtype)))
                    {
                        EmitCustomActionCall();
                        ilgen.MarkLabel(exit);
                        return(XamlIlNodeEmitResult.Void(1));
                    }

                    var callCustomLabel  = ilgen.DefineLabel();
                    var afterCustomLabel = ilgen.DefineLabel();
                    foreach (var ct in customTypes)
                    {
                        codeGen
                        .Ldloc(resultLocal)
                        .Isinst(ct)
                        .Brtrue(callCustomLabel);
                    }
                    ilgen
                    .Br(afterCustomLabel)
                    .MarkLabel(callCustomLabel);
                    EmitCustomActionCall();
                    ilgen.MarkLabel(afterCustomLabel);
                }


                TypeSystemHelpers.EmitConvert(context, node, rtype, ptype,
                                              lda => ilgen.Emit(lda ? OpCodes.Ldloca : OpCodes.Ldloc, resultLocal));

                // Call some method either on the target or on target's property
                if (me.Manipulation != null)
                {
                    // {target}.{Property}.{Method)(res)
                    if (me.Property != null)
                    {
                        using (var res = context.GetLocal(ptype))
                            ilgen
                            .Emit(OpCodes.Stloc, res.Local)
                            .EmitCall(me.Property.Getter)
                            .Emit(OpCodes.Ldloc, res.Local);
                    }
                    me.Manipulation.Emit(context, ilgen, true);
                }
                // Call property setter on the target
                else
                {
                    ilgen.EmitCall(me.Property.Setter);
                }

                ilgen.MarkLabel(exit);
            }

            return(XamlIlNodeEmitResult.Void(1));
        }