public void VisitNode(JSWriteThroughPointerExpression wtpe)
        {
            JSExpression newPointer, offset;

            if (ExtractOffsetFromPointerExpression(wtpe.Left, TypeSystem, out newPointer, out offset))
            {
                // HACK: Is this right?
                if (wtpe.OffsetInBytes != null)
                {
                    offset = new JSBinaryOperatorExpression(JSOperator.Add, wtpe.OffsetInBytes, offset, TypeSystem.Int32);
                }

                var replacement = new JSWriteThroughPointerExpression(newPointer, wtpe.Right, wtpe.ActualType, offset);
                ParentNode.ReplaceChild(wtpe, replacement);
                VisitReplacement(replacement);
            }
            else if (JSPointerExpressionUtil.UnwrapExpression(wtpe.Pointer) is JSBinaryOperatorExpression)
            {
                var replacement = new JSUntranslatableExpression("Write through confusing pointer " + wtpe.Pointer);
                ParentNode.ReplaceChild(wtpe, replacement);
                VisitReplacement(replacement);
            }
            else
            {
                VisitChildren(wtpe);
            }
        }
Exemple #2
0
        public static bool ExtractOffsetFromPointerExpression(JSExpression pointer, TypeSystem typeSystem, out JSExpression newPointer, out JSExpression offset)
        {
            pointer = JSPointerExpressionUtil.UnwrapExpression(pointer);

            offset     = null;
            newPointer = pointer;

            var boe = pointer as JSBinaryOperatorExpression;

            if (boe == null)
            {
                return(false);
            }

            if (boe.Right.IsNull)
            {
                return(false);
            }

            var right = JSPointerExpressionUtil.UnwrapExpression(boe.Right);

            var rightType  = right.GetActualType(typeSystem);
            var resultType = boe.GetActualType(typeSystem);

            if (!resultType.IsPointer)
            {
                return(false);
            }

            if (
                !TypeUtil.IsIntegral(rightType) &&
                // Adding pointers together shouldn't be valid but ILSpy generates it. Pfft.
                !TypeUtil.IsPointer(rightType)
                )
            {
                return(false);
            }

            if (boe.Operator == JSOperator.Subtract)
            {
                newPointer = boe.Left;
                offset     = new JSUnaryOperatorExpression(JSOperator.Negation, right, rightType);
                return(true);
            }
            else if (boe.Operator == JSOperator.Add)
            {
                newPointer = boe.Left;
                offset     = right;
                return(true);
            }
            else
            {
                return(false);
            }
        }
Exemple #3
0
        public void VisitNode(JSReadThroughPointerExpression rtpe)
        {
            JSExpression newPointer, offset;

            if (ExtractOffsetFromPointerExpression(rtpe.Pointer, TypeSystem, out newPointer, out offset))
            {
                var replacement = new JSReadThroughPointerExpression(newPointer, rtpe.ElementType, offset);
                ParentNode.ReplaceChild(rtpe, replacement);
                VisitReplacement(replacement);
            }
            else if (JSPointerExpressionUtil.UnwrapExpression(rtpe.Pointer) is JSBinaryOperatorExpression)
            {
                var replacement = new JSUntranslatableExpression("Read through confusing pointer " + rtpe.Pointer);
                ParentNode.ReplaceChild(rtpe, replacement);
                VisitReplacement(replacement);
            }
            else
            {
                VisitChildren(rtpe);
            }
        }
Exemple #4
0
        public void VisitNode(JSFunctionExpression fn)
        {
            FirstPass = GetFirstPass(fn.Method.QualifiedIdentifier);
            if (FirstPass == null)
            {
                throw new InvalidOperationException(String.Format(
                                                        "No first pass static analysis data for method '{0}'",
                                                        fn.Method.QualifiedIdentifier
                                                        ));
            }

            ExemptVariablesFromEffectivelyConstantStatus();

            foreach (var v in fn.AllVariables.Values.ToArray())
            {
                if (v.IsThis || v.IsParameter)
                {
                    continue;
                }

                var assignments         = (from a in FirstPass.Assignments where v.Identifier.Equals(a.Target) select a).ToArray();
                var reassignments       = (from a in FirstPass.Assignments where v.Identifier.Equals(a.SourceVariable) select a).ToArray();
                var accesses            = (from a in FirstPass.Accesses where v.Identifier.Equals(a.Source) select a).ToArray();
                var invocations         = (from i in FirstPass.Invocations where v.Name == i.ThisVariable select i).ToArray();
                var unsafeInvocations   = FilterInvocations(invocations);
                var isPassedByReference = FirstPass.VariablesPassedByRef.Contains(v.Name);

                if (assignments.FirstOrDefault() == null)
                {
                    if ((accesses.Length == 0) && (invocations.Length == 0) && (reassignments.Length == 0) && !isPassedByReference)
                    {
                        if (TraceLevel >= 1)
                        {
                            Console.WriteLine("Eliminating {0} because it is never used.", v);
                        }

                        if (!DryRun)
                        {
                            EliminatedVariables.Add(v);
                            EliminateVariable(fn, v, new JSEliminatedVariable(v), fn.Method.QualifiedIdentifier);

                            // We've invalidated the static analysis data so the best choice is to abort.
                            break;
                        }
                    }
                    else
                    {
                        if (TraceLevel >= 2)
                        {
                            Console.WriteLine("Never found an initial assignment for {0}.", v);
                        }
                    }

                    continue;
                }

                var valueType = v.GetActualType(TypeSystem);
                if (TypeUtil.IsIgnoredType(valueType))
                {
                    continue;
                }

                if (FirstPass.VariablesPassedByRef.Contains(v.Name))
                {
                    if (TraceLevel >= 2)
                    {
                        Console.WriteLine("Cannot eliminate {0}; it is passed by reference.", v);
                    }

                    continue;
                }

                if (unsafeInvocations.Length > 1)
                {
                    if (TraceLevel >= 2)
                    {
                        Console.WriteLine("Cannot eliminate {0}; methods are invoked on it multiple times that are not provably safe.", v);
                    }

                    continue;
                }

                if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null)
                {
                    if (TraceLevel >= 2)
                    {
                        Console.WriteLine("Cannot eliminate {0}; it participates in control flow.", v);
                    }

                    continue;
                }

                if (assignments.Length > 1)
                {
                    if (TraceLevel >= 2)
                    {
                        Console.WriteLine("Cannot eliminate {0}; it is reassigned.", v);
                    }

                    continue;
                }

                var replacementAssignment = assignments.First();
                var replacement           = replacementAssignment.NewValue;
                if (replacement.SelfAndChildrenRecursive.Contains(v))
                {
                    if (TraceLevel >= 2)
                    {
                        Console.WriteLine("Cannot eliminate {0}; it contains a self-reference.", v);
                    }

                    continue;
                }

                var copies = (from a in FirstPass.Assignments where v.Identifier.Equals(a.SourceVariable) select a).ToArray();
                if (
                    (copies.Length + accesses.Length) > 1
                    )
                {
                    if (replacement is JSLiteral)
                    {
                        if (TraceLevel >= 5)
                        {
                            Console.WriteLine("Skipping veto of elimination for {0} because it is a literal.", v);
                        }
                    }
                    else
                    {
                        if (TraceLevel >= 2)
                        {
                            Console.WriteLine("Cannot eliminate {0}; it is used multiple times.", v);
                        }

                        continue;
                    }
                }

                if (!IsEffectivelyConstant(v, replacement))
                {
                    if (TraceLevel >= 2)
                    {
                        Console.WriteLine("Cannot eliminate {0}; {1} is not a constant expression.", v, replacement);
                    }

                    continue;
                }

                var replacementField = JSPointerExpressionUtil.UnwrapExpression(replacement) as JSFieldAccess;
                if (replacementField == null)
                {
                    var replacementRef = replacement as JSReferenceExpression;
                    if (replacementRef != null)
                    {
                        replacementField = replacementRef.Referent as JSFieldAccess;
                    }
                }

                var _affectedFields = replacement.SelfAndChildrenRecursive.OfType <JSField>();
                if (replacementField != null)
                {
                    _affectedFields = _affectedFields.Concat(new[] { replacementField.Field });
                }

                var affectedFields = new HashSet <FieldInfo>((from jsf in _affectedFields select jsf.Field));
                _affectedFields = null;

                if ((affectedFields.Count > 0) || (replacementField != null))
                {
                    var firstAssignment = assignments.FirstOrDefault();
                    var lastAccess      = accesses.LastOrDefault();

                    bool invalidatedByLaterFieldAccess = false;

                    foreach (var fieldAccess in FirstPass.FieldAccesses)
                    {
                        // Note that we only compare the FieldInfo, not the this-reference.
                        // Otherwise, aliasing (accessing the same field through two this references) would cause us
                        //  to incorrectly eliminate a local.
                        if (!affectedFields.Contains(fieldAccess.Field.Field))
                        {
                            continue;
                        }

                        // Ignore field accesses before the replacement was initialized
                        if (fieldAccess.NodeIndex <= replacementAssignment.NodeIndex)
                        {
                            continue;
                        }

                        // If the field access comes after the last use of the temporary, we don't care
                        if ((lastAccess != null) && (fieldAccess.StatementIndex > lastAccess.StatementIndex))
                        {
                            continue;
                        }

                        // It's a read, so no impact on whether this optimization is valid
                        if (fieldAccess.IsRead)
                        {
                            continue;
                        }

                        if (TraceLevel >= 2)
                        {
                            Console.WriteLine(String.Format("Cannot eliminate {0}; {1} is potentially mutated later", v, fieldAccess.Field.Field));
                        }

                        invalidatedByLaterFieldAccess = true;
                        break;
                    }

                    if (invalidatedByLaterFieldAccess)
                    {
                        continue;
                    }

                    foreach (var invocation in FirstPass.Invocations)
                    {
                        // If the invocation comes after (or is) the last use of the temporary, we don't care
                        if ((lastAccess != null) && (invocation.StatementIndex >= lastAccess.StatementIndex))
                        {
                            continue;
                        }

                        // Same goes for the first assignment.
                        if ((firstAssignment != null) && (invocation.StatementIndex <= firstAssignment.StatementIndex))
                        {
                            continue;
                        }

                        var invocationSecondPass = GetSecondPass(invocation.Method);
                        if (
                            (invocationSecondPass == null) ||
                            (invocationSecondPass.MutatedFields == null)
                            )
                        {
                            if (invocation.ThisAndVariables.Any((kvp) => kvp.Value.ToEnumerable().Contains(v.Identifier)))
                            {
                                if (TraceLevel >= 2)
                                {
                                    Console.WriteLine(String.Format("Cannot eliminate {0}; a method call without field mutation data ({1}) is invoked between its initialization and use with it as an argument", v, invocation.Method ?? invocation.NonJSMethod));
                                }

                                invalidatedByLaterFieldAccess = true;
                            }
                        }
                        else if (affectedFields.Any(invocationSecondPass.FieldIsMutatedRecursively))
                        {
                            if (TraceLevel >= 2)
                            {
                                Console.WriteLine(String.Format("Cannot eliminate {0}; a method call ({1}) potentially mutates a field it references", v, invocation.Method ?? invocation.NonJSMethod));
                            }

                            invalidatedByLaterFieldAccess = true;
                        }
                    }

                    if (invalidatedByLaterFieldAccess)
                    {
                        continue;
                    }
                }

                if (TraceLevel >= 1)
                {
                    Console.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement));
                }

                if (!DryRun)
                {
                    EliminatedVariables.Add(v);
                    EliminateVariable(fn, v, replacement, fn.Method.QualifiedIdentifier);

                    // We've invalidated the static analysis data so the best choice is to abort.
                    break;
                }
            }
        }