Beispiel #1
0
        protected void EliminateVariable(JSNode context, JSVariable variable, JSExpression replaceWith, QualifiedMemberIdentifier method)
        {
            {
                var replacer = new VariableEliminator(
                    variable,
                    JSChangeTypeExpression.New(replaceWith, variable.GetActualType(TypeSystem), TypeSystem)
                    );
                replacer.Visit(context);
            }

            {
                var replacer    = new VariableEliminator(variable, replaceWith);
                var assignments = (from a in FirstPass.Assignments where
                                   variable.Equals(a.NewValue) ||
                                   a.NewValue.SelfAndChildrenRecursive.Any(variable.Equals)
                                   select a).ToArray();

                foreach (var a in assignments)
                {
                    if (!variable.Equals(a.NewValue))
                    {
                        replacer.Visit(a.NewValue);
                    }
                }
            }

            Variables.Remove(variable.Identifier);
            FunctionSource.InvalidateFirstPass(method);
        }
Beispiel #2
0
        protected void EliminateVariable(JSNode context, JSVariable variable, JSExpression replaceWith, QualifiedMemberIdentifier method)
        {
            {
                var replacer = new VariableEliminator(
                    variable,
                    JSChangeTypeExpression.New(replaceWith, TypeSystem, variable.GetActualType(TypeSystem))
                    );
                replacer.Visit(context);
            }

            {
                var replacer    = new VariableEliminator(variable, replaceWith);
                var assignments = (from a in FirstPass.Assignments where
                                   variable.Equals(a.NewValue) ||
                                   a.NewValue.SelfAndChildrenRecursive.Any((_n) => variable.Equals(_n))
                                   select a).ToArray();

                foreach (var a in assignments)
                {
                    if (variable.Equals(a.NewValue))
                    {
                        FirstPass.Assignments.Remove(a);

                        FirstPass.Assignments.Add(
                            new FunctionAnalysis1stPass.Assignment(
                                a.ParentNodeIndices, a.StatementIndex, a.NodeIndex,
                                a.Target, replaceWith, a.Operator,
                                a.TargetType, a.SourceType
                                )
                            );
                    }
                    else
                    {
                        replacer.Visit(a.NewValue);
                    }
                }
            }

            Variables.Remove(variable.Identifier);
            FunctionSource.InvalidateFirstPass(method);
        }
Beispiel #3
0
        public void VisitNode(JSVariable variable)
        {
            if (CurrentName == "FunctionSignature")
            {
                // In argument list
                return;
            }

            if (Variable.Equals(variable))
            {
                ParentNode.ReplaceChild(variable, Replacement);
            }
            else
            {
                VisitChildren(variable);
            }
        }
Beispiel #4
0
        protected bool IsEffectivelyConstant(JSVariable target, JSExpression source)
        {
            if ((source == null) || (source.IsNull))
            {
                return(false);
            }

            // Can't eliminate struct temporaries, since that might eliminate some implied copies.
            if (TypeUtil.IsStruct(target.Type))
            {
                return(false);
            }

            // Handle special cases where our interpretation of 'constant' needs to be more flexible
            {
                var ie = source as JSIndexerExpression;
                if (ie != null)
                {
                    if (
                        IsEffectivelyConstant(target, ie.Target) &&
                        IsEffectivelyConstant(target, ie.Index)
                        )
                    {
                        return(true);
                    }
                }
            }

            {
                var ae = source as JSArrayExpression;
                if (
                    (ae != null) &&
                    (from av in ae.Values select IsEffectivelyConstant(target, av)).All((b) => b)
                    )
                {
                    return(true);
                }
            }

            {
                var de = source as JSDotExpressionBase;
                if (
                    (de != null) &&
                    IsEffectivelyConstant(target, de.Target) &&
                    IsEffectivelyConstant(target, de.Member)
                    )
                {
                    return(true);
                }
            }

            {
                var ie = source as JSInvocationExpression;
                if (
                    (ie != null) && ie.ConstantIfArgumentsAre &&
                    IsEffectivelyConstant(target, ie.ThisReference) &&
                    ie.Arguments.All((a) => IsEffectivelyConstant(target, a))
                    )
                {
                    return(true);
                }

                if ((ie != null) && (ie.JSMethod != null))
                {
                    var sa = FunctionSource.GetSecondPass(ie.JSMethod);
                    if (sa != null)
                    {
                        if (sa.IsPure)
                        {
                            if (ie.Arguments.All((a) => IsEffectivelyConstant(target, a)))
                            {
                                return(true);
                            }
                            else
                            {
                                return(false);
                            }
                        }
                    }
                }
            }

            if ((source is JSUnaryOperatorExpression) || (source is JSBinaryOperatorExpression))
            {
                if (source.Children.OfType <JSExpression>().All((_v) => IsEffectivelyConstant(target, _v)))
                {
                    return(true);
                }
            }

            if (source.IsConstant)
            {
                return(true);
            }

            // Try to find a spot between the source variable's assignments where all of our
            //  copies and accesses can fit. If we find one, our variable is effectively constant.
            var v = source as JSVariable;

            if (v != null)
            {
                var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray();
                if (assignments.Length < 1)
                {
                    return(v.IsParameter);
                }

                var targetAssignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray();
                if (targetAssignments.Length < 1)
                {
                    return(false);
                }

                var targetAccesses = (from a in FirstPass.Accesses where target.Equals(a.Source) select a).ToArray();
                if (targetAccesses.Length < 1)
                {
                    return(false);
                }

                var targetFirstAssigned = targetAssignments.FirstOrDefault();
                var targetLastAssigned  = targetAssignments.LastOrDefault();

                var targetFirstAccessed = targetAccesses.FirstOrDefault();
                var targetLastAccessed  = targetAccesses.LastOrDefault();

                bool foundAssignmentSlot = false;

                for (int i = 0, c = assignments.Length; i < c; i++)
                {
                    int assignment = assignments[i].StatementIndex, nextAssignment = int.MaxValue;
                    if (i < c - 1)
                    {
                        nextAssignment = assignments[i + 1].StatementIndex;
                    }

                    if (
                        (targetFirstAssigned.StatementIndex >= assignment) &&
                        (targetFirstAssigned.StatementIndex < nextAssignment) &&
                        (targetFirstAccessed.StatementIndex >= assignment) &&
                        (targetLastAccessed.StatementIndex <= nextAssignment)
                        )
                    {
                        foundAssignmentSlot = true;
                        break;
                    }
                }

                if (!foundAssignmentSlot)
                {
                    return(false);
                }

                return(true);
            }

            return(false);
        }
Beispiel #5
0
        public void VisitNode(JSWhileLoop whileLoop)
        {
            JSVariable       initVariable = null, lastVariable = null;
            JSBinaryOperator initOperator = null;
            JSExpression     initValue    = null;

            var prevEStmt = PreviousSibling as JSExpressionStatement;
            var prevVDS   = PreviousSibling as JSVariableDeclarationStatement;

            if (prevEStmt != null)
            {
                var boe = prevEStmt.Expression as JSBinaryOperatorExpression;
                if (
                    (boe != null) &&
                    (boe.Operator is JSAssignmentOperator) &&
                    (boe.Left is JSVariable)
                    )
                {
                    initVariable = (JSVariable)boe.Left;
                    initOperator = boe.Operator;
                    initValue    = boe.Right;
                }
            }
            else if (prevVDS != null)
            {
                var decl = prevVDS.Declarations.FirstOrDefault(
                    (d) => !d.IsNull
                    );
                if (decl != null)
                {
                    initVariable = (JSVariable)decl.Left;
                    initOperator = decl.Operator;
                    initValue    = decl.Right;
                }
            }

            var lastStatement = whileLoop.Statements.LastOrDefault();

            while ((lastStatement != null) && (lastStatement.GetType() == typeof(JSBlockStatement)))
            {
                lastStatement = ((JSBlockStatement)lastStatement).Statements.LastOrDefault();
            }

            var lastExpressionStatement = lastStatement as JSExpressionStatement;

            if (lastExpressionStatement != null)
            {
                var lastUoe = lastExpressionStatement.Expression as JSUnaryOperatorExpression;
                var lastBoe = lastExpressionStatement.Expression as JSBinaryOperatorExpression;

                if ((lastUoe != null) && (lastUoe.Operator is JSUnaryMutationOperator))
                {
                    lastVariable = lastUoe.Expression as JSVariable;
                }
                else if ((lastBoe != null) && (lastBoe.Operator is JSAssignmentOperator))
                {
                    lastVariable = lastBoe.Left as JSVariable;
                    if (
                        (lastVariable != null) &&
                        !lastBoe.Right.SelfAndChildrenRecursive.Any(
                            (n) => lastVariable.Equals(n)
                            )
                        )
                    {
                        lastVariable = null;
                    }
                }
            }

            var lastIfStatement = lastStatement as JSIfStatement;

            if (
                (lastIfStatement != null) &&
                whileLoop.Condition is JSBooleanLiteral &&
                ((JSBooleanLiteral)whileLoop.Condition).Value
                )
            {
                var innerStatement = lastIfStatement.TrueClause;
                while (innerStatement is JSBlockStatement)
                {
                    var bs = (JSBlockStatement)innerStatement;
                    if (bs.Statements.Count != 1)
                    {
                        innerStatement = null;
                        break;
                    }

                    innerStatement = bs.Statements[0];
                }

                var eStmt = innerStatement as JSExpressionStatement;

                if (eStmt != null)
                {
                    var breakExpr = eStmt.Expression as JSBreakExpression;
                    if ((breakExpr != null) && (breakExpr.TargetLoop == whileLoop.Index))
                    {
                        whileLoop.ReplaceChildRecursive(lastIfStatement, new JSNullStatement());

                        var doLoop = new JSDoLoop(
                            new JSUnaryOperatorExpression(JSOperator.LogicalNot, lastIfStatement.Condition, TypeSystem.Boolean),
                            whileLoop.Statements.ToArray()
                            );
                        doLoop.Index = whileLoop.Index;

                        ParentNode.ReplaceChild(whileLoop, doLoop);
                        VisitChildren(doLoop);
                        return;
                    }
                }
            }

            bool cantBeFor = false;

            if ((initVariable != null) && (lastVariable != null) &&
                !initVariable.Equals(lastVariable)
                )
            {
                cantBeFor = true;
            }
            else if ((initVariable ?? lastVariable) == null)
            {
                cantBeFor = true;
            }
            else if (!whileLoop.Condition.SelfAndChildrenRecursive.Any(
                         (n) => (initVariable ?? lastVariable).Equals(n)
                         ))
            {
                cantBeFor = true;
            }
            else if (
                !PostSwitchTransform && (
                    (lastStatement is JSSwitchStatement) ||
                    (lastStatement is JSLabelGroupStatement)
                    )
                )
            {
                cantBeFor = true;
            }

            if (!cantBeFor)
            {
                JSStatement initializer = null, increment = null;

                if (initVariable != null)
                {
                    initializer = PreviousSibling as JSStatement;

                    ParentNode.ReplaceChild(PreviousSibling, new JSNullStatement());
                }

                if (lastVariable != null)
                {
                    increment = lastExpressionStatement;

                    whileLoop.ReplaceChildRecursive(lastExpressionStatement, new JSNullStatement());
                }

                var forLoop = new JSForLoop(
                    initializer, whileLoop.Condition, increment,
                    whileLoop.Statements.ToArray()
                    );
                forLoop.Index = whileLoop.Index;

                ParentNode.ReplaceChild(whileLoop, forLoop);
                VisitChildren(forLoop);
            }
            else
            {
                VisitChildren(whileLoop);
            }
        }
Beispiel #6
0
        protected bool IsEffectivelyConstant(JSVariable target, JSExpression source)
        {
            if ((source == null) || (source.IsNull))
            {
                return(false);
            }

            // Can't eliminate struct temporaries, since that might eliminate some implied copies.
            if (TypeUtil.IsStruct(target.IdentifierType))
            {
                return(false);
            }

            // Handle special cases where our interpretation of 'constant' needs to be more flexible
            {
                var ie = source as JSIndexerExpression;
                if (ie != null)
                {
                    if (
                        IsEffectivelyConstant(target, ie.Target) &&
                        IsEffectivelyConstant(target, ie.Index)
                        )
                    {
                        return(true);
                    }
                }
            }

            {
                var ae = source as JSArrayExpression;
                if (
                    (ae != null) &&
                    (from av in ae.Values select IsEffectivelyConstant(target, av)).All((b) => b)
                    )
                {
                    return(true);
                }
            }

            {
                var de = source as JSDotExpressionBase;
                if (
                    (de != null) &&
                    IsEffectivelyConstant(target, de.Target) &&
                    IsEffectivelyConstant(target, de.Member)
                    )
                {
                    var pa = source as JSPropertyAccess;
                    if (pa != null)
                    {
                        // Property accesses must not be treated as constant since they call functions
                        // TODO: Use static analysis information to figure out whether the accessor is pure/has state dependencies
                        return(false);
                    }

                    return(true);
                }
            }

            {
                var ie = source as JSInvocationExpression;
                if (
                    (ie != null) && ie.ConstantIfArgumentsAre &&
                    IsEffectivelyConstant(target, ie.ThisReference) &&
                    ie.Arguments.All((a) => IsEffectivelyConstant(target, a))
                    )
                {
                    return(true);
                }

                if ((ie != null) && (ie.JSMethod != null))
                {
                    var sa = GetSecondPass(ie.JSMethod);
                    if (sa != null)
                    {
                        if (sa.IsPure)
                        {
                            if (ie.Arguments.All((a) => IsEffectivelyConstant(target, a)))
                            {
                                return(true);
                            }
                            else
                            {
                                return(false);
                            }
                        }
                    }
                }
            }

            if ((source is JSUnaryOperatorExpression) || (source is JSBinaryOperatorExpression))
            {
                if (source.Children.OfType <JSExpression>().All((_v) => IsEffectivelyConstant(target, _v)))
                {
                    return(true);
                }
            }

            if (source.IsConstant)
            {
                return(true);
            }

            // Try to find a spot between the source variable's assignments where all of our
            //  copies and accesses can fit. If we find one, our variable is effectively constant.
            // FIXME: I think this section might be fundamentally flawed. Do let me know if you agree. :)
            var v = source as JSVariable;

            if (v != null)
            {
                // Ensure that we never treat a local variable as constant if functions we call allow it to escape
                //  or modify it, because that can completely invalidate our purity analysis.
                if (VariablesExemptedFromEffectivelyConstantStatus.Contains(v.Identifier))
                {
                    return(false);
                }

                var sourceAssignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray();
                if (sourceAssignments.Length < 1)
                {
                    return(v.IsParameter);
                }

                var sourceAccesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray();
                if (sourceAccesses.Length < 1)
                {
                    return(false);
                }

                var targetAssignmentIndices = (from a in FirstPass.Assignments where target.Equals(a.Target) select a.StatementIndex);
                var targetAccessIndices     = (from a in FirstPass.Accesses where target.Equals(a.Source) select a.StatementIndex).ToArray();
                var targetUseIndices        = targetAccessIndices.Concat(targetAssignmentIndices).ToArray();

                if (sourceAssignments.Length == 1)
                {
                    if (targetAccessIndices.All((tai) => tai > sourceAssignments[0].StatementIndex))
                    {
                        return(true);
                    }
                }

                var sourceFirstAssigned = sourceAssignments.First();
                var sourceLastAssigned  = sourceAssignments.Last();

                var sourceFirstAccessed = sourceAccesses.First();
                var sourceLastAccessed  = sourceAccesses.Last();

                bool foundAssignmentSlot = false;

                for (int i = 0, c = targetUseIndices.Length; i < c; i++)
                {
                    int assignment = targetUseIndices[i], nextAssignment = int.MaxValue;
                    if (i < c - 1)
                    {
                        nextAssignment = targetUseIndices[i + 1];
                    }

                    if (
                        (sourceFirstAssigned.StatementIndex >= assignment) &&
                        (sourceLastAssigned.StatementIndex < nextAssignment) &&
                        (sourceFirstAccessed.StatementIndex >= assignment) &&
                        (sourceLastAccessed.StatementIndex <= nextAssignment)
                        )
                    {
                        if (TraceLevel >= 5)
                        {
                            Console.WriteLine("Found assignment slot for {0} <- {1} between {2} and {3}", target, source, assignment, nextAssignment);
                        }

                        foundAssignmentSlot = true;
                        break;
                    }
                }

                if (!foundAssignmentSlot)
                {
                    return(false);
                }

                return(true);
            }

            return(false);
        }