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); }
protected bool DetermineIfPure() { foreach (var i in Data.Invocations) { if (i.Method == null) { return(false); } var secondPass = FunctionSource.GetSecondPass(i.Method); if (secondPass == null) { return(false); } if (!secondPass.IsPure) { return(false); } } return(_IsPure); }
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); }
protected bool IsCopyNeeded(JSExpression value) { if ((value == null) || (value.IsNull)) { return(false); } while (value is JSReferenceExpression) { value = ((JSReferenceExpression)value).Referent; } var valueType = value.GetActualType(TypeSystem); if (!TypeUtil.IsStruct(valueType)) { return(false); } if (valueType.FullName.StartsWith("System.Nullable")) { return(false); } if ( (value is JSLiteral) || (value is JSNewExpression) || (value is JSPassByReferenceExpression) ) { return(false); } if (IsImmutable(value)) { return(false); } // If the expression is a parameter that is only used once and isn't aliased, // we don't need to copy it. var rightVar = value as JSVariable; if (rightVar != null) { int referenceCount; if ( ReferenceCounts.TryGetValue(rightVar.Identifier, out referenceCount) && (referenceCount == 1) && !rightVar.IsReference && rightVar.IsParameter && !SecondPass.VariableAliases.ContainsKey(rightVar.Identifier) ) { if (Tracing) { Debug.WriteLine(String.Format("Returning false from IsCopyNeeded for parameter {0} because reference count is 1 and it has no aliases", value)); } return(false); } } var rightInvocation = value as JSInvocationExpression; if (rightInvocation == null) { return(true); } var invokeMethod = rightInvocation.JSMethod; if (invokeMethod == null) { return(true); } var secondPass = FunctionSource.GetSecondPass(invokeMethod); if (secondPass == null) { return(true); } // If this expression is the return value of a function invocation, we can eliminate struct // copies if the return value is a 'new' expression. if (secondPass.ResultIsNew) { return(false); } // We can also eliminate a return value copy if the return value is one of the function's // arguments, and we are sure that argument does not need a copy either. if (secondPass.ResultVariable != null) { var parameters = invokeMethod.Method.Parameters; int parameterIndex = -1; for (var i = 0; i < parameters.Length; i++) { if (parameters[i].Name != secondPass.ResultVariable) { continue; } parameterIndex = i; break; } if (parameterIndex < 0) { return(true); } return(IsCopyNeeded(rightInvocation.Arguments[parameterIndex])); } return(true); }
public FunctionAnalysis2ndPass(IFunctionSource functionSource, FunctionAnalysis1stPass data) { FunctionSource = functionSource; Data = data; if (data.Function.Method.Method.Metadata.HasAttribute("JSIsPure")) { _IsPure = true; } else { _IsPure = (data.StaticReferences.Count == 0) && (data.SideEffects.Count == 0); } VariableAliases = new Dictionary <string, HashSet <string> >(); foreach (var assignment in data.Assignments) { if (assignment.SourceVariable != null) { HashSet <string> aliases; if (!VariableAliases.TryGetValue(assignment.SourceVariable.Identifier, out aliases)) { VariableAliases[assignment.SourceVariable.Identifier] = aliases = new HashSet <string>(); } aliases.Add(assignment.Target.Identifier); } } var parameterNames = new HashSet <string>( from p in data.Function.Parameters select p.Name ); var parms = data.Function.Method.Method.Metadata.GetAttributeParameters("JSIL.Meta.JSMutatedArguments"); if (parms != null) { ModifiedVariables = new HashSet <string>(); foreach (var p in parms) { var s = p.Value as string; if (s != null) { ModifiedVariables.Add(s); } } } else { ModifiedVariables = new HashSet <string>( data.ModificationCount.Where((kvp) => { var isParameter = parameterNames.Contains(kvp.Key); return(kvp.Value >= (isParameter ? 1 : 2)); }).Select((kvp) => kvp.Key) ); foreach (var v in Data.VariablesPassedByRef) { ModifiedVariables.Add(v); } } parms = data.Function.Method.Method.Metadata.GetAttributeParameters("JSIL.Meta.JSEscapingArguments"); if (parms != null) { EscapingVariables = new HashSet <string>(); foreach (var p in parms) { var s = p.Value as string; if (s != null) { EscapingVariables.Add(s); } } } else { EscapingVariables = Data.EscapingVariables; // Scan over all the invocations performed by this function and see if any of them cause // a variable to escape FunctionAnalysis2ndPass invocationSecondPass; foreach (var invocation in Data.Invocations) { if (invocation.Method != null) { invocationSecondPass = functionSource.GetSecondPass(invocation.Method); } else { invocationSecondPass = null; } foreach (var invocationKvp in invocation.Variables) { bool escapes; if (invocationSecondPass != null) { escapes = invocationSecondPass.EscapingVariables.Contains(invocationKvp.Key); } else { escapes = true; } if (escapes) { foreach (var variableName in invocationKvp.Value) { Data.EscapingVariables.Add(variableName); } } } } } ResultVariable = Data.ResultVariable; ResultIsNew = Data.ResultIsNew; var seenMethods = new HashSet <string>(); var rm = Data.ResultMethod; while (rm != null) { var currentMethod = rm.QualifiedIdentifier.ToString(); if (seenMethods.Contains(currentMethod)) { break; } seenMethods.Add(currentMethod); var rmfp = functionSource.GetFirstPass(rm.QualifiedIdentifier); if (rmfp == null) { ResultIsNew = rm.Method.Metadata.HasAttribute("JSIL.Meta.JSResultIsNew"); break; } rm = rmfp.ResultMethod; ResultIsNew = rmfp.ResultIsNew; } Trace(data.Function.Method.Reference.FullName); }
protected FunctionAnalysis2ndPass GetSecondPass(JSMethod method) { return(FunctionSource.GetSecondPass(method, Member)); }