public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (EnumeratorsToKill.Count > 0) { // Rerun the static analyzer since we made major changes InvalidateFirstPass(); FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); // Scan to see if any of the enumerators we eliminated uses of are now // unreferenced. If they are, eliminate them entirely. foreach (var variable in EnumeratorsToKill) { var variableName = variable.Name; var accesses = ( from a in FirstPass.Accesses where a.Source.Name == variableName select a ); if (!accesses.Any()) { var eliminator = new VariableEliminator( variable, new JSNullExpression() ); eliminator.Visit(fn); } } } }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (ToDeclare.Count > 0) { int i = 0; foreach (var pd in ToDeclare) { var es = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, pd.Expression, new JSDefaultValueLiteral(pd.Type), pd.Type )); fn.Body.Statements.Insert(i++, es); } } }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new OptimizeArrayEnumerators(TypeSystem, FunctionSource); nested.Visit(fn); return; } Function = fn; FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (EnumeratorsToKill.Count > 0) { // Rerun the static analyzer since we made major changes FunctionSource.InvalidateFirstPass(Function.Method.QualifiedIdentifier); FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); // Scan to see if any of the enumerators we eliminated uses of are now // unreferenced. If they are, eliminate them entirely. foreach (var variable in EnumeratorsToKill) { var variableName = variable.Name; var accesses = ( from a in FirstPass.Accesses where a.Source.Name == variableName select a ); if (!accesses.Any()) { var eliminator = new VariableEliminator( variable, new JSNullExpression() ); eliminator.Visit(fn); } } } }
// Filters out invocations that are provably safe to ignore, based on proxy attributes/type info private FunctionAnalysis1stPass.Invocation[] FilterInvocations (FunctionAnalysis1stPass.Invocation[] invocations) { return invocations.Where( (invocation) => { if (invocation.Method == null) return true; var secondPass = GetSecondPass(invocation.Method); if (secondPass == null) return true; return (!secondPass.IsPure); } ).ToArray(); }
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; } if (replacement.SelfAndChildrenRecursive.OfType<JSBinaryOperatorExpression>().Any(boe => boe.Operator is JSAssignmentOperator)) { if (TraceLevel >= 2) Console.WriteLine("Cannot eliminate {0}; it contains an assignment.", 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 (CanSkipUsageVeto(replacement)) { 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; } } }
private FunctionAnalysis2ndPass CreateSecondPassForOverridableMethod(MethodInfo method, FunctionAnalysis1stPass firstPass) { // FIXME: Existing code is probably wrong in terms of static analysis for virtual method calls. Welp. return(new FunctionAnalysis2ndPass(this, firstPass, false)); }
private FunctionAnalysis2ndPass CreateSecondPassForKnownMethod(FunctionAnalysis1stPass firstPass) { return(new FunctionAnalysis2ndPass(this, firstPass, true)); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType<JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { bool eliminated = false; do { var nested = new EliminateSingleUseTemporaries(TypeSystem, fn.AllVariables, FunctionSource); nested.Visit(fn); eliminated = nested.EliminatedVariables.Count > 0; } while (eliminated); return; } var nullList = new List<int>(); FirstPass = FunctionSource.GetFirstPass(fn.Identifier); if (FirstPass == null) throw new InvalidOperationException(); VisitChildren(fn); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) continue; var valueType = v.GetExpectedType(TypeSystem); if (ILBlockTranslator.IsIgnoredType(valueType)) continue; var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is passed by reference.", v)); continue; } if (assignments.FirstOrDefault() == null) { if (accesses.Length == 0) { if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} because it is never used.", v)); EliminatedVariables.Add(v); EliminateVariable(fn, v, new JSEliminatedVariable(v)); } else { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Never found an initial assignment for {0}.", v)); } continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it participates in control flow.", v)); continue; } /* if ((from a in FirstPass.Assignments where v.Equals(a.Target) && a.IsConversion select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it undergoes type conversion.", v)); continue; } */ if (assignments.Length > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is reassigned.", v)); continue; } var copies = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); if ((copies.Length + accesses.Length) > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is used multiple times.", v)); continue; } var replacement = assignments.First().NewValue; if (replacement.AllChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it contains a self-reference.", v)); continue; } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is not a constant expression.", v)); continue; } if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement)); var transferDataTo = replacement as JSVariable; if (transferDataTo != null) { foreach (var access in accesses) { FirstPass.Accesses.Remove(access); FirstPass.Accesses.Add(new FunctionAnalysis1stPass.Access( access.StatementIndex, access.NodeIndex, transferDataTo, access.IsControlFlow )); } foreach (var assignment in assignments) { FirstPass.Assignments.Remove(assignment); FirstPass.Assignments.Add(new FunctionAnalysis1stPass.Assignment( assignment.StatementIndex, assignment.NodeIndex, transferDataTo, assignment.NewValue, assignment.Operator, assignment.TargetType, assignment.SourceType )); } } FirstPass.Assignments.RemoveAll((a) => v.Equals(a.Target)); FirstPass.Accesses.RemoveAll((a) => v.Equals(a.Source)); EliminatedVariables.Add(v); EliminateVariable(fn, v, replacement); } }
public void VisitNode(JSFunctionExpression fn) { var nullList = new List<int>(); 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 )); VisitChildren(fn); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) continue; var valueType = v.GetActualType(TypeSystem); if (TypeUtil.IsIgnoredType(valueType)) continue; var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); var reassignments = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); var invocations = (from i in FirstPass.Invocations where v.Name == i.ThisVariable select i).ToArray(); if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is passed by reference.", v)); continue; } if (assignments.FirstOrDefault() == null) { if ((accesses.Length == 0) && (invocations.Length == 0) && (reassignments.Length == 0)) { if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} because it is never used.", v)); 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) Debug.WriteLine(String.Format("Never found an initial assignment for {0}.", v)); } continue; } if (invocations.Length > 0) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; methods are invoked on it.", v)); continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it participates in control flow.", v)); continue; } if (assignments.Length > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is reassigned.", v)); continue; } var copies = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); if ((copies.Length + accesses.Length) > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is used multiple times.", v)); continue; } var replacement = assignments.First().NewValue; if (replacement.SelfAndChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it contains a self-reference.", v)); continue; } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is not a constant expression.", v)); continue; } if (TraceLevel >= 1) Debug.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement)); 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; } }
private FunctionAnalysis2ndPass CreateSecondPassForOverridableMethod (MethodInfo method, FunctionAnalysis1stPass firstPass) { // FIXME: Existing code is probably wrong in terms of static analysis for virtual method calls. Welp. return new FunctionAnalysis2ndPass(this, firstPass, false); }
private FunctionAnalysis2ndPass CreateSecondPassForKnownMethod (FunctionAnalysis1stPass firstPass) { return new FunctionAnalysis2ndPass(this, firstPass, true); }
public void VisitNode (JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (ToDeclare.Count > 0) { int i = 0; var vds = new JSVariableDeclarationStatement(); foreach (var pd in ToDeclare) { vds.Declarations.Add( new JSBinaryOperatorExpression( JSOperator.Assignment, pd.Expression, pd.DefaultValue ?? new JSDefaultValueLiteral(pd.Type), pd.Type ) ); } fn.Body.Statements.Insert(i++, vds); InvalidateFirstPass(); } }
public void VisitNode(JSFunctionExpression fn) { var nullList = new List<int>(); 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 )); VisitChildren(fn); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) continue; var valueType = v.GetActualType(TypeSystem); if (TypeUtil.IsIgnoredType(valueType)) continue; var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); var reassignments = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); var invocations = (from i in FirstPass.Invocations where v.Name == i.ThisVariable select i).ToArray(); if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is passed by reference.", v)); continue; } if (assignments.FirstOrDefault() == null) { if ((accesses.Length == 0) && (invocations.Length == 0) && (reassignments.Length == 0)) { if (TraceLevel >= 1) Debug.WriteLine(String.Format("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) Debug.WriteLine(String.Format("Never found an initial assignment for {0}.", v)); } continue; } if (invocations.Length > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; methods are invoked on it multiple times.", v)); continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it participates in control flow.", v)); continue; } if (assignments.Length > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is reassigned.", v)); continue; } var copies = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); if ((copies.Length + accesses.Length) > 1) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it is used multiple times.", v)); continue; } var replacementAssignment = assignments.First(); var replacement = replacementAssignment.NewValue; if (replacement.SelfAndChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("Cannot eliminate {0}; it contains a self-reference.", v)); continue; } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) Debug.WriteLine(String.Format("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; } if (replacementField != null) { var lastAccess = accesses.LastOrDefault(); var affectedFields = replacement.SelfAndChildrenRecursive.OfType<JSField>().ToArray(); bool invalidatedByLaterFieldAccess = false; foreach (var field in affectedFields) { foreach (var fieldAccess in FirstPass.FieldAccesses) { // Different field. 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 (fieldAccess.Field.Field != replacementField.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) Debug.WriteLine(String.Format("Cannot eliminate {0}; {1} is potentially mutated later", v, replacementField.Field)); invalidatedByLaterFieldAccess = true; break; } if (invalidatedByLaterFieldAccess) break; } if (invalidatedByLaterFieldAccess) continue; } if (TraceLevel >= 1) Debug.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; } } }