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())); } }
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); } }
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); } }
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); }
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); }
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)); }
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); }
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)); }