public XamlIlNodeEmitResult Emit(IXamlIlAstNode value, IXamlIlCodeGen codeGen, IXamlIlType expectedType) { var res = EmitCore(value, codeGen); 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 (!expectedType.IsAssignableFrom(returnedType)) { throw new XamlIlLoadException( $"Emit of node {value} resulted in {returnedType.GetFqn()} which is not convertible to expected {expectedType.GetFqn()}", value); } if (returnedType.IsValueType && !expectedType.IsValueType) { codeGen.Generator.Emit(OpCodes.Box, returnedType); } } return(res); }
public static bool IsDirectlyAssignableFrom(this IXamlIlType type, IXamlIlType other) { if (type.IsValueType || other.IsValueType) { return(type.Equals(other)); } return(type.IsAssignableFrom(other)); }
public static bool TryGetCorrectlyTypedValue(XamlIlAstTransformationContext context, IXamlIlAstValueNode node, IXamlIlType type, out IXamlIlAstValueNode rv) { if (type.IsAssignableFrom(node.Type.GetClrType())) { rv = node; return(true); } return(TryConvertValue(context, node, type, null, out rv)); }
public static void EmitConvert(XamlIlEmitContext context, IXamlIlLineInfo node, IXamlIlType what, IXamlIlType to, Func <bool, IXamlIlEmitter> ld) { if (what.Equals(to)) { ld(false); } else if (what == XamlIlPseudoType.Null) { if (to.IsValueType) { if (to.GenericTypeDefinition?.Equals(context.Configuration.WellKnownTypes.NullableT) == true) { using (var loc = context.GetLocal(to)) ld(false) .Pop() .Ldloca(loc.Local) .Emit(OpCodes.Initobj, to) .Ldloc(loc.Local); } else { throw new XamlIlLoadException("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 XamlIlLoadException( $"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 XamlIlLoadException( $"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 XamlIlLoadException( $"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 XamlIlLoadException( $"Don't know how to convert reference type {what.GetFullName()} to reference type {to.GetFullName()}", node); } } }
public static bool TryGetCorrectlyTypedValue(XamlIlAstTransformationContext context, IXamlIlAstValueNode node, IXamlIlType type, out IXamlIlAstValueNode rv) { var cfg = context.Configuration; rv = null; if (type.IsAssignableFrom(node.Type.GetClrType())) { rv = node; return(true); } // 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 XamlIlAstTextNode tn) { if (type.IsEnum) { var enumValue = type.Fields.FirstOrDefault(f => f.Name == tn.Text); if (enumValue != null) { rv = TypeSystemHelpers.GetLiteralFieldConstantNode(enumValue, node); 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 = XamlIlTypeReferenceResolver.ResolveType(context, tn.Text, tn, true); rv = new XamlIlTypeExtensionNode(tn, new XamlIlAstClrTypeReference(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 XamlIlLoadMethodDelegateNode(tn, context.RootObject, type, handler); return(true); } } } IXamlIlAstValueNode CreateInvariantCulture() => new XamlIlStaticOrTargetedReturnMethodCallNode(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 <IXamlIlAstValueNode> { node }; if (parser.Parameters.Count == 2) { args.Add(CreateInvariantCulture()); } rv = new XamlIlStaticOrTargetedReturnMethodCallNode(node, parser, args); return(true); } if (cfg.TypeMappings.TypeDescriptorContext != null) { var typeConverterAttribute = cfg.GetCustomAttribute(type, cfg.TypeMappings.TypeConverterAttributes).FirstOrDefault(); if (typeConverterAttribute != null) { var arg = typeConverterAttribute.Parameters.FirstOrDefault(); var converterType = (arg as IXamlIlType) ?? (arg is String sarg ? cfg.TypeSystem.FindType(sarg) : null); if (converterType != null) { var converterMethod = converterType.FindMethod("ConvertFrom", cfg.WellKnownTypes.Object, false, cfg.TypeMappings.TypeDescriptorContext, cfg.WellKnownTypes.CultureInfo, cfg.WellKnownTypes.Object); rv = new XamlIlAstNeedsParentStackValueNode(node, new XamlIlAstRuntimeCastNode(node, new XamlIlStaticOrTargetedReturnMethodCallNode(node, converterMethod, new[] { new XamlIlAstNewClrObjectNode(node, converterType, null, new List <IXamlIlAstValueNode>()), new XamlIlAstContextLocalNode(node, cfg.TypeMappings.TypeDescriptorContext), CreateInvariantCulture(), node }), new XamlIlAstClrTypeReference(node, type))); return(true); } } } return(false); }