/// <summary> /// Re-run the analysis for the specified routines. Repeatively propagate the changes of their return types /// to their callers, until there are none left. /// </summary> /// <remarks> /// It is expected that the introduced changes don't change the semantics of the program and hence don't /// increase the set of possible return types of the particular routines. /// </remarks> internal void Update(IEnumerable <SourceRoutineSymbol> updatedRoutines, bool concurrent = false) { // Initialize the currently re-analysed set of methods with the given ones _currentRoutinesLastReturnTypes = new Dictionary <SourceRoutineSymbol, TypeRefMask>(); foreach (var routine in updatedRoutines) { _currentRoutinesLastReturnTypes.Add(routine, routine.ResultTypeMask); } do { foreach (var kvp in _currentRoutinesLastReturnTypes) { kvp.Key.ControlFlowGraph.FlowContext.InvalidateAnalysis(); Enqueue((T)kvp.Key.ControlFlowGraph.Start); } // Re-run the analysis with the invalidated routine flow information DoAll(concurrent); var lastMethods = _currentRoutinesLastReturnTypes; _currentRoutinesLastReturnTypes = new Dictionary <SourceRoutineSymbol, TypeRefMask>(); // Check the changes of the return types and enlist the callers for the next round foreach (var kvp in lastMethods) { if (kvp.Key.ResultTypeMask != kvp.Value) { // No other types could have been added, only removed (we're making the overapproximation more precise) Debug.Assert(((kvp.Key.ResultTypeMask & ~TypeRefMask.FlagsMask) & ~kvp.Value) == 0); var callers = _callGraph.GetCallerEdges(kvp.Key) .Select(e => e.Caller) .Where(c => !_currentRoutinesLastReturnTypes.ContainsKey(c)); // These were already reanalysed in this phase foreach (var caller in callers) { _currentRoutinesLastReturnTypes.Add(caller, caller.ResultTypeMask); } } } } while (_currentRoutinesLastReturnTypes.Count > 0); _currentRoutinesLastReturnTypes = null; }