示例#1
0
        protected bool IsCopyNeededForAssignmentTarget(JSExpression target)
        {
            target = JSReferenceExpression.Strip(target);

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

            if (IsImmutable(target))
            {
                return(false);
            }

            var variable = target as JSVariable;

            if (variable != null)
            {
                return(SecondPass.IsVariableModified(variable.Name));
            }

            return(true);
        }
示例#2
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);
        }
示例#3
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);
        }