Esempio n. 1
0
        protected bool IsParameterCopyNeeded(FunctionAnalysis2ndPass sa, string parameterName, JSExpression expression, out GenericParameter relevantParameter)
        {
            if (!IsCopyNeeded(expression, out relevantParameter))
            {
                return(false);
            }

            if (sa == null)
            {
                return(true);
            }

            if (!OptimizeCopies)
            {
                return(true);
            }

            bool modified = true, escapes = true, isResult = false;

            if (parameterName != null)
            {
                modified = sa.ModifiedVariables.Contains(parameterName);
                escapes  = sa.EscapingVariables.Contains(parameterName);
                isResult = sa.ResultVariable == parameterName;
            }

            return(modified || (escapes && !isResult));
        }
Esempio n. 2
0
        public bool Equals(FunctionAnalysis2ndPass rhs, out string[] _differences)
        {
            var differences = new List <string>();

            if (rhs == this)
            {
                _differences = null;
                return(true);
            }

            if (!EscapingVariables.SequenceEqual(rhs.EscapingVariables))
            {
                differences.Add("EscapingVariables");
            }

            if (!ModifiedVariables.SequenceEqual(rhs.ModifiedVariables))
            {
                differences.Add("ModifiedVariables");
            }

            if (ResultIsNew != rhs.ResultIsNew)
            {
                differences.Add("ResultIsNew");
            }

            if (ResultVariable != rhs.ResultVariable)
            {
                differences.Add("ResultVariable");
            }

            if (!VariableAliases.SequenceEqual(rhs.VariableAliases))
            {
                differences.Add("VariableAliases");
            }

            if (ViolatesThisReferenceImmutability != rhs.ViolatesThisReferenceImmutability)
            {
                differences.Add("ViolatesThisReferenceImmutability");
            }

            if (IsPure != rhs.IsPure)
            {
                differences.Add("IsPure");
            }

            if (differences.Count > 0)
            {
                _differences = differences.ToArray();
                return(false);
            }
            else
            {
                _differences = null;
                return(true);
            }
        }
Esempio n. 3
0
        public void VisitNode(JSNewExpression newexp)
        {
            FunctionAnalysis2ndPass sa = null;

            if (newexp.Constructor != null)
            {
                // HACK
                sa = GetSecondPass(new JSMethod(newexp.ConstructorReference, newexp.Constructor, MethodTypes));
            }

            CloneArgumentsIfNecessary(newexp.Parameters, newexp.Arguments, sa);

            VisitChildren(newexp);
        }
Esempio n. 4
0
        public void VisitNode(JSFunctionExpression fn)
        {
            var countRefs = new CountVariableReferences(ReferenceCounts);

            countRefs.Visit(fn.Body);

            SecondPass = GetSecondPass(fn.Method);
            if (SecondPass == null)
            {
                throw new InvalidDataException("No second-pass static analysis data for function '" + fn.Method.QualifiedIdentifier + "'");
            }

            VisitChildren(fn);
        }
Esempio n. 5
0
        public void VisitNode(JSFunctionExpression fn)
        {
            // Create a new visitor for nested function expressions
            if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null)
            {
                var nested = new EmulateStructAssignment(TypeSystem, FunctionSource, CLR, OptimizeCopies);
                nested.Visit(fn);
                return;
            }

            var countRefs = new CountVariableReferences(ReferenceCounts);

            countRefs.Visit(fn.Body);

            SecondPass = FunctionSource.GetSecondPass(fn.Method);

            VisitChildren(fn);
        }
Esempio n. 6
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
            {
                sa = FunctionSource.GetSecondPass(invocation.JSMethod);
            }

            var parms = invocation.Parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++)
            {
                var pd       = parms[i].Key;
                var argument = parms[i].Value;

                string parameterName = null;
                if (pd != null)
                {
                    parameterName = pd.Name;
                }

                GenericParameter relevantParameter;
                if (IsParameterCopyNeeded(sa, parameterName, argument, out relevantParameter))
                {
                    if (Tracing)
                    {
                        Debug.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument));
                    }

                    invocation.Arguments[i] = MakeCopyForExpression(argument, relevantParameter);
                }
                else
                {
                    if (Tracing && TypeUtil.IsStruct(argument.GetActualType(TypeSystem)))
                    {
                        Debug.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument));
                    }
                }
            }

            VisitChildren(invocation);
        }
Esempio n. 7
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
            {
                sa = GetSecondPass(invocation.JSMethod);
            }

            CloneArgumentsIfNecessary(invocation.Parameters, invocation.Arguments, sa);

            var thisReference = invocation.ThisReference;

            if (
                (thisReference != null) &&
                (sa != null) &&
                sa.ViolatesThisReferenceImmutability &&
                !(ParentNode is JSCommaExpression)
                )
            {
                // The method we're calling violates immutability so we need to clone the this-reference
                //  before we call it.
                var thisReferenceType = thisReference.GetActualType(TypeSystem);
                if (TypeUtil.IsStruct(thisReferenceType))
                {
                    if (!(thisReference is JSVariable) && !(thisReference is JSFieldAccess))
                    {
                        throw new NotImplementedException("Unsupported invocation of method that reassigns this within an immutable struct: " + invocation.ToString());
                    }

                    var cloneExpr = new JSBinaryOperatorExpression(
                        JSOperator.Assignment, thisReference, new JSStructCopyExpression(thisReference), thisReferenceType
                        );
                    var commaExpression = new JSCommaExpression(cloneExpr, invocation);

                    ParentNode.ReplaceChild(invocation, commaExpression);
                    VisitReplacement(commaExpression);
                    return;
                }
            }

            VisitChildren(invocation);
        }
Esempio n. 8
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
            {
                sa = FunctionSource.GetSecondPass(invocation.JSMethod);
            }

            var parms = invocation.Parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++)
            {
                var pd       = parms[i].Key;
                var argument = parms[i].Value;

                string parameterName = null;
                if (pd != null)
                {
                    parameterName = pd.Name;
                }

                if (IsParameterCopyNeeded(sa, parameterName, argument))
                {
                    if (Tracing)
                    {
                        Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument));
                    }

                    invocation.Arguments[i] = new JSStructCopyExpression(argument);
                }
                else
                {
                    if (Tracing)
                    {
                        Debug.WriteLine(String.Format("struct copy elided for argument {0}", argument));
                    }
                }
            }

            VisitChildren(invocation);
        }
Esempio n. 9
0
        private void CloneArgumentsIfNecessary(
            IEnumerable <KeyValuePair <ParameterDefinition, JSExpression> > parameters,
            IList <JSExpression> argumentValues,
            FunctionAnalysis2ndPass sa
            )
        {
            var parms = parameters.ToArray();

            for (int i = 0, c = parms.Length; i < c; i++)
            {
                var pd       = parms[i].Key;
                var argument = parms[i].Value;

                string parameterName = null;
                if (pd != null)
                {
                    parameterName = pd.Name;
                }

                GenericParameter relevantParameter;
                if (IsArgumentCopyNeeded(sa, parameterName, argument, out relevantParameter))
                {
                    if (TraceInsertedCopies)
                    {
                        Console.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument));
                    }

                    argumentValues[i] = MakeCopyForExpression(argument, relevantParameter);
                }
                else
                {
                    if (TraceElidedCopies && TypeUtil.IsStruct(argument.GetActualType(TypeSystem)))
                    {
                        Console.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument));
                    }
                }
            }
        }
Esempio n. 10
0
        protected bool IsArgumentCopyNeeded(FunctionAnalysis2ndPass sa, string parameterName, JSExpression expression, out GenericParameter relevantParameter)
        {
            if (!IsCopyNeeded(expression, out relevantParameter))
            {
                return(false);
            }

            if (sa == null)
            {
                return(true);
            }

            if (!OptimizeCopies)
            {
                return(true);
            }

            bool modified = true, escapes = true, isResult = false;

            if (parameterName != null)
            {
                modified = sa.IsVariableModified(parameterName);
                escapes  = sa.DoesVariableEscape(parameterName, true);
                isResult = sa.ResultVariable == parameterName;
            }

            var result = modified || (escapes && !isResult);

            if (!result)
            {
                if (TraceElidedCopies)
                {
                    Console.WriteLine("argument {0} ('{1}') needs no copy because it isn't modified and doesn't escape", expression, parameterName);
                }
            }

            return(result);
        }
        protected bool IsEffectivelyConstant(JSExpression expression)
        {
            if (expression.IsConstant)
            {
                return(true);
            }

            var invocation = expression as JSInvocationExpression;
            FunctionAnalysis2ndPass secondPass = null;

            if ((invocation != null) && (invocation.JSMethod != null))
            {
                secondPass = FunctionSource.GetSecondPass(invocation.JSMethod);

                if ((secondPass != null) && secondPass.IsPure)
                {
                    return(true);
                }

                var methodName = invocation.JSMethod.Method.Name;
                if ((methodName == "IDisposable.Dispose") || (methodName == "Dispose"))
                {
                    var thisType = invocation.ThisReference.GetActualType(TypeSystem);

                    if (thisType != null)
                    {
                        var typeInfo = TypeInfo.GetExisting(thisType);

                        if ((typeInfo != null) && typeInfo.Metadata.HasAttribute("JSIL.Meta.JSPureDispose"))
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Esempio n. 12
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
            {
                sa = GetSecondPass(invocation.JSMethod);
            }

            CloneArgumentsIfNecessary(invocation.Parameters, invocation.Arguments, sa);

            var thisReference         = invocation.ThisReference;
            var thisReferenceType     = thisReference.GetActualType(TypeSystem);
            var thisReferenceIsStruct = TypeUtil.IsStruct(thisReferenceType) &&
                                        !TypeUtil.IsNullable(thisReferenceType);

            bool thisReferenceNeedsCopy = false;
            bool thisReferenceNeedsCopyAndReassignment = false;

            if (thisReferenceIsStruct && !invocation.SuppressThisClone)
            {
                var isMethodInvocation = (thisReference != null) &&
                                         (sa != null) &&
                                         !(ParentNode is JSCommaExpression) &&
                                         !(thisReference is JSStructCopyExpression) &&
                                         !(
                    (ParentNode is JSResultReferenceExpression) &&
                    Stack.OfType <JSCommaExpression>().Any()
                    );


                // If a struct is immutable, a method may reassign the this-reference,
                //  i.e. 'this = otherstruct' successfully.
                // In this scenario we have to clone the old this-reference, invoke
                //  the method on the clone, and replace the old this-reference with
                //  the new, modified clone.

                thisReferenceNeedsCopyAndReassignment = isMethodInvocation && sa.ViolatesThisReferenceImmutability;


                // When invoking a method that mutates a struct's members or lets them escape,
                //  we need to copy the this-reference if it isn't writable.

                // FIXME: We're white-listing writable targets, but we probably want to blacklist
                //  non-writable targets instead, so that if a fn's result is new we don't clone it
                //  to use it as a this-reference.

                // FIXME: Handle pointers, replace x.get().foo() with some sort of comma expr,
                //  like ($x = x.get(), $x.foo(), x.set($x)) ?
                var isWritableInstance =
                    (thisReference is JSFieldAccess) ||
                    (thisReference is JSVariable) ||
                    (thisReference is JSReadThroughReferenceExpression);

                thisReferenceNeedsCopy = isMethodInvocation &&
                                         (
                    sa.IsVariableModified("this") ||
                    // Maybe don't include return here?
                    sa.DoesVariableEscape("this", true)
                                         ) &&
                                         (!isWritableInstance);
            }

            GenericParameter relevantParameter;
            var isCopyNeeded = IsCopyNeeded(thisReference, out relevantParameter, false);

            if (
                thisReferenceNeedsCopyAndReassignment && isCopyNeeded
                )
            {
                if (TraceInsertedCopies)
                {
                    Console.WriteLine("Cloning this-reference because method reassigns this: {0}", invocation);
                }

                if (
                    (thisReference is JSFieldAccess) ||
                    (thisReference is JSVariable)
                    )
                {
                    var rre       = ParentNode as JSResultReferenceExpression;
                    var cloneExpr = new JSBinaryOperatorExpression(
                        JSOperator.Assignment, thisReference, MakeCopyForExpression(thisReference, relevantParameter), thisReferenceType
                        );
                    var commaExpression = new JSCommaExpression(
                        cloneExpr,
                        (rre != null)
                            ? (JSExpression) new JSResultReferenceExpression(invocation)
                            : (JSExpression)invocation
                        );

                    if (rre != null)
                    {
                        ResultReferenceReplacement = commaExpression;
                        return;
                    }
                    else
                    {
                        ParentNode.ReplaceChild(invocation, commaExpression);
                        VisitReplacement(commaExpression);
                        return;
                    }
                }
                else
                {
                    // Huh?
                    invocation.ReplaceChild(thisReference, MakeCopyForExpression(thisReference, relevantParameter));
                    VisitChildren(invocation);
                    return;
                }
            }
            else if (thisReferenceNeedsCopy && isCopyNeeded)
            {
                if (TraceInsertedCopies)
                {
                    Console.WriteLine("Cloning this-reference because method mutates this and this-reference is not field/local: {0}", invocation);
                }

                invocation.ReplaceChild(thisReference, MakeCopyForExpression(thisReference, relevantParameter));
                VisitChildren(invocation);
                return;
            }

            VisitChildren(invocation);
        }
Esempio n. 13
0
        public void VisitNode(JSInvocationExpression invocation)
        {
            FunctionAnalysis2ndPass sa = null;

            if (invocation.JSMethod != null)
            {
                sa = GetSecondPass(invocation.JSMethod);
            }

            CloneArgumentsIfNecessary(invocation.Parameters, invocation.Arguments, sa);

            var thisReference = invocation.ThisReference;

            if (
                (thisReference != null) &&
                (sa != null) &&
                !(ParentNode is JSCommaExpression) &&
                !(thisReference is JSStructCopyExpression) &&
                !(
                    (ParentNode is JSResultReferenceExpression) &&
                    Stack.OfType <JSCommaExpression>().Any()
                    ) &&
                (
                    sa.ViolatesThisReferenceImmutability ||
                    sa.ModifiedVariables.Contains("this") ||
                    sa.EscapingVariables.Contains("this")
                )
                )
            {
                // The method we're calling violates immutability so we need to clone the this-reference
                //  before we call it.
                var thisReferenceType = thisReference.GetActualType(TypeSystem);

                if (TypeUtil.IsStruct(thisReferenceType) && !TypeUtil.IsStructImmutable(thisReferenceType))
                {
                    if ((thisReference is JSVariable) || (thisReference is JSFieldAccess))
                    {
                        var rre       = ParentNode as JSResultReferenceExpression;
                        var cloneExpr = new JSBinaryOperatorExpression(
                            JSOperator.Assignment, thisReference, new JSStructCopyExpression(thisReference), thisReferenceType
                            );
                        var commaExpression = new JSCommaExpression(
                            cloneExpr,
                            (rre != null)
                                ? (JSExpression) new JSResultReferenceExpression(invocation)
                                : (JSExpression)invocation
                            );

                        if (rre != null)
                        {
                            ResultReferenceReplacement = commaExpression;
                        }
                        else
                        {
                            ParentNode.ReplaceChild(invocation, commaExpression);
                            VisitReplacement(commaExpression);
                        }
                    }
                    else
                    {
                        invocation.ReplaceChild(thisReference, new JSStructCopyExpression(thisReference));
                        VisitChildren(invocation);
                    }

                    return;
                }
            }

            VisitChildren(invocation);
        }