public ResolvedSignature(TypeInferenceContext ctx, MethodInfo signature) { Ctx = ctx; Signature = signature; if (Signature.IsOpenGeneric()) { throw new NotSupportedException(String.Format( "Cannot create a resolved signature from an open generic method '{0}'.", signature)); } }
public MethodGroupResolutionFailedException(JSToCSharpExceptionType type, MethodGroup methodGroup, Dictionary<MethodInfo, MethodResolutionException> failboats, TypeInferenceContext ctx, Exception innerException) : base(type, ctx.Root.AsArray().Concat(ctx.Root.Parents()).Last(), ctx.Root, innerException) { MethodGroup = methodGroup; Failboats = failboats; try { ActualArguments = ctx.Root.ToXArgs(ctx.Inferences).ToArray(); } catch (Exception) { // sigh, the client won't get full info from the crash site // sad, but true, so just ignore possible exceptions } }
private void InferMethodGroup(MethodGroup mg, RelinqScriptExpression root, TypeInferenceCache cache) { // q: should existing but not accessible (security!) methods be included into the resolution? // a: yes and here's why: // scenario 1. Included; when overload resolution binds to an unauthorized method = crash. // scenario 2. Not included, so overload resolution binds to an unexpected method = fail. // lets us the fail fast if the resolution is unnecessary. // inferences made here won't be wasted anyways since they get cached var ctx = new TypeInferenceContext(root, cache, Integration); var preview = new TypeInferenceEngine(ctx); root.CallArgs().ForEach(child => preview.InferTypes(child)); // we cannot pass the preview.Ctx inside since it might have // potentially half-inferred lambdas that won't be able to be reinferred // by MG resolution since ctx is init only. var resolved = mg.Resolve(ctx); cache.Add(root, resolved.Signature.ReturnType); cache.Invocations.Add(root, resolved); cache.Upgrade(resolved.Inferences); }
public TypeInferenceEngine(TypeInferenceContext external) { Ctx = external.Clone(); }
public TypeInferenceEngine(RelinqScriptExpression ast, IntegrationContext integration) { Ctx = new TypeInferenceContext(ast, new TypeInferenceCache(), integration); }
public MethodGroupResolutionFailedException(JSToCSharpExceptionType type, MethodGroup methodGroup, Dictionary<MethodInfo, MethodResolutionException> failboats, TypeInferenceContext ctx) : this(type, methodGroup, failboats, ctx, null) { }
public MethodMatchingException(MethodMatchingExceptionType type, MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceContext ctx, Exception innerException) : this(type, originalSignature, inferredSignature, ctx, null, innerException) { }
public MethodMatchingException(MethodMatchingExceptionType type, MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceContext ctx, int? mismatchIndex, Exception innerException) : base(originalSignature, inferredSignature, ctx, mismatchIndex, innerException) { Type = type; }
public MethodMatchingException(MethodMatchingExceptionType type, MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceContext ctx, int? mismatchIndex) : this(type, originalSignature, inferredSignature, ctx, mismatchIndex, null) { }
public static MethodInfo InferGenericTypeParams(this MethodInfo mi, TypeInferenceContext ctx) { var originalSignature = mi; TypeInferenceEngine preview = null; try { preview = new TypeInferenceEngine(ctx); ctx.Root.CallArgs().ForEach(child => preview.InferTypes(child)); var fail = Array.FindIndex(ctx.Root.CallArgs().ToArray(), c => preview.Inferences[c] is Variant); if (fail != -1) { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.VariantArg, originalSignature, mi, preview, fail); } else { Func<IEnumerable<RelinqScriptType>> args = () => ctx.Root.ToXArgs(preview.Inferences); Func<IEnumerable<Type>> @params = () => mi.ToXArgs(args().Count()); var argc = args().Count(); if (argc != @params().Count()) { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.ArgcMismatch, originalSignature, mi, preview); } else { var processed = new TrackableList<bool>(Enumerable.Repeat(false, argc)); var fresh = new List<bool>(Enumerable.Repeat(true, argc)); processed.ListChanged += (o, e) => fresh[e.Index] = true; while (fresh.Any(b => b) && !processed.All(b => b)) { Enumerable.Range(0, argc).ForEach(i => fresh[i] = false); foreach (var i in processed.ToArray() .Select((b, j) => new { b, j }).Where(_ => !_.b).Select(_ => _.j)) { var ai_r = args().ElementAt(i); var pi = @params().ElementAt(i); var argIndex = mi.IsExtension() ? i : i - 1; if (!pi.IsOpenGeneric()) { if (pi.IsFunctionType() && ai_r is Lambda) { var ai_s = ((Lambda)ai_r).Type.GetFunctionSignature(); var pi_s = pi.GetFunctionSignature(); // todo. support varargs here as well if (ai_s.GetParameters().Length == pi_s.GetParameters().Length) { var preview2 = new TypeInferenceEngine(ctx); // todo. the line below is only possible because we don't process lambda extension calls var callArg = ctx.Root.CallArgs().ElementAt(i - 1); preview2.Ctx.Inferences.Add(callArg, new Lambda( (LambdaExpression)callArg, (pi_s.GetParameters() .Select(pi2 => pi2.ParameterType.IsGenericParameter ? typeof(Variant) : pi2.ParameterType) .Concat(typeof(Variant).AsArray())) .ForgeFuncType())); try { preview2.InferTypes(callArg); } catch (RelinqException rex) { if (rex.IsUnexpected) { throw; } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.LambdaInconsistentBody, originalSignature, mi, preview, i, rex); } } catch (Exception ex) { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.Unexpected, originalSignature, mi, preview, i, ex); } var lambdaBody = ((LambdaExpression)callArg).Body; var retval = preview2.Inferences[lambdaBody]; if (retval is ClrType) { var ai_ret = ((ClrType)retval).Type; var pi_ret = pi_s.ReturnType; if (!ai_ret.HasImplicitCastTo(pi_ret)) { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.LambdaIncompatibleRetval, originalSignature, mi, preview, i); } } else { // lambdas that return lambdas or method groups // are not implemented, since they're not supported by C# spec. throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.LambdaInvalidRetval, originalSignature, mi, preview, i); } } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.LambdaArgcMismatch, originalSignature, mi, preview, i); } } processed[i] = true; } else { if (ai_r is ClrType) { var ai = ((ClrType)ai_r).Type; // todo. also check implicit casts here? var inf = ai.LookupInheritanceChain() .Select(aij => pi.InferFrom(aij)) .FirstOrDefault(inf2 => inf2 != null); if (inf != null) { mi = mi.InferStructuralUnits(inf.ToDictionary( kvp => "a" + argIndex + kvp.Key, kvp => kvp.Value)); if (mi != null) { processed[i] = true; } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.InferencesContradictGenericConstraints, originalSignature, mi, preview, i); } } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.FormalArgCannotBeInferredFromActualArg, originalSignature, mi, preview, i); } } else if (ai_r is Lambda) { if (pi.IsFunctionType()) { var ai_s = ((Lambda)ai_r).Type.GetFunctionSignature(); var pi_s = pi.GetFunctionSignature(); // todo. support varargs here as well if (ai_s.GetParameters().Length == pi_s.GetParameters().Length) { var preview2 = new TypeInferenceEngine(ctx); // todo. the line below is only possible because we don't process lambda extension calls var callArg = ctx.Root.CallArgs().ElementAt(i - 1); preview2.Ctx.Inferences.Add(callArg, new Lambda( (LambdaExpression)callArg, (pi_s.GetParameters() .Select(pi2 => pi2.ParameterType.IsGenericParameter ? typeof(Variant) : pi2.ParameterType) .Concat(typeof(Variant).AsArray())) .ForgeFuncType())); try { preview2.InferTypes(callArg); } catch (RelinqException rex) { if (rex.IsUnexpected) { throw; } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.LambdaInconsistentBody, originalSignature, mi, preview, i, rex); } } catch (Exception ex) { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.Unexpected, originalSignature, mi, preview, i, ex); } var lambdaBody = ((LambdaExpression)callArg).Body; var retval = preview2.Inferences[lambdaBody]; if (retval is ClrType) { var ai_ret = ((ClrType)retval).Type; var pi_ret = pi_s.ReturnType; if (pi_ret.IsOpenGeneric()) { var inf = ai_ret.LookupInheritanceChain() .Select(ai_retj => pi_ret.InferFrom(ai_retj)) .FirstOrDefault(inf2 => inf2 != null); if (inf != null) { var map = pi.GetStructuralTree(); mi = mi.InferStructuralUnits(inf.ToDictionary( kvp => "a" + argIndex + map.Single(kvp2 => kvp2.Value == pi_ret.SelectStructuralUnit(kvp.Key)).Key, kvp => kvp.Value)); if (mi != null) { processed[i] = true; } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.InferencesContradictGenericConstraints, originalSignature, mi, preview, i); } } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.FormalArgCannotBeInferredFromActualArg, originalSignature, mi, preview, i); } } else { processed[i] = true; } } else if (retval is Variant) { continue; } // lambdas that return lambdas or method groups // are not implemented, since they're not supported by C# spec. else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.LambdaInvalidRetval, originalSignature, mi, preview, i); } } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.LambdaArgcMismatch, originalSignature, mi, preview, i); } } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.FormalArgCannotBeInferredFromActualArg, originalSignature, mi, preview, i); } } // unlike lambdas, method groups don't participate in type args inference // neither they can help to infer type args, // nor can they be inferred themselves. else { processed[i] = true; } } } } if (!mi.IsOpenGeneric()) { return mi; } else { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.IncompleteInferenceSet, originalSignature, mi, preview); } } } } catch (GenericArgumentsInferenceException) { throw; } catch (Exception ex) { throw new GenericArgumentsInferenceException( GenericArgumentsInferenceExceptionType.Unexpected, originalSignature, mi, preview ?? new TypeInferenceEngine(ctx), ex); } }
// todo. resolve the method group -> delegate cast // and also provide generic arg inference altogether public static ResolvedInvocation Resolve(this MethodGroup mg, TypeInferenceContext ctx) { var failboats = new Dictionary<MethodInfo, MethodResolutionException>(); var infToOrig = new Dictionary<MethodInfo, MethodInfo>(); var origToInf = new Dictionary<MethodInfo, MethodInfo>(); try { // Step 1. Get rid of alternatives that don't have a form that suits arg count. var suitableAlts = new List<MethodInfo>(); foreach (var alt in mg.Alts) { if (alt.XArgsCount().Contains(ctx.Root.XArgsCount())) { suitableAlts.Add(alt); } else { failboats.Add(alt, new MethodMatchingException( MethodMatchingExceptionType.ArgcMismatch, alt, null, ctx)); } } // Step 2. Infer generic arguments of all open generic alternatives and get rid of those who fail to be inferred. var inferredAlts = new List<MethodInfo>(); foreach (var salt in suitableAlts) { try { var infAlt = salt.InferGenericTypeParams(ctx); inferredAlts.Add(infAlt); origToInf.Add(salt, infAlt); infToOrig.Add(infAlt, salt); } catch (GenericArgumentsInferenceException gex) { if (gex.IsUnexpected) { throw; } else { failboats.Add(salt, new MethodMatchingException( MethodMatchingExceptionType.GenericArgInferenceFailed, salt, null, ctx, gex)); } } } // Step 3. Store type inference context of all alternatives that had made it so far var sigs = new List<ResolvedSignature>(); foreach (var infAlt in inferredAlts) { var infAlt1 = infAlt; var resolvedCtx = new TypeInferenceEngine(ctx); ctx.Root.CallArgs().ForEach((callArg, i) => { if (callArg is LambdaExpression) { var inferredFormal = infAlt1.IsExtension() ? infAlt1.GetParameters()[i + 1].ParameterType : infAlt1.GetParameters()[i].ParameterType; var inferredLambda = new Lambda( (LambdaExpression)callArg, inferredFormal.NormalizeFunctionType()); resolvedCtx.Ctx.Inferences.Add(callArg, inferredLambda); } resolvedCtx.InferTypes(callArg); }); sigs.Add(new ResolvedSignature(resolvedCtx.Ctx, infAlt)); } // Step 4. Now get rid of alternatives that do not match Relinq-specific validation var validSigs = new List<ResolvedSignature>(); foreach (var sig in sigs) { var args = ctx.Root.ToXArgs(sig.Inferences); if (sig.Signature.IsValid(sig.Signature.IsExtension() ? args : args.Skip(1))) { validSigs.Add(sig); } else { failboats.Add(infToOrig[sig.Signature], new MethodMatchingException( MethodMatchingExceptionType.SignatureFailsMetaConstrains, infToOrig[sig.Signature], sig.Signature, ctx)); } } // Step 5. Eliminate signatures that do not satisfy actual argument conversions var invs = new List<ResolvedInvocation>(); foreach (var validSig in validSigs) { var args = ctx.Root.ToXArgs(validSig.Inferences); var @params = validSig.Signature.ToXArgs(ctx.Root.XArgsCount()); var inv = new ResolvedInvocation(validSig, args.Zip(@params, (arg, param) => Cast.Lookup(arg, param))); var fail = Array.FindIndex(inv.Casts.ToArray(), c => c == null); if (fail == -1) { invs.Add(inv); } else { failboats.Add(infToOrig[validSig.Signature], new MethodMatchingException( MethodMatchingExceptionType.ActualArgCannotBeCastToFormalArg, infToOrig[validSig.Signature], validSig.Signature, ctx, fail)); } } // Step 6. Remove sub-par signatures from the all-satisfying survivors var res = new List<ResolvedInvocation>(); foreach (var inv in invs) { var subpar = false; foreach (var anotherInv in invs) { subpar |= anotherInv > inv; } if (!subpar) { res.Add(inv); } else { failboats.Add(infToOrig[inv.Signature], new MethodMatchingException( MethodMatchingExceptionType.SignatureIsSubPar, infToOrig[inv.Signature], inv.Signature, ctx)); } } // Step 7. Finalize the death race (and evade the final trap) switch (res.Count()) { case 0: throw new MethodGroupResolutionFailedException( JSToCSharpExceptionType.FruitlessMethodGroupResolution, mg, failboats, ctx); case 1: var winrar = res.Single(); if (winrar.Signature.IsTrap()) { failboats.Add(infToOrig[winrar.Signature], new MethodMatchingException( MethodMatchingExceptionType.SignatureIsEntrapped, infToOrig[winrar.Signature], winrar.Signature, ctx)); throw new MethodGroupResolutionFailedException( JSToCSharpExceptionType.FruitlessMethodGroupResolution, mg, failboats, ctx); } // my sincere congratulations return winrar; default: foreach (var winrar2 in res) { failboats.Add(infToOrig[winrar2.Signature], new MethodMatchingException( MethodMatchingExceptionType.SignatureIsOkButAmbiguous, infToOrig[winrar2.Signature], winrar2.Signature, ctx)); } throw new MethodGroupResolutionFailedException( JSToCSharpExceptionType.AmbiguousMethodGroupResolution, mg, failboats, ctx); } } catch (MethodGroupResolutionFailedException) { throw; } catch (Exception ex) { throw new MethodGroupResolutionFailedException( JSToCSharpExceptionType.Unexpected, mg, failboats, ctx, ex); } }
protected MethodResolutionException(MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceContext ctx, int? mismatchIndex, Exception innerException) : this(originalSignature, inferredSignature, new TypeInferenceEngine(ctx), mismatchIndex, innerException) { }
protected MethodResolutionException(MethodInfo originalSignature, MethodInfo inferredSignature, TypeInferenceContext ctx) : this(originalSignature, inferredSignature, new TypeInferenceEngine(ctx), null, null) { }