Example #1
0
        // 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, TypeInferenceEngine engine, int? mismatchIndex, Exception innerException)
            : base(innerException)
        {
            OriginalSignature = originalSignature;
            InferredSignature = inferredSignature;
            RootExpression = engine.Root;
            MismatchIndex = mismatchIndex;

            try
            {
                // ensure that all call args are inferred successfully
                foreach (var arg in engine.Root.CallArgs())
                {
                    // we need this check because depending on the context
                    // call args might already be inferred by the moment of crash
                    if (!engine.Inferences.ContainsKey(arg))
                    {
                        // this is unlikely to crash and ruin our attempt to provide the client with comprehensive info
                        // since type inference for arguments has already been probed before entering TypeInferenceMethods.Resolve
                        engine.InferTypes(arg);
                    }
                }

                ActualArguments = engine.Ctx.Root.ToXArgs(engine.Inferences).ToArray();
                FormalArguments = (InferredSignature ?? OriginalSignature).ToXArgs(ActualArguments.Length).ToArray();

                // the lines below are copy&pasted from GenericArgsInference.cs
                for (var i = 0; i < ActualArguments.Length; ++i)
                {
                    var ai_r = ActualArguments.ElementAt(i);
                    var pi = FormalArguments.ElementAt(i);

                    if (!pi.IsOpenGeneric())
                    {
                        if (pi.IsFunctionType() && ai_r is Lambda)
                        {
                            var ai_s = ((Lambda)ai_r).Type.GetFunctionSignature();
                            var pi_s = pi.GetFunctionSignature();

                            if (ai_s.GetParameters().Length == pi_s.GetParameters().Length)
                            {
                                var callArg = engine.Ctx.Root.CallArgs().ElementAt(i - 1);
                                ActualArguments[i] = new Lambda(
                                    (LambdaExpression)callArg,
                                    (pi_s.GetParameters()
                                        .Select(pi2 => pi2.ParameterType.IsGenericParameter ? 
                                            typeof(Variant) : pi2.ParameterType)
                                        .Concat(typeof(Variant).AsArray()))
                                        .ForgeFuncType());
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {
                // sigh, the client won't get full info from the crash site
                // sad, but true, so just ignore possible exceptions
            }
        }