public void VisitNode (JSBinaryOperatorExpression boe) {
            JSPropertyAccess pa;
            JSAssignmentOperator assignmentOperator;
            JSBinaryOperator newOperator;

            if (
                IsPropertyAccess(boe, out pa) &&
                ((assignmentOperator = boe.Operator as JSAssignmentOperator) != null) &&
                IntroduceEnumCasts.ReverseCompoundAssignments.TryGetValue(assignmentOperator, out newOperator)
            ) {
                // FIXME: Terrible hack
                var type = pa.GetActualType(TypeSystem);
                var tempVariable = TemporaryVariable.ForFunction(
                    Stack.Last() as JSFunctionExpression, type
                );
                var replacement = new JSCommaExpression(
                    new JSBinaryOperatorExpression(
                        JSOperator.Assignment, tempVariable,
                        new JSBinaryOperatorExpression(
                            newOperator,
                            new JSPropertyAccess(
                                pa.ThisReference, pa.Property, false,
                                pa.TypeQualified, 
                                pa.OriginalType, pa.OriginalMethod, 
                                pa.IsVirtualCall
                            ), 
                            boe.Right, boe.GetActualType(TypeSystem)
                        ), type
                    ),
                    new JSBinaryOperatorExpression(
                        JSOperator.Assignment, 
                        new JSPropertyAccess(
                            pa.ThisReference, pa.Property, true, 
                            pa.TypeQualified,
                            pa.OriginalType, pa.OriginalMethod, 
                            pa.IsVirtualCall
                        ), tempVariable, type
                    ),
                    tempVariable
                );

                ParentNode.ReplaceChild(boe, replacement);
                VisitReplacement(replacement);
            } else {
                VisitChildren(boe);
            }
        }
Ejemplo n.º 2
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));
                }
            }

            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);
        }
        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);
        }
Ejemplo n.º 4
0
        public void VisitNode(JSUnaryOperatorExpression uoe)
        {
            var type = uoe.Expression.GetActualType(TypeSystem);
            var isEnum = IsEnumOrNullableEnum(type);

            if (isEnum) {
                var castToInt = JSInvocationExpression.InvokeMethod(
                    JS.valueOf(TypeSystem.Int32), uoe.Expression, null, true
                );

                if (LogicalOperators.Contains(uoe.Operator)) {
                    uoe.ReplaceChild(uoe.Expression, castToInt);
                } else if (uoe.Operator == JSOperator.Negation) {
                    uoe.ReplaceChild(uoe.Expression, castToInt);
                } else if (uoe.Operator is JSUnaryMutationOperator) {
                    if (
                        (uoe.Operator == JSOperator.PreIncrement) ||
                        (uoe.Operator == JSOperator.PreDecrement)
                    ) {
                        var assignment = MakeUnaryMutation(
                            uoe.Expression,
                            (uoe.Operator == JSOperator.PreDecrement)
                                ? JSOperator.Subtract
                                : JSOperator.Add,
                            type
                        );

                        ParentNode.ReplaceChild(uoe, assignment);
                        VisitReplacement(assignment);
                        return;

                    } else if (
                        (uoe.Operator == JSOperator.PostIncrement) ||
                        (uoe.Operator == JSOperator.PostDecrement)
                    ) {
                        // FIXME: Terrible hack oh god (also not strict mode safe)
                        var tempVariable = new JSRawOutputIdentifier(
                            (output) => output.WriteRaw("$temp"), type
                        );
                        var makeTempCopy = new JSBinaryOperatorExpression(
                            JSOperator.Assignment, tempVariable, uoe.Expression, type
                        );
                        var assignment = MakeUnaryMutation(
                            uoe.Expression,
                            (uoe.Operator == JSOperator.PostDecrement)
                                ? JSOperator.Subtract
                                : JSOperator.Add,
                            type
                        );

                        var comma = new JSCommaExpression(
                            makeTempCopy,
                            assignment,
                            tempVariable
                        );

                        ParentNode.ReplaceChild(uoe, comma);
                        VisitReplacement(comma);
                        return;

                    } else {
                        throw new NotImplementedException("Unary mutation of enum not supported: " + uoe.ToString());
                    }
                }
            }

            VisitChildren(uoe);
        }
Ejemplo n.º 5
0
        public void VisitNode(JSUnaryOperatorExpression uoe)
        {
            var type = uoe.Expression.GetActualType(TypeSystem);
            var isIntegral = TypeUtil.Is32BitIntegral(type);

            if (isIntegral && (uoe.Operator is JSUnaryMutationOperator)) {
                if (
                    (uoe.Operator == JSOperator.PreIncrement) ||
                    (uoe.Operator == JSOperator.PreDecrement)
                ) {
                    var assignment = MakeUnaryMutation(
                        uoe.Expression,
                        (uoe.Operator == JSOperator.PreDecrement)
                            ? JSOperator.Subtract
                            : JSOperator.Add,
                        type
                    );

                    ParentNode.ReplaceChild(uoe, assignment);
                    VisitReplacement(assignment);
                    return;

                } else if (
                    (uoe.Operator == JSOperator.PostIncrement) ||
                    (uoe.Operator == JSOperator.PostDecrement)
                ) {
                    // FIXME: Terrible hack
                    var tempVariable = TemporaryVariable.ForFunction(
                        Stack.Last() as JSFunctionExpression, type
                    );
                    var makeTempCopy = new JSBinaryOperatorExpression(
                        JSOperator.Assignment, tempVariable, uoe.Expression, type
                    );
                    var assignment = MakeUnaryMutation(
                        uoe.Expression,
                        (uoe.Operator == JSOperator.PostDecrement)
                            ? JSOperator.Subtract
                            : JSOperator.Add,
                        type
                    );

                    var comma = new JSCommaExpression(
                        makeTempCopy,
                        assignment,
                        tempVariable
                    );

                    ParentNode.ReplaceChild(uoe, comma);
                    VisitReplacement(comma);
                    return;

                } else {
                    throw new NotImplementedException("Unary mutation not supported: " + uoe.ToString());
                }
            }

            VisitChildren(uoe);
        }
Ejemplo n.º 6
0
        public void VisitNode(JSNewExpression newexp)
        {
            var type = newexp.GetActualType(TypeSystem);

            var isStruct = TypeUtil.IsStruct(type);
            var isInsideLoop = (Stack.Any((node) => node is JSLoopStatement));
            var parentInvocation = ParentNode as JSInvocationExpression;

            var doesValueEscape = true;
            if (
                isStruct &&
                (parentInvocation != null) &&
                (parentInvocation.JSMethod != null) &&
                (parentInvocation.JSMethod.Reference != null)
            ) {
                var methodDef = parentInvocation.JSMethod.Reference.Resolve();
                var secondPass = FunctionSource.GetSecondPass(parentInvocation.JSMethod, Function.Method.QualifiedIdentifier);
                if ((secondPass != null) && (methodDef != null)) {
                    // HACK
                    var argumentIndex = parentInvocation.Arguments.Select(
                        (a, i) => new { argument = a, index = i })
                        .FirstOrDefault((_) => _.argument.SelfAndChildrenRecursive.Contains(newexp));

                    if (argumentIndex != null) {
                        var argumentName = methodDef.Parameters[argumentIndex.index].Name;

                        doesValueEscape = secondPass.EscapingVariables.Contains(argumentName);
                    }
                }
            }

            if (isStruct &&
                isInsideLoop &&
                (parentInvocation != null) &&
                !doesValueEscape
            ) {
                string id;
                var hoistedVariable = MakeTemporaryVariable(type, out id);
                var constructorInvocation = JSInvocationExpression.InvokeMethod(
                    type, new JSMethod(newexp.ConstructorReference, newexp.Constructor, MethodTypes, null), hoistedVariable, newexp.Arguments.ToArray(), false
                );
                var replacement = new JSCommaExpression(
                    constructorInvocation, hoistedVariable
                );

                ToDeclare.Add(new PendingDeclaration(id, type, hoistedVariable));

                ParentNode.ReplaceChild(newexp, replacement);
                VisitReplacement(replacement);
            } else {
                VisitChildren(newexp);
            }
        }
Ejemplo 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;
            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.º 8
0
        public static JSExpression DecomposeUnaryMutation (
            JSUnaryOperatorExpression uoe, Func<JSRawOutputIdentifier> makeTemporaryVariable, TypeReference type, TypeSystem typeSystem
        ) {
            if (
                (uoe.Operator == JSOperator.PreIncrement) ||
                (uoe.Operator == JSOperator.PreDecrement)
            ) {
                var assignment = MakeUnaryMutation(
                    uoe.Expression,
                    (uoe.Operator == JSOperator.PreDecrement)
                        ? JSOperator.Subtract
                        : JSOperator.Add,
                    type, typeSystem
                );

                return assignment;
            } else if (
                (uoe.Operator == JSOperator.PostIncrement) ||
                (uoe.Operator == JSOperator.PostDecrement)
            ) {
                // FIXME: Terrible hack
                var tempVariable = makeTemporaryVariable();
                var makeTempCopy = new JSBinaryOperatorExpression(
                    JSOperator.Assignment, tempVariable, uoe.Expression, type
                );
                var assignment = MakeUnaryMutation(
                    uoe.Expression,
                    (uoe.Operator == JSOperator.PostDecrement)
                        ? JSOperator.Subtract
                        : JSOperator.Add,
                    type, typeSystem
                );

                var comma = new JSCommaExpression(
                    makeTempCopy,
                    assignment,
                    tempVariable
                );

                return comma;
            } else {
                throw new NotImplementedException("Unary mutation not supported: " + uoe);
            }
        }
Ejemplo n.º 9
0
        public static JSExpression DeconstructMutationAssignment (
            JSNode parentNode, JSBinaryOperatorExpression boe, TypeSystem typeSystem, TypeReference intermediateType
        ) {
            var assignmentOperator = boe.Operator as JSAssignmentOperator;
            if (assignmentOperator == null)
                return null;

            JSBinaryOperator replacementOperator;
            if (!IntroduceEnumCasts.ReverseCompoundAssignments.TryGetValue(assignmentOperator, out replacementOperator))
                return null;

            var leftType = boe.Left.GetActualType(typeSystem);

            var newBoe = new JSBinaryOperatorExpression(
                JSOperator.Assignment, MakeLhsForAssignment(boe.Left),
                new JSBinaryOperatorExpression(
                    replacementOperator, boe.Left, boe.Right, intermediateType
                ),
                leftType
            );

            var result = ConvertReadExpressionToWriteExpression(newBoe, typeSystem);
            if (parentNode is JSExpressionStatement) {
                return result;
            } else {
                var comma = new JSCommaExpression(
                    newBoe, boe.Left
                );
                return comma;
            }
        }
        public void VisitNode (JSPropertySetterInvocation psi) {
            if (ParentNode is JSExpressionStatement) {
                VisitChildren(psi);
                return;
            }

            var thisReference = psi.Invocation.ThisReference;
            var valueType = psi.GetActualType(TypeSystem);
            var thisType = thisReference.GetActualType(TypeSystem);

            JSExpression tempThis;
            JSExpression[] tempArguments = new JSExpression[psi.Invocation.Arguments.Count];
            var commaElements = new List<JSExpression>();

            tempThis = Hoist(thisReference, thisType, commaElements);

            for (var i = 0; i < tempArguments.Length; i++) {
                var arg = psi.Invocation.Arguments[i];
                var argType = arg.GetActualType(TypeSystem);

                tempArguments[i] = Hoist(arg, argType, commaElements);
            }

            var resultInvocation = psi.Invocation;

            if ((tempThis != null) || tempArguments.Any(ta => ta != null)) {
                resultInvocation = psi.Invocation.FilterArguments(
                    (i, a) => {
                        if ((i >= 0) && (tempArguments[i] != null))
                            return tempArguments[i];
                        else if ((i == -1) && (tempThis != null))
                            return tempThis;
                        else
                            return a;
                    }
                );
            }

            commaElements.Add(resultInvocation);

            if (tempArguments.Last() != null)
                commaElements.Add(tempArguments.Last());
            else
                commaElements.Add(psi.Value);

            var replacement = new JSCommaExpression(commaElements.ToArray());

            ParentNode.ReplaceChild(psi, replacement);
            VisitReplacement(replacement);
        }
Ejemplo n.º 11
0
        public void VisitNode (JSUnaryOperatorExpression uoe) {
            JSUnaryMutationOperator op;
            JSExpression target;
            TypeReference type;

            if (UnpackUnaryMutation(uoe, out op, out target, out type)) {
                var tempVar = TemporaryVariable.ForFunction(
                    Stack.Last() as JSFunctionExpression, type
                );
                var store = new JSBinaryOperatorExpression(
                    JSOperator.Assignment, tempVar, target, type
                );

                var delta = (
                    (op == JSOperator.PostIncrement) ||
                    (op == JSOperator.PreIncrement)
                )
                    ? 1
                    : -1;

                JSExpression replacement;
                if (
                    (op == JSOperator.PostIncrement) ||
                    (op == JSOperator.PostDecrement)
                ) {
                    var mutated = new JSPointerAddExpression(target, JSLiteral.New(delta), false);
                    replacement = new JSCommaExpression(store, mutated, tempVar);
                } else {
                    replacement = new JSPointerAddExpression(target, JSLiteral.New(delta), true);
                }

                ParentNode.ReplaceChild(uoe, replacement);
                VisitReplacement(replacement);
                return;
            }

            VisitChildren(uoe);
        }
Ejemplo n.º 12
0
        public void VisitNode(JSUnaryOperatorExpression uoe)
        {
            var type   = uoe.Expression.GetActualType(TypeSystem);
            var isEnum = IsEnumOrNullableEnum(type);

            if (isEnum)
            {
                var castToInt = JSInvocationExpression.InvokeMethod(
                    JS.valueOf(TypeSystem.Int32), uoe.Expression, null, true
                    );

                if (LogicalOperators.Contains(uoe.Operator))
                {
                    uoe.ReplaceChild(uoe.Expression, castToInt);
                }
                else if (uoe.Operator == JSOperator.Negation)
                {
                    uoe.ReplaceChild(uoe.Expression, castToInt);
                }
                else if (uoe.Operator is JSUnaryMutationOperator)
                {
                    if (
                        (uoe.Operator == JSOperator.PreIncrement) ||
                        (uoe.Operator == JSOperator.PreDecrement)
                        )
                    {
                        var assignment = MakeUnaryMutation(
                            uoe.Expression,
                            (uoe.Operator == JSOperator.PreDecrement)
                                ? JSOperator.Subtract
                                : JSOperator.Add,
                            type
                            );

                        ParentNode.ReplaceChild(uoe, assignment);
                        VisitReplacement(assignment);
                        return;
                    }
                    else if (
                        (uoe.Operator == JSOperator.PostIncrement) ||
                        (uoe.Operator == JSOperator.PostDecrement)
                        )
                    {
                        // FIXME: Terrible hack
                        var tempVariable = TemporaryVariable.ForFunction(
                            Stack.Last() as JSFunctionExpression, type
                            );
                        var makeTempCopy = new JSBinaryOperatorExpression(
                            JSOperator.Assignment, tempVariable, uoe.Expression, type
                            );
                        var assignment = MakeUnaryMutation(
                            uoe.Expression,
                            (uoe.Operator == JSOperator.PostDecrement)
                                ? JSOperator.Subtract
                                : JSOperator.Add,
                            type
                            );

                        var comma = new JSCommaExpression(
                            makeTempCopy,
                            assignment,
                            tempVariable
                            );

                        ParentNode.ReplaceChild(uoe, comma);
                        VisitReplacement(comma);
                        return;
                    }
                    else
                    {
                        throw new NotImplementedException("Unary mutation of enum not supported: " + uoe);
                    }
                }
            }

            VisitChildren(uoe);
        }
Ejemplo n.º 13
0
        public void VisitNode (JSUnaryOperatorExpression uoe) {
            var type = uoe.Expression.GetActualType(TypeSystem);
            var isEnum = IsEnumOrNullableEnum(type);

            if (isEnum) {
                var castToInt = JSInvocationExpression.InvokeMethod(
                    JS.valueOf(TypeSystem.Int32), uoe.Expression, null, true
                );

                if (LogicalOperators.Contains(uoe.Operator)) {
                    uoe.ReplaceChild(uoe.Expression, castToInt);
                } else if (uoe.Operator == JSOperator.Negation) {
                    uoe.ReplaceChild(uoe.Expression, castToInt);
                } else if (uoe.Operator is JSUnaryMutationOperator) {
                    if (
                        (uoe.Operator == JSOperator.PreIncrement) || 
                        (uoe.Operator == JSOperator.PreDecrement)
                    ) {
                        var assignment = MakeUnaryMutation(
                            uoe.Expression,
                            (uoe.Operator == JSOperator.PreDecrement) 
                                ? JSOperator.Subtract 
                                : JSOperator.Add,
                            type
                        );

                        ParentNode.ReplaceChild(uoe, assignment);
                        VisitReplacement(assignment);
                        return;

                    } else if (
                        (uoe.Operator == JSOperator.PostIncrement) || 
                        (uoe.Operator == JSOperator.PostDecrement)
                    ) {
                        // FIXME: Terrible hack
                        var tempVariable = TemporaryVariable.ForFunction(
                            Stack.Last() as JSFunctionExpression, type, FunctionSource
                        );
                        var makeTempCopy = new JSBinaryOperatorExpression(
                            JSOperator.Assignment, tempVariable, uoe.Expression, type
                        );
                        var assignment = MakeUnaryMutation(
                            uoe.Expression,
                            (uoe.Operator == JSOperator.PostDecrement)
                                ? JSOperator.Subtract
                                : JSOperator.Add,
                            type
                        );

                        var comma = new JSCommaExpression(
                            makeTempCopy,
                            assignment,
                            tempVariable
                        );

                        ParentNode.ReplaceChild(uoe, comma);
                        VisitReplacement(comma);
                        return;

                    } else {
                        throw new NotImplementedException("Unary mutation of enum not supported: " + uoe);
                    }
                }
            }

            VisitChildren(uoe);
        }
        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)
            {
                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.º 15
0
        public void VisitNode(JSPropertySetterInvocation psi)
        {
            if (ParentNode is JSExpressionStatement)
            {
                VisitChildren(psi);
                return;
            }

            var thisReference = psi.Invocation.ThisReference;
            var valueType     = psi.GetActualType(TypeSystem);
            var thisType      = thisReference.GetActualType(TypeSystem);

            JSExpression tempThis;

            JSExpression[] tempArguments = new JSExpression[psi.Invocation.Arguments.Count];
            var            commaElements = new List <JSExpression>();

            tempThis = Hoist(thisReference, thisType, commaElements);

            for (var i = 0; i < tempArguments.Length; i++)
            {
                var arg     = psi.Invocation.Arguments[i];
                var argType = arg.GetActualType(TypeSystem);

                tempArguments[i] = Hoist(arg, argType, commaElements);
            }

            var resultInvocation = psi.Invocation;

            if ((tempThis != null) || tempArguments.Any(ta => ta != null))
            {
                resultInvocation = psi.Invocation.FilterArguments(
                    (i, a) => {
                    if ((i >= 0) && (tempArguments[i] != null))
                    {
                        return(tempArguments[i]);
                    }
                    else if ((i == -1) && (tempThis != null))
                    {
                        return(tempThis);
                    }
                    else
                    {
                        return(a);
                    }
                }
                    );
            }

            commaElements.Add(resultInvocation);

            if (tempArguments.Last() != null)
            {
                commaElements.Add(tempArguments.Last());
            }
            else
            {
                commaElements.Add(psi.Value);
            }

            var replacement = new JSCommaExpression(commaElements.ToArray());

            ParentNode.ReplaceChild(psi, replacement);
            VisitReplacement(replacement);
        }
Ejemplo n.º 16
0
        public void VisitNode(JSUnaryOperatorExpression uoe)
        {
            var type       = uoe.Expression.GetActualType(TypeSystem);
            var isIntegral = TypeUtil.Is32BitIntegral(type);

            if (isIntegral && (uoe.Operator is JSUnaryMutationOperator))
            {
                if (
                    (uoe.Operator == JSOperator.PreIncrement) ||
                    (uoe.Operator == JSOperator.PreDecrement)
                    )
                {
                    var assignment = MakeUnaryMutation(
                        uoe.Expression,
                        (uoe.Operator == JSOperator.PreDecrement)
                            ? JSOperator.Subtract
                            : JSOperator.Add,
                        type
                        );

                    ParentNode.ReplaceChild(uoe, assignment);
                    VisitReplacement(assignment);
                    return;
                }
                else if (
                    (uoe.Operator == JSOperator.PostIncrement) ||
                    (uoe.Operator == JSOperator.PostDecrement)
                    )
                {
                    // FIXME: Terrible hack
                    var tempVariable = TemporaryVariable.ForFunction(
                        Stack.Last() as JSFunctionExpression, type
                        );
                    var makeTempCopy = new JSBinaryOperatorExpression(
                        JSOperator.Assignment, tempVariable, uoe.Expression, type
                        );
                    var assignment = MakeUnaryMutation(
                        uoe.Expression,
                        (uoe.Operator == JSOperator.PostDecrement)
                            ? JSOperator.Subtract
                            : JSOperator.Add,
                        type
                        );

                    var comma = new JSCommaExpression(
                        makeTempCopy,
                        assignment,
                        tempVariable
                        );

                    ParentNode.ReplaceChild(uoe, comma);
                    VisitReplacement(comma);
                    return;
                }
                else
                {
                    throw new NotImplementedException("Unary mutation not supported: " + uoe);
                }
            }

            VisitChildren(uoe);
        }