Beispiel #1
0
        private void AfterCall(Guid id, RelinqScriptExpression e, TypeInferenceCache cache)
        {
            var startedEntry = History.Single(entry2 => entry2.Id == id);
            var finishedEntry = new TypeInferenceHistoryEntry(startedEntry);

            finishedEntry.InferredType = cache.ContainsKey(e) ? cache[e] : null;

            if (e.IsCall())
            {
                RelinqScriptExpression invtarget = null;
                if (e is InvokeExpression)
                {
                    invtarget = e.Children.ElementAt(0);
                }
                else if (e is IndexerExpression || e is OperatorExpression)
                {
                    invtarget = e;
                }

                if (invtarget != null && cache.Invocations.ContainsKey(invtarget))
                {
                    finishedEntry.InferredInvocation = cache.Invocations[invtarget];
                }
            }

            History.Add(finishedEntry);
            if (History.Count % 20000 == 0) DumpHistory();
        }
Beispiel #2
0
        public void InferTypes(RelinqScriptExpression e, TypeInferenceCache cache,
            Action<RelinqScriptExpression, TypeInferenceCache> impl)
        {
            Action<RelinqScriptExpression, TypeInferenceCache> wrappedImpl = (e1, cache1) =>
            {
#if TRACE
                var id = BeforeCall(e, cache);

                try
                {
                    impl(e, cache);
                }
                finally
                {
                    AfterCall(id, e, cache);
                }
#else
                impl(e, cache);
#endif
            };

            if (Cache == null)
            {
                wrappedImpl(e, cache);
            }
            else
            {
                Cache.RunThroughCache(e, cache, wrappedImpl);
            }
        }
        private static Expression EnsureBoolean(this Expression e, TypeInferenceCache types)
        {
            var t = types[e];

            if (t == typeof(bool))
            {
                return(e.CurrentTransform());
            }
            else
            {
                if (t.IsValueType)
                {
                    t.IsInteger().AssertTrue();
                    var @const = e as Const;
                    if (@const != null)
                    {
                        var value = @const.Value.AssertCast <int>();
                        (value == 0 || value == 1).AssertTrue();
                        return(new Const(value == 1));
                    }

                    return(Operator.NotEqual(e.CurrentTransform(), new Const(0)));
                }
                else
                {
                    return(Operator.NotEqual(e.CurrentTransform(), new Const(null, t)));
                }
            }
        }
Beispiel #4
0
        public TypeInferenceContext(RelinqScriptExpression root, 
            TypeInferenceCache inferences, IntegrationContext integration)
        {
            Root = root;

            // ctx.Root might only be one of the following:
            // * Invoke expression (for both cases of compile-time and late-bound invocation)
            // * Indexer or Operator expression
            // * Root of an entire AST
            if (Root.Parent != null && !Root.IsCall())
            {
                throw new NotSupportedException(root.ToString());
            }

            Inferences = inferences;
            Integration = integration;
        }
Beispiel #5
0
        private Guid BeforeCall(RelinqScriptExpression e, TypeInferenceCache cache)
        {
            var startedEntry = new TypeInferenceHistoryEntry{Expression = e};
            startedEntry.InferredType = cache.ContainsKey(e) ? cache[e] : null;
            History.Add(startedEntry);

            Func<LambdaExpression, String, RelinqScriptType> argType = (lambda, arg) => {
                var argIndex = Array.IndexOf(lambda.Args.ToArray(), arg);
                var lambdaType = ((Lambda)cache[lambda]).Type.GetFunctionSignature();
                return lambdaType.GetParameters()[argIndex].ParameterType;
            };

            e.GetClosure().ForEach(kvp => startedEntry.Closure.Add(kvp.Key,
                cache.ContainsKey(kvp.Value) ? argType(kvp.Value, kvp.Key) : null));

            return startedEntry.Id;
        }
Beispiel #6
0
        public static IEnumerable<RelinqScriptType> ToXArgs(
            this RelinqScriptExpression root, TypeInferenceCache inf)
        {
            // cba to duplicate validating logics here
            // see todo in CSharpExpressionTreeBuilder

            if (!root.IsCall())
            {
                throw new NotSupportedException(String.Format(
                    "X-args can only be acquired for call-type nodes. " +
                    "Target node '{0}' is unsuitable for this purpose.", root));
            }
            else
            {
                if (root is InvokeExpression)
                {
                    var ie = (InvokeExpression)root;

                    // head is either an irrelevant type (if its a delegate invocation)
                    // or a source of the method group (if its a normal invocation)
                    yield return inf[ie.Target] is ClrType ?
                        new This() :
                        inf[((MemberAccessExpression)ie.Target).Target];

                    foreach(var arg in ie.Args) yield return inf[arg]; // body + tail
                }
                else if (root is IndexerExpression)
                {
                    var ie = (IndexerExpression)root;
                    yield return inf[ie.Target]; // head is not null, since indexers are instance methods
                    foreach (var operand in ie.Operands) yield return inf[operand]; // body + tail
                }
                else if (root is OperatorExpression)
                {
                    var ie = (OperatorExpression)root;
                    yield return null; // head is null, since the call is static
                    foreach (var operand in ie.Operands) yield return inf[operand]; // body + tail
                }
                else
                {
                    throw new NotSupportedException(String.Format(
                        "Something was overlooked: there's no way a call-type node " +
                        "can be the following: '{0}'.", root));
                }
            }
        }
Beispiel #7
0
        private void InferIndexer(IndexerExpression ie, TypeInferenceCache cache)
        {
            InferTypes(ie.Target, cache);
            var typeofTarget = cache[ie.Target];

            if (typeofTarget is ClrType)
            {
                var preview = cache.Clone();
                ie.Operands.ForEach(operand => InferTypes(operand, preview));
                var types = ie.Operands.Select(operand => preview[operand]);

                if (types.Any(type => type is Variant))
                {
                    cache.Add(ie, new Variant());
                    cache.Upgrade(preview);
                }
                else
                {
                    var alts = typeofTarget.LookupIndexers();
                    if (alts == null)
                    {
                        throw new NoSuchIndexerException(Root, ie, typeofTarget);
                    }

                    InferMethodGroup(alts, ie, cache);
                }
            }
            else if (typeofTarget is Variant)
            {
                cache.Add(ie, new Variant());
            }
            else
            {
                throw new NoSuchIndexerException(Root, ie, typeofTarget);
            }
        }
Beispiel #8
0
        private void InferInvoke(InvokeExpression ie, TypeInferenceCache cache)
        {
            InferTypes(ie.Target, cache);
            var typeofTarget = cache[ie.Target];

            if (typeofTarget is ClrType)
            {
                var preview = cache.Clone();
                ie.Args.ForEach(arg => InferTypes(arg, preview));
                var types = ie.Args.Select(arg => preview[arg]);

                if (types.Any(type => type is Variant))
                {
                    cache.Add(ie, new Variant());
                    cache.Upgrade(preview);
                }
                else
                {
                    var clrType = ((ClrType)typeofTarget).Type;
                    if (clrType.IsDelegate())
                    {
                        var sig = clrType.GetFunctionSignature();
                        InferMethodGroup(new MethodGroup(sig.AsArray(), clrType.Name), ie, cache);
                    }
                    else
                    {
                        throw new CannotBeInvokedException(Root, ie, typeofTarget);
                    }
                }
            }
            else if (typeofTarget is MethodGroup)
            {
                InferMethodGroup((MethodGroup)typeofTarget, ie, cache);
            }
            else if (typeofTarget is Variant)
            {
                cache.Add(ie, new Variant());
            }
            else
            {
                throw new CannotBeInvokedException(Root, ie, typeofTarget);
            }
        }
Beispiel #9
0
        private void InferMemberAccess(MemberAccessExpression mae, TypeInferenceCache cache)
        {
            InferTypes(mae.Target, cache);
            var typeofTarget = cache[mae.Target];

            if (typeofTarget is ClrType)
            {
                // nb: the member access also covers method group lookup
                // i.e. object.Method(arg1, arg2) is in fact resolved in two steps:
                // 1) object.Method -> resolves to all instance or extension methods
                //    (on this step fields and properties are omitted because the expression
                //    is being used in the context of an invocation)
                // 2) MG(arg1, arg2) -> picks up the best method from the MG that matches arglist

                var usedInContextOfInvocation = mae.Parent is InvokeExpression && mae.ChildIndex == 0;
                cache.Add(mae, usedInContextOfInvocation ? 
                    typeofTarget.LookupMethodGroup(mae.Name) : 
                    typeofTarget.LookupMemberAccess(mae.Name));

                if (cache[mae] == null)
                {
                    throw new NoSuchMemberException(Root, mae, typeofTarget);
                }
            }
            else if (typeofTarget is Variant)
            {
                cache.Add(mae, new Variant());
            }
            else
            {
                throw new NoSuchMemberException(Root, mae, typeofTarget);
            }
        }
Beispiel #10
0
        private void InferLambda(LambdaExpression le, TypeInferenceCache cache)
        {
            // lambda is the only one expression that can have its type set from outside
            if (!cache.ContainsKey(le))
            {
                var lambdaType = Enumerable.Repeat(typeof(Variant), 1 + le.Args.Count()).ForgeFuncType();
                cache.Add(le, new Lambda(le, lambdaType));
            }

            // detect overlapping variables
            var closure = le.Parent == null ? null : le.Parent.GetClosure();
            if (closure != null)
            {
                var overlapping = closure.Keys.Intersect(le.Args);
                if (overlapping.IsNotEmpty())
                {
                    throw new RedeclaredVariableException(Root, le, closure);
                }
            }

            // detect overriding keywords
            foreach (var arg in le.Args)
            {
                if (Integration.IsRegisteredJS(arg))
                {
                    throw new VariableOverridesKeywordException(Root, le, arg);
                }
            }

            InferTypes(le.Body, cache);
        }
Beispiel #11
0
        private void InferTypesImpl(RelinqScriptExpression e, TypeInferenceCache cache)
        {
            try
            {
                switch (e.NodeType)
                {
                    case ExpressionType.Keyword:
                        InferKeyword((KeywordExpression)e, cache);
                        break;

                    case ExpressionType.Variable:
                        InferVariable((VariableExpression)e, cache);
                        break;

                    case ExpressionType.Constant:
                        InferConstant((ConstantExpression)e, cache);
                        break;

                    case ExpressionType.New:
                        InferNew((NewExpression)e, cache);
                        break;

                    case ExpressionType.Lambda:
                        InferLambda((LambdaExpression)e, cache);
                        break;

                    case ExpressionType.MemberAccess:
                        InferMemberAccess((MemberAccessExpression)e, cache);
                        break;

                    case ExpressionType.Invoke:
                        InferInvoke((InvokeExpression)e, cache);
                        break;

                    case ExpressionType.Indexer:
                        InferIndexer((IndexerExpression)e, cache);
                        break;

                    case ExpressionType.Operator:
                        InferOperator((OperatorExpression)e, cache);
                        break;

                    case ExpressionType.Conditional:
                        InferConditional((ConditionalExpression)e, cache);
                        break;

                    default:
                        throw new TypeInferenceException(
                            JSToCSharpExceptionType.UnexpectedNodeType, Root, e);
                }
            }
            catch (TypeInferenceException)
            {
                throw;
            }
            catch (Exception ex)
            {
                throw new TypeInferenceException(
                    JSToCSharpExceptionType.Unexpected, Root, e, ex);
            }
        }
Beispiel #12
0
        private void InferConditional(ConditionalExpression ce, TypeInferenceCache cache)
        {
            ce.Children.ForEach(child => InferTypes(child, cache));
            var typeofTest = cache[ce.Test];
            var typeofFalse = cache[ce.IfFalse];
            var typeofTrue = cache[ce.IfTrue];

            if (typeofTest is Variant || typeofFalse is Variant || typeofTrue is Variant)
            {
                cache.Add(ce, new Variant());
            }
            else
            {
                if (!(typeofTest is ClrType &&
                    typeofTest.HasImplicitCastTo(typeof(bool))))
                {
                    throw new InconsistentConditionalExpression(
                        JSToCSharpExceptionType.ConditionalTestInvalidType,
                        Root, ce, typeofTest, typeofTrue, typeofFalse);
                }

                var f2t = typeofFalse.HasImplicitCastTo(typeofTrue);
                var t2f = typeofTrue.HasImplicitCastTo(typeofFalse);

                if (!f2t && !t2f)
                {
                    if (typeofFalse is Lambda || typeofTrue is Lambda)
                    {
                        throw new NotImplementedException(String.Format(
                            "Don't know how to infer the '{0} ? {1} : {2}' conditional expression.",
                            typeofTest, typeofTrue, typeofFalse));
                    }
                    else
                    {
                        throw new InconsistentConditionalExpression(
                            JSToCSharpExceptionType.ConditionalClausesNoCommonTypeWithOnlyOneClauseBeingCast,
                            Root, ce, typeofTest, typeofTrue, typeofFalse);
                    }
                }
                else if (f2t && t2f && typeofFalse != typeofTrue)
                {
                    throw new InconsistentConditionalExpression(
                        JSToCSharpExceptionType.ConditionalClausesAmbiguousCommonType,
                        Root, ce, typeofTest, typeofTrue, typeofFalse);
                }
                else
                {
                    cache.Add(ce, f2t ? typeofTrue : typeofFalse);
                }
            }
        }
Beispiel #13
0
 private void InferKeyword(KeywordExpression ke, TypeInferenceCache cache)
 {
     try
     {
         cache.Add(ke, Integration.ProduceCSharp(ke.Name).GetType());
     }
     catch (JSToCSharpIntegrationException jsex)
     {
         throw new TypeInferenceException(JSToCSharpExceptionType.Integration, Root, ke, jsex);
     }
 }
Beispiel #14
0
 public CompilationContext(TypeInferenceCache inferences)
 {
     Closure = new Dictionary<String, ParameterExpression>();
     Types = inferences.AsReadOnly();
     Invocations = inferences.Invocations.AsReadOnly();
 }
        private static Expression EnsureBoolean(this Expression e, TypeInferenceCache types)
        {
            var t = types[e];
            if (t == typeof(bool)) return e.CurrentTransform();
            else
            {
                if (t.IsValueType)
                {
                    t.IsInteger().AssertTrue();
                    var @const = e as Const;
                    if (@const != null)
                    {
                        var value = @const.Value.AssertCast<int>();
                        (value == 0 || value == 1).AssertTrue();
                        return new Const(value == 1);
                    }

                    return Operator.NotEqual(e.CurrentTransform(), new Const(0));
                }
                else
                {
                    return Operator.NotEqual(e.CurrentTransform(), new Const(null, t));
                }
            }
        }
Beispiel #16
0
        private void InferOperator(OperatorExpression oe, TypeInferenceCache cache)
        {
            var preview = cache.Clone();
            oe.Operands.ForEach(operand => InferTypes(operand, preview));
            var types = oe.Operands.Select(operand => preview[operand]);

            if (types.Any(type => type is Variant))
            {
                cache.Add(oe, new Variant());
                cache.Upgrade(preview);
            }
            else
            {
                var alts = oe.Type.LookupOperators(types.ToArray());

                // logical not can also be used to express ones complement
                // since they both correspond to a single LINQ expression type
                if (oe.Type == OperatorType.LogicalNot)
                {
                    var addendum = OperatorType.OnesComplement.LookupOperators(types.ToArray());
                    if (addendum != null)
                    {
                        var original = alts == null ? new MethodInfo[0] : alts.Alts;
                        alts = new MethodGroup(original.Concat(addendum.Alts), oe.Type.GetOpCode());
                    }
                }

                if (alts == null)
                {
                    throw new NoSuchOperatorException(Root, oe, types);
                }

                InferMethodGroup(alts, oe, cache);
            }
        }
Beispiel #17
0
        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);
        }
Beispiel #18
0
 private void InferVariable(VariableExpression ve, TypeInferenceCache cache)
 {
     if (ve.GetClosure().ContainsKey(ve.Name))
     {
         var lambda = ve.GetClosure()[ve.Name];
         var funcType = ((Lambda)cache[lambda]).Type;
         var argIndex = Array.IndexOf(lambda.Args.ToArray(), ve.Name);
         cache.Add(ve, funcType.GetFunctionDesc().Args.ElementAt(argIndex));
     }
     else
     {
         throw new UndeclaredVariableException(Root, ve, ve.GetClosure());
     }
 }
Beispiel #19
0
 private void InferTypes(RelinqScriptExpression e, TypeInferenceCache cache)
 {
     _sap.InferTypes(e, cache, InferTypesImpl);
 }
Beispiel #20
0
 private void InferConstant(ConstantExpression ce, TypeInferenceCache cache)
 {
     var inferred = TypeInferenceConstants.InferType(ce);
     if (inferred != null)
     {
         cache.Add(ce, inferred);
     }
     else
     {
         throw new ConstantInferenceFailedException(Root, ce);
     }
 }
Beispiel #21
0
        private void InferNew(NewExpression ne, TypeInferenceCache cache)
        {
            ne.Props.Values.ForEach(e => InferTypes(e, cache));

            var propTypes = ne.Props.Values.Select(e => cache[e]);
            if (propTypes.All(t => t is ClrType))
            {
                var clrPropTypes = propTypes.Cast<ClrType>().Select(pt => pt.Type);
                cache.Add(ne, AnonymousTypesHelper.ForgeTupleType(ne.Props.Keys, clrPropTypes));
            }
            else
            {
                var fail = Array.FindIndex(propTypes.ToArray(), 
                    t => !(t is ClrType || t is Variant));

                if (fail == -1)
                {
                    cache.Add(ne, new Variant());
                }
                else
                {
                    throw new CannotForgeAnonymousTypeException(
                        Root, ne, ne.Props.Keys.ElementAt(fail), propTypes.ElementAt(fail));
                }
            }
        }
 public LinqExpression Compile(TypeInferenceCache inferences)
 {
     return Compile(Ast, new CompilationContext(inferences));
 }