public XamlIlNodeEmitResult Emit(IXamlIlAstNode node, XamlIlEmitContext context, IXamlIlEmitter ilgen) { if (!(node is XamlIlMarkupExtensionNode me)) { return(null); } XamlIlNeedsParentStackCache.Verify(context, node); var prop = context.ParentNodes().OfType <XamlIlPropertyAssignmentNode>().FirstOrDefault(); var needProvideValueTarget = me.ProvideValue.Parameters.Count != 0 && context.RuntimeContext.PropertyTargetObject != null && prop != null; void EmitPropertyDescriptor() { if (context.Configuration.TypeMappings.ProvideValueTargetPropertyEmitter ?.Invoke(context, ilgen, prop.Property) == true) { return; } ilgen.Ldstr(prop.Property.Name); } context.Emit(me.Value, ilgen, me.Value.Type.GetClrType()); if (me.ProvideValue.Parameters.Count > 0) { ilgen .Emit(OpCodes.Ldloc, context.ContextLocal); } if (needProvideValueTarget) { ilgen .Ldloc(context.ContextLocal); EmitPropertyDescriptor(); ilgen .Stfld(context.RuntimeContext.PropertyTargetProperty); } ilgen.EmitCall(me.ProvideValue); if (needProvideValueTarget) { ilgen .Ldloc(context.ContextLocal) .Ldnull() .Stfld(context.RuntimeContext.PropertyTargetProperty); } return(XamlIlNodeEmitResult.Type(0, me.ProvideValue.ReturnType)); }
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)); }