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