protected bool IsParameterCopyNeeded(FunctionAnalysis2ndPass sa, string parameterName, JSExpression expression, out GenericParameter relevantParameter) { if (!IsCopyNeeded(expression, out relevantParameter)) { return(false); } if (sa == null) { return(true); } if (!OptimizeCopies) { return(true); } bool modified = true, escapes = true, isResult = false; if (parameterName != null) { modified = sa.ModifiedVariables.Contains(parameterName); escapes = sa.EscapingVariables.Contains(parameterName); isResult = sa.ResultVariable == parameterName; } return(modified || (escapes && !isResult)); }
public bool Equals(FunctionAnalysis2ndPass rhs, out string[] _differences) { var differences = new List <string>(); if (rhs == this) { _differences = null; return(true); } if (!EscapingVariables.SequenceEqual(rhs.EscapingVariables)) { differences.Add("EscapingVariables"); } if (!ModifiedVariables.SequenceEqual(rhs.ModifiedVariables)) { differences.Add("ModifiedVariables"); } if (ResultIsNew != rhs.ResultIsNew) { differences.Add("ResultIsNew"); } if (ResultVariable != rhs.ResultVariable) { differences.Add("ResultVariable"); } if (!VariableAliases.SequenceEqual(rhs.VariableAliases)) { differences.Add("VariableAliases"); } if (ViolatesThisReferenceImmutability != rhs.ViolatesThisReferenceImmutability) { differences.Add("ViolatesThisReferenceImmutability"); } if (IsPure != rhs.IsPure) { differences.Add("IsPure"); } if (differences.Count > 0) { _differences = differences.ToArray(); return(false); } else { _differences = null; return(true); } }
public void VisitNode(JSNewExpression newexp) { FunctionAnalysis2ndPass sa = null; if (newexp.Constructor != null) { // HACK sa = GetSecondPass(new JSMethod(newexp.ConstructorReference, newexp.Constructor, MethodTypes)); } CloneArgumentsIfNecessary(newexp.Parameters, newexp.Arguments, sa); VisitChildren(newexp); }
public void VisitNode(JSFunctionExpression fn) { var countRefs = new CountVariableReferences(ReferenceCounts); countRefs.Visit(fn.Body); SecondPass = GetSecondPass(fn.Method); if (SecondPass == null) { throw new InvalidDataException("No second-pass static analysis data for function '" + fn.Method.QualifiedIdentifier + "'"); } VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new EmulateStructAssignment(TypeSystem, FunctionSource, CLR, OptimizeCopies); nested.Visit(fn); return; } var countRefs = new CountVariableReferences(ReferenceCounts); countRefs.Visit(fn.Body); SecondPass = FunctionSource.GetSecondPass(fn.Method); VisitChildren(fn); }
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)); } } } 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); }
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; } if (IsParameterCopyNeeded(sa, parameterName, argument)) { if (Tracing) { Debug.WriteLine(String.Format("struct copy introduced for argument {0}", argument)); } invocation.Arguments[i] = new JSStructCopyExpression(argument); } else { if (Tracing) { Debug.WriteLine(String.Format("struct copy elided for argument {0}", argument)); } } } VisitChildren(invocation); }
private void CloneArgumentsIfNecessary( IEnumerable <KeyValuePair <ParameterDefinition, JSExpression> > parameters, IList <JSExpression> argumentValues, FunctionAnalysis2ndPass sa ) { var parms = 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 (IsArgumentCopyNeeded(sa, parameterName, argument, out relevantParameter)) { if (TraceInsertedCopies) { Console.WriteLine(String.Format("struct copy introduced for argument #{0}: {1}", i, argument)); } argumentValues[i] = MakeCopyForExpression(argument, relevantParameter); } else { if (TraceElidedCopies && TypeUtil.IsStruct(argument.GetActualType(TypeSystem))) { Console.WriteLine(String.Format("struct copy elided for argument #{0}: {1}", i, argument)); } } } }
protected bool IsArgumentCopyNeeded(FunctionAnalysis2ndPass sa, string parameterName, JSExpression expression, out GenericParameter relevantParameter) { if (!IsCopyNeeded(expression, out relevantParameter)) { return(false); } if (sa == null) { return(true); } if (!OptimizeCopies) { return(true); } bool modified = true, escapes = true, isResult = false; if (parameterName != null) { modified = sa.IsVariableModified(parameterName); escapes = sa.DoesVariableEscape(parameterName, true); isResult = sa.ResultVariable == parameterName; } var result = modified || (escapes && !isResult); if (!result) { if (TraceElidedCopies) { Console.WriteLine("argument {0} ('{1}') needs no copy because it isn't modified and doesn't escape", expression, parameterName); } } return(result); }
protected bool IsEffectivelyConstant(JSExpression expression) { if (expression.IsConstant) { return(true); } var invocation = expression as JSInvocationExpression; FunctionAnalysis2ndPass secondPass = null; if ((invocation != null) && (invocation.JSMethod != null)) { secondPass = FunctionSource.GetSecondPass(invocation.JSMethod); if ((secondPass != null) && secondPass.IsPure) { return(true); } var methodName = invocation.JSMethod.Method.Name; if ((methodName == "IDisposable.Dispose") || (methodName == "Dispose")) { var thisType = invocation.ThisReference.GetActualType(TypeSystem); if (thisType != null) { var typeInfo = TypeInfo.GetExisting(thisType); if ((typeInfo != null) && typeInfo.Metadata.HasAttribute("JSIL.Meta.JSPureDispose")) { return(true); } } } } return(false); }
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); }
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) && !(ParentNode is JSCommaExpression) && !(thisReference is JSStructCopyExpression) && !( (ParentNode is JSResultReferenceExpression) && Stack.OfType <JSCommaExpression>().Any() ) && ( sa.ViolatesThisReferenceImmutability || sa.ModifiedVariables.Contains("this") || sa.EscapingVariables.Contains("this") ) ) { // 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) && !TypeUtil.IsStructImmutable(thisReferenceType)) { if ((thisReference is JSVariable) || (thisReference is JSFieldAccess)) { var rre = ParentNode as JSResultReferenceExpression; var cloneExpr = new JSBinaryOperatorExpression( JSOperator.Assignment, thisReference, new JSStructCopyExpression(thisReference), thisReferenceType ); var commaExpression = new JSCommaExpression( cloneExpr, (rre != null) ? (JSExpression) new JSResultReferenceExpression(invocation) : (JSExpression)invocation ); if (rre != null) { ResultReferenceReplacement = commaExpression; } else { ParentNode.ReplaceChild(invocation, commaExpression); VisitReplacement(commaExpression); } } else { invocation.ReplaceChild(thisReference, new JSStructCopyExpression(thisReference)); VisitChildren(invocation); } return; } } VisitChildren(invocation); }