public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (EnumeratorsToKill.Count > 0) { // Rerun the static analyzer since we made major changes InvalidateFirstPass(); FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); // Scan to see if any of the enumerators we eliminated uses of are now // unreferenced. If they are, eliminate them entirely. foreach (var variable in EnumeratorsToKill) { var variableName = variable.Name; var accesses = ( from a in FirstPass.Accesses where a.Source.Name == variableName select a ); if (!accesses.Any()) { var eliminator = new VariableEliminator( variable, new JSNullExpression() ); eliminator.Visit(fn); } } } }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); }
public void VisitNode(JSFunctionExpression fn) { Function = fn; FirstPass = GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (ToDeclare.Count > 0) { int i = 0; foreach (var pd in ToDeclare) { var es = new JSExpressionStatement( new JSBinaryOperatorExpression( JSOperator.Assignment, pd.Expression, pd.DefaultValue ?? new JSDefaultValueLiteral(pd.Type), pd.Type )); fn.Body.Statements.Insert(i++, es); } InvalidateFirstPass(); } }
public FunctionAnalysis2ndPass(IFunctionSource functionSource, MethodInfo method) { FunctionSource = functionSource; Data = null; _IsPure = method.Metadata.HasAttribute("JSIL.Meta.JSIsPure"); var parms = 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 if (!_IsPure) { ModifiedVariables = new HashSet <string>(from p in method.Parameters select p.Name); } else { ModifiedVariables = new HashSet <string>(); } parms = 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 if (!_IsPure) { EscapingVariables = new HashSet <string>(from p in method.Parameters select p.Name); } else { EscapingVariables = new HashSet <string>(); } VariableAliases = new Dictionary <string, HashSet <string> >(); ResultVariable = null; ResultIsNew = method.Metadata.HasAttribute("JSIL.Meta.JSResultIsNew"); Trace(method.Member.FullName); }
public FunctionAnalysis1stPass FirstPass(QualifiedMemberIdentifier identifier, JSFunctionExpression function) { State = new FunctionAnalysis1stPass(identifier, function); Visit(function); State.Accesses.Sort(FunctionAnalysis1stPass.ItemComparer); State.Assignments.Sort(FunctionAnalysis1stPass.ItemComparer); var result = State; State = null; if (false) { var bg = new StaticAnalysis.BarrierGenerator(TypeSystem, function); bg.Generate(); var targetFolder = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "Barriers" ); Directory.CreateDirectory(targetFolder); var typeName = function.Method.QualifiedIdentifier.Type.ToString(); var methodName = function.Method.Method.Name; if (typeName.Length >= 96) { typeName = typeName.Substring(0, 93) + "…"; } if (methodName.Length >= 32) { methodName = methodName.Substring(0, 29) + "…"; } var filename = String.Format("{0}.{1}", typeName, methodName); filename = filename.Replace("<", "").Replace(">", "").Replace("/", ""); var targetFile = Path.Combine( targetFolder, String.Format("{0}.xml", filename) ); bg.SaveXML(targetFile); } return(result); }
public FunctionAnalysis1stPass FirstPass(QualifiedMemberIdentifier identifier, JSFunctionExpression function) { State = new FunctionAnalysis1stPass(identifier, function); Visit(function); State.Accesses.Sort(FunctionAnalysis1stPass.ItemComparer); State.Assignments.Sort(FunctionAnalysis1stPass.ItemComparer); var result = State; State = null; return(result); }
public FunctionAnalysis2ndPass(FunctionCache functionCache, MethodInfo method) { FunctionCache = functionCache; Data = null; _IsPure = method.Metadata.HasAttribute("JSIL.Meta.JSIsPure"); var parms = method.Metadata.GetAttributeParameters("JSIL.Meta.JSMutatedArguments"); if (parms != null) { ModifiedVariables = new HashSet <string>(GetAttributeArguments <string>(parms)); } else if (!_IsPure) { ModifiedVariables = new HashSet <string>(from p in method.Parameters select p.Name); } else { ModifiedVariables = new HashSet <string>(); } parms = method.Metadata.GetAttributeParameters("JSIL.Meta.JSEscapingArguments"); if (parms != null) { EscapingVariables = new HashSet <string>(GetAttributeArguments <string>(parms)); } else if (!_IsPure) { EscapingVariables = new HashSet <string>(from p in method.Parameters select p.Name); } else { EscapingVariables = new HashSet <string>(); } VariableAliases = new Dictionary <string, HashSet <string> >(); ResultVariable = null; ResultIsNew = method.Metadata.HasAttribute("JSIL.Meta.JSResultIsNew"); MutatedFields = null; Trace(method.Member.FullName); }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { var nested = new OptimizeArrayEnumerators(TypeSystem, FunctionSource); nested.Visit(fn); return; } Function = fn; FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); VisitChildren(fn); if (EnumeratorsToKill.Count > 0) { // Rerun the static analyzer since we made major changes FunctionSource.InvalidateFirstPass(Function.Method.QualifiedIdentifier); FirstPass = FunctionSource.GetFirstPass(Function.Method.QualifiedIdentifier); // Scan to see if any of the enumerators we eliminated uses of are now // unreferenced. If they are, eliminate them entirely. foreach (var variable in EnumeratorsToKill) { var variableName = variable.Name; var accesses = ( from a in FirstPass.Accesses where a.Source.Name == variableName select a ); if (!accesses.Any()) { var eliminator = new VariableEliminator( variable, new JSNullExpression() ); eliminator.Visit(fn); } } } }
public void VisitNode(JSFunctionExpression fn) { // Create a new visitor for nested function expressions if (Stack.OfType <JSFunctionExpression>().Skip(1).FirstOrDefault() != null) { bool eliminated = false; do { var nested = new EliminateSingleUseTemporaries(TypeSystem, fn.AllVariables, FunctionSource); nested.Visit(fn); eliminated = nested.EliminatedVariables.Count > 0; } while (eliminated); return; } var nullList = new List <int>(); FirstPass = FunctionSource.GetFirstPass(fn.Method.QualifiedIdentifier); if (FirstPass == null) { throw new InvalidOperationException(String.Format( "No first pass static analysis data for method '{0}'", fn.Method.QualifiedIdentifier )); } VisitChildren(fn); foreach (var v in fn.AllVariables.Values.ToArray()) { if (v.IsThis || v.IsParameter) { continue; } var valueType = v.GetActualType(TypeSystem); if (TypeUtil.IsIgnoredType(valueType)) { continue; } var assignments = (from a in FirstPass.Assignments where v.Equals(a.Target) select a).ToArray(); var accesses = (from a in FirstPass.Accesses where v.Equals(a.Source) select a).ToArray(); var invocations = (from i in FirstPass.Invocations where v.Name == i.ThisVariable select i).ToArray(); if (FirstPass.VariablesPassedByRef.Contains(v.Name)) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is passed by reference.", v)); } continue; } if (assignments.FirstOrDefault() == null) { if (accesses.Length == 0) { if (TraceLevel >= 1) { Debug.WriteLine(String.Format("Eliminating {0} because it is never used.", v)); } EliminatedVariables.Add(v); EliminateVariable(fn, v, new JSEliminatedVariable(v), fn.Method.QualifiedIdentifier); } else { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Never found an initial assignment for {0}.", v)); } } continue; } if (invocations.Length > 0) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; methods are invoked on it.", v)); } continue; } if ((from a in accesses where a.IsControlFlow select a).FirstOrDefault() != null) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it participates in control flow.", v)); } continue; } if (assignments.Length > 1) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is reassigned.", v)); } continue; } var copies = (from a in FirstPass.Assignments where v.Equals(a.SourceVariable) select a).ToArray(); if ((copies.Length + accesses.Length) > 1) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is used multiple times.", v)); } continue; } var replacement = assignments.First().NewValue; if (replacement.SelfAndChildrenRecursive.Contains(v)) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it contains a self-reference.", v)); } continue; } if (!IsEffectivelyConstant(v, replacement)) { if (TraceLevel >= 2) { Debug.WriteLine(String.Format("Cannot eliminate {0}; it is not a constant expression.", v)); } continue; } if (TraceLevel >= 1) { Debug.WriteLine(String.Format("Eliminating {0} <- {1}", v, replacement)); } var transferDataTo = replacement as JSVariable; if (transferDataTo != null) { foreach (var access in accesses) { FirstPass.Accesses.Remove(access); FirstPass.Accesses.Add(new FunctionAnalysis1stPass.Access( access.ParentNodeIndices, access.StatementIndex, access.NodeIndex, transferDataTo, access.IsControlFlow )); } foreach (var assignment in assignments) { FirstPass.Assignments.Remove(assignment); FirstPass.Assignments.Add(new FunctionAnalysis1stPass.Assignment( assignment.ParentNodeIndices, assignment.StatementIndex, assignment.NodeIndex, transferDataTo, assignment.NewValue, assignment.Operator, assignment.TargetType, assignment.SourceType )); } foreach (var invocation in invocations) { FirstPass.Invocations.Remove(invocation); FirstPass.Invocations.Add(new FunctionAnalysis1stPass.Invocation( invocation.ParentNodeIndices, invocation.StatementIndex, invocation.NodeIndex, transferDataTo, invocation.Method, invocation.Variables )); } } FirstPass.Assignments.RemoveAll((a) => v.Equals(a.Target)); FirstPass.Accesses.RemoveAll((a) => v.Equals(a.Source)); EliminatedVariables.Add(v); EliminateVariable(fn, v, replacement, fn.Method.QualifiedIdentifier); } }
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; } } }
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); }
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); }
public FunctionAnalysis2ndPass(IFunctionSource functionSource, FunctionAnalysis1stPass data) { FunctionSource = functionSource; Data = data; _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 ); 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); } EscapingVariables = Data.EscapingVariables; 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); }