protected bool DetermineIfPure() { foreach (var i in Data.Invocations) { if (i.Method == null) { return(false); } var secondPass = FunctionCache.GetSecondPass(i.Method, Data.Identifier); if (secondPass == null) { return(false); } if (!secondPass.IsPure) { return(false); } } return(_IsPure); }
public FunctionAnalysis2ndPass(FunctionCache functionCache, FunctionAnalysis1stPass data) { FunctionAnalysis2ndPass invocationSecondPass; FunctionCache = functionCache; 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) ); if (TraceModifications && (ModifiedVariables.Count > 0)) { Console.WriteLine("Tagged variables as modified due to modification count: {0}", String.Join(", ", ModifiedVariables)); } foreach (var v in Data.VariablesPassedByRef) { if (TraceModifications) { Console.WriteLine("Tagging variable '{0}' as modified because it is passed byref", v); } 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 foreach (var invocation in Data.Invocations) { if (invocation.Method != null) { invocationSecondPass = functionCache.GetSecondPass(invocation.Method, Data.Identifier); } else { invocationSecondPass = null; } foreach (var invocationKvp in invocation.Variables) { if (invocationKvp.Value.Length == 0) { continue; } bool escapes; if (invocationSecondPass != null) { escapes = invocationSecondPass.EscapingVariables.Contains(invocationKvp.Key); } else { escapes = true; } if (escapes) { if (invocationKvp.Value.Length > 1) { // FIXME: Is this right? // Multiple variables -> a binary operator expression or an invocation. // In either case, it should be impossible for any of them to escape without being flagged otherwise. if (TraceEscapes) { Console.WriteLine( "Parameter '{0}::{1}' escapes but it is a composite so we are not flagging variables {2}", GetMethodName(invocation.Method), invocationKvp.Key, String.Join(", ", invocationKvp.Value) ); } continue; } else { var escapingVariable = invocationKvp.Value[0]; if (TraceEscapes) { Console.WriteLine("Parameter '{0}::{1}' escapes; flagging variable '{2}'", GetMethodName(invocation.Method), invocationKvp.Key, escapingVariable); } Data.EscapingVariables.Add(escapingVariable); } } } } } 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 = functionCache.GetFirstPass(rm.QualifiedIdentifier, data.Identifier); if (rmfp == null) { ResultIsNew = rm.Method.Metadata.HasAttribute("JSIL.Meta.JSResultIsNew"); break; } rm = rmfp.ResultMethod; ResultIsNew = rmfp.ResultIsNew; } if ( !data.Function.Method.Method.IsStatic && data.Function.Method.Method.DeclaringType.IsImmutable && data.ReassignsThisReference ) { ViolatesThisReferenceImmutability = true; } MutatedFields = new HashSet <FieldInfo>( from fa in data.FieldAccesses where !fa.IsRead select fa.Field.Field ); var recursivelyMutatedFields = new HashSet <HashSet <FieldInfo> >(); foreach (var invocation in Data.Invocations) { if (invocation.Method != null) { invocationSecondPass = functionCache.GetSecondPass(invocation.Method, Data.Identifier); } else { invocationSecondPass = null; } if ((invocationSecondPass == null) || (invocationSecondPass.MutatedFields == null)) { // Can't know for sure. MutatedFields = null; break; } recursivelyMutatedFields.Add(invocationSecondPass.MutatedFields); foreach (var rrmf in invocationSecondPass.RecursivelyMutatedFields) { recursivelyMutatedFields.Add(rrmf); } } RecursivelyMutatedFields = recursivelyMutatedFields.ToArray(); Trace(data.Function.Method.Reference.FullName); }