Ejemplo n.º 1
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);            
        }
Ejemplo n.º 2
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);
        }
Ejemplo n.º 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;

            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);
        }