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