System.Compiler.Framework Runs after Normalizer and Analyzer. Performs rewrites that we don't want the analyzer to see, e.g., removes calls to conditional methods. It can also take advantage of invariants learned during the Analyzer phase. Represents the pointsTo and the write effects of the current block. That is, PtWe = < PtGraph , WriteEffects , NonAnalyzableCalls >. It is a semilattice (bottom , <=). An element of the lattice. This class should be derived from in any implementation of MathematicalLattice. Returns true if a <= this. An element of the lattice. This class should be derived from in any implementation of MathematicalLattice. This is the semilattice: a PointsToGraph, the WriteEffects, and the non-analyzed calls. Assumptions made when analyzing virtual calls Method under analysis Join two PointsToAndWriteEffects Inclusion check for two PointsToAndWriteEffects Transfer function for the Method Header f(v1 = null, ptwe), operation only over the pointsToGraph Represent when the value of a variable is not longer valid This means losing information about which nodes v1 points to A more complex copy operation f(v1 = v2), operation only over the pointsToGraph f(v1 = v2), operation only over the pointsToGraph f(new Type, ptwe) , operation only over the pointsToGraph f(v1.f = v2), operates over the pointsToGraph and register the writeEffect f(v1[.] = v2), operates over the pointsToGraph and register the writeEffect f(C.f = v2), operates over the pointsToGraph and register the writeEffect f(v1 = v2.f), operates over the pointsToGraph and register the read effect f(v1 = v2[.]), operates over the pointsToGraph and register the read effect f(v1 = C.f), operates over the pointsToGraph f(v1 = &v2) f(v1 = *v2), operation only over the pointsToGraph A more complex copy operation f(*v1 = v2, operation only over the pointsToGraph f(v1 = & v2.f) f(v1 = & C.f). Register effect of the non-analyzable call in the pointsToGraph inferring the information from the callee annotations Apply the interprocedural analysis, binding information from caller and callee f(return v, ptwe), only has effect in the pointsToGraph Register the write effect in all nodes pointed by v Register the write effect in all nodes pointed by v Given a Set of effects over abstract heap locations, computes a set of effects over the method parameters or in the global scope Compute one Effects using one path from a variable an effect in an abstract heap location Verify is the parameter is read-only. That means it cannot reach a modified node. Generate the set with all the non-purity warnings using writeEffects and call information. This set is used to send information to the compiler about the non-purity warnings. Check whether a method is pure or not by analyzing the pointsTo and writeEffects info. To be pure, the parameters (or locations reachable from them) cannot be modified or accessed via escaping locations, and it cannot call non-analyzable methods. Display the ptwe Effects: Represent the semilattice of read/write effects It is a Set of < PTAnalysisNode, Field > Elements with Join and Include functions. VariableEffect: Represent a Read/Write effects over a variable That is, the variable and a chain of fields until the (read) written field Used to give format to a field dereference (.f, [.], *) The dataflow analysis for the method under analysis Implements a pretty standard data flow analysis with merging of data flow states at join points. Details: At each block, we maintain two data flow states, pending, and done. Done represents the dataflow state under which the block has already been analyzed, whereas Pending represent the state under which the block still needs to be analyzed. When a block is reached with a state, it is merged into the pending state. This merge can either be precise, or with weakening (includes now more possibilities than the two states that were merged). Once a block is dequeued for analysis, we compute the new done state, which is the merge of the pending state with the old done state. There are 3 possible outcomes: 1. The old done state completely supersedes the new pending state: no reanalysis is necessary 2. The merge is precise, then the block needs to be analyzed only with the pending state (no need to redo all cases in the done state). 3. The merge is imprecise (contains more possibilities than in either the pending or the old done state), then we need to analyze the block using this merged state in order to account for these new possibilities. Another imprecision arises from the merge of pending states. This merge could be imprecise, and therefore at the next analysis, we could be analyzing the block under extra cases. Buckets could be used to avoid this. Buckets just increase the language of formulas for expressing fixpoints to a finite disjunction. Each block can be in one of 3 states: a) unenabled (block needs to be reached by more edges before we run it) b) enabled and in work queue (needs to be rescheduled) c) enabled but has been processed with latest incoming states. Like PendingState returns the pending state for the given block, but it also sets the pending state for this block to null. old pending state Merge the new pending state with the old pending states. merged pending state Checks if a block needs to be reanalyzed and under what state. Updates the doneState of this block to reflect the pending state null if no reanalysis necessary, the DFS state if the merge is precise, the merged state if the merge is imprecise Starts the analysis from the entry block of the CFG Starts the analysis at the first instruction of the given block Push the given state onto the handler of the block. This causes call-backs to SplitException in order to correctly distribute the exception state among different nested handlers. Block from which exception escapes state on exception flow Add the given state to the pending states of the target block. If the block is enabled (by the pending edge count optimization), add the block to the worklist. Inv: DoneState => PendingState /\ PendingState != null => InQueue Cases: 1. Done => new, nothing to do 2. Done |_| new is precise. Pend' = Pend |_| new, Done' = Done |_| new 3. Done |_| new is imprecise. Pend' = Done |_| new, Done' = Done |_| new Default per block visitor. Called from Run. It calls VisitStatement on each statement in a block. The result of this method is used as the state for all normal control flow successors. To push state onto an exception handler, use the PushExceptionState method. Furthermore, for conditional branches, different states can be pushed onto the true and false branches directly by calling PushPending. In that case, null should be returned from the method in order to avoid pushing the returned state onto both true and false targets again. Default per statement visitor called from the default VisitBlock. Does identity transformation. Subclasses either override this method if the default block handling is sufficient, or they override the Visit method for blocks. The result of this method is used as the state for all normal control flow successors. To push state onto an exception handler, use the PushExceptionState method. Furthermore, for conditional branches, different states can be pushed onto the true and false branches directly by calling PushPending. In that case, null should be returned from the method in order to avoid pushing the returned state onto both true and false targets again. Compute the join of two data flow states at the given block. Predecessor block for this new state Block at which join is computed Old state at this block. Can be null, in which case the incoming state is the first non-bottom state. In this case, the method must set changed resultDiffersFromPreviousMerge to true. New data flow state flowing to this block. Boolean for fix point. If the state after the merge is equal to the old atMerge state, set to false, otherwise set to true. can be set to true if the merged result state strictly contains only information representing either the atMerge or the incoming state, but no extra approximation. If this information cannot be determined by the merge, it must return false. True can only be returned if result is truly precise. The new merged state. Splits the exceptions into the ones that this handler will handle and the ones that should currentHandlerState and nextHandlerState cannot both be null. On exit, if currentHandlerState is null, handler handles no exceptions, and if nextHandlerState is null, handler handles all the exceptions in the initial exception set of currentHandlerState. Elements can only be once on this queue. Duplicates are removed. Implements a work list as a priority queue A reference to the Main Analysis class To get information about other methods under analysis Constructor Compute the Dataflow analysis for the given method Returns true if the method is pure Verify Purity in the Final State Visit the statement. It call the instruction visitor to perform the transfer function Merge two PtWe Exception management Need Checking! Return the results of the analysis en the exit of the CFG Instruction visitor. Implement the transfer function of the dataflow analysis Decoder for CCI statements after normalization and code flattening. Provides a view of instructions close to CIL (.NET CLR Partition 3 document). The Visit method recognizes the CCI encodings for each instruction and calls the appropriate Visit* method. Subclassing InstructionVisitor allows writing visitors that deal directly with only a small set of distinct instructions and without recursive expressions, since arguments to all instructions are variables.

To make decoding simple, some MSIL instructions are split, such as ldtoken, which was split into three cases: LDTYPETOKEN, LDFIELDTOKEN, LDMETHODTOKEN.

On the other hand, some instructions are distinguished by null tests; the MSIL instructions "ldfld" (load object field) and "ldsfld" (load static field) are modeled by the same visitor LDFIELD "dest := source.field" with source being null for a static field. Similarly, an unconditional branch instruction is modelled as an BRANCH with a null condition.

The envisioned use of this class is as follows: subclass InstructionVisitor and override each Visit* method (VisitCopy, VisitLoadConstant etc.) to specify the processing that you want for each instruction. Given a Cci statement (normalized and flattened) stat, do the appropriate processing for it by calling Visit(stat, some_arg).

Visit acts as a dispatcher: it recognizes the instruction encoded by stat and calls the appropriate Visit* method with the right arguments. Visit stands for a top-level transfer function: its second argument is the argument passed to the appropriate transfer function; its result is the result of that transfer function.

NOTE: With generics, we would have defined InstructionVisitor as Visitor<A,B>, where A and B are the argument type, respectively the result type of each transfer function. If your transfer functions don't take any argument, you can pass null. Similarly, if they don't return anything, you can return null.

See the documentation of each of the Visit* methods for more info on the format of each instruction. Also see the SampleInstructionVisitor for a full example.

The following conventions apply for each Visit* method:

  1. The last two arguments are the original Cci statement and the argument passed to Visit(Statement stat, object arg)
  2. Having the original statements allows you to attach information to it (in some hashtable) or, in rare cases, to look for features that are not exposed.
  3. Each method returns an object. This is conceptually the result of the transfer function associated with that instruction.
  4. Each such method is called by the dispatcher Visit(Statement stat, object arg) with all arguments non-null, unless specified otherwise in the documentation for Visit*.

By default, each Visit* method calls the abstract method DefaultVisit.

Decodes the statement and dispatches to one of the Visit methods below. Visited statement. Argument for the transfer function. Result of the appropriate transfer function. nop -- no operation method entry -- a pseudo instruction that marks the beginning of a method. Method that starts with this instruction. All parameters of this method, including this (order is important). unwind -- a pseudo instruction that marks the exceptional exit of a method.

Description:
This is the point where the currently thrown exception leaves the method.

branch cond,target -- branch on condition.

Description:
Branch to target block if condition is true.

Condition of the branch statement; null for an unconditional jump. Target of the branching instruction.
switch selector,targets -- branch to target block indexed by selector

Description:
if selector is between 0 and targets.Length-1c> then branch to targets[selector]. Otherwise, fall though.

Selector variable. List of targets.
switchcasebottom -- a pseudo instruction equal to nop. Marks the end of a switch case when introduced by the language parser. Allows checking for fall through from one case to another. The source context should point at the case. return -- return from the method

Description:
Return the value in var from the method.

Variable that holds the returned value. null if the method return type is void.
throw -- throw the exception Variable that holds the thrown value (never null). rethrow -- rethrow the currently handled exception

Description:
Only appears within handlers. Its semantics is to rethrow the exception that the handler is currently processing.

var = catch(type) -- catch exception matching type and store in var

Description:
Starts an exception handler and acts as the test whether the handler applies to the caught exception given the type. If the exception does not apply, then control goes to the handler of the current block. Otherwise, control goes to the next instruction.

Variable that holds the caught exception. Type of the exceptions that are caught here.
dest := filter -- pseudo instruction marking the beginning of a filter handler.

Description:
Semantics: assigns the exception being filtered to the dest variable.

endfilter code -- marks the end of the filter section of a filter handler.

Description:
tests the code at the end of the filter section of a filter handler. If code is 0, handler does not apply, and the next handler should be tried. If 1, hander applies and the next instruction is executed. In our encoding, if the instruction falls through, it must push the implicit exception being handled onto the stack, since the next instruction is a catch of the actual handler.

dest := null -- ldnull instruction, assigns null to destination. The null literal. Passed here for source context. dest := ldc c -- store the constant c in dest dest := arglist -- (corresponds to the MSIL instruction OxFE00 arglist). dest := sizeof(T) -- store the runtime size of type T in dest dest := Constraint(T).receiver.callee(arguments) -- invoke virtual method with Constrain prefix. Variable that stores the result of the call. null if the called method return type is void or ignored. Receiver of the virtual call. Note that the behavior of this instruction depends on the type of the receiver in the generic instance: The receiver must have a reference type ref P. If the instance of P is a struct type, then the code becomes st0 = box(receiver, T); st0.callee(arguments); If the instance of P is a reference type (class or interface), then we simply load the indirect pointer contents: st0 = *receiver; st0.callee(arguments);
Compile-time called method; the method that is actually invoked might be different in the case of a dynamically dispatched call. Call arguments; does not include the value for the "this" argument; that value is the given by the receiver (if any). All elements of this list are Variables. The type constraint of the receiver
dest := source -- variable to variable assignment dest := new T -- allocate a new object of type T. DOES NOT INCLUDE .CTOR CALL, see below. Called for a new expression. Note that MSIL newobj instructions are broken into 3 steps: 1. A separate allocation (this method) 2. A separate constructor call (normal call) 3. A separate assignment of the newly allocated object to the intended target variable. Temporary to hold raw allocation result Object type to be allocated dest := new T[size] -- allocate a new array of the given size and type dest := receiver.callee(arguments) -- invoke method Variable that stores the result of the call. null if the called method return type is void. Receiver for virtual calls. null in the case of a static call (warning: static call and call to a static method different things: you can call a virtual method without using dynamic dyspatch).
If the callee is a member of a value type T, receiver is of type T& (reference to T). Compile-time called method; the method that is actually invoked might be different in the case of a dynamically dispatched call. Call arguments; does not include the value for the "this" argument; that value is the given by the receiver (if any). All elements of this list are Variables. Indicates whether this is a dynamically dispatched call or not.
dest := (*callee)([receiver], arguments) -- call indirect function pointer. Variable that stores the result of the call. null if the called method return type is void. Function pointer value. Receiver for virtual calls. null in the case of a static call (warning: static call and call to a static method are different things: you can call a virtual method without using dynamic dispatch). Call arguments; does not include the value for the "this" argument; that value is the given by the receiver (if any). All elements of this list are Variables. Function pointer signature. dest := operand1 op operand2 -- assign result of binary operation to dest Binary operator (e.g. NodeType.Add). dest := op operand -- assign result of unary operation to dest. Unary operator. dest := (T)source -- cast object source to type T and assign result to dest.

Description:
if type of source is a subtype of type, then assign source to dest; otherwise throw a System.InvalidCastException.

dest := source as T -- istest instruction.

Description:
If type of source is a subtype of T, assign source to dest; otherwise assign null to dest.

dest := box(T) source -- box source object of type T.

Description:
If boxable (T is a value type or type parameter), boxes the source value int a fresh object and assigns result to dest. If T is an object type, it acts as a no-op.

dest := unbox(T) source -- convert boxed value type to its raw (value) form

Description:
The instruction first checks that source is not null, otherwise throws NullReferenceException. Then it checks that the boxed value actually contains a value of type T, otherwise InvalidCastException is thrown. Finally, it copies a pointer of type T& (reference to value type) into dest that points at the box contents.

Value type of the contents expected in the boxed value.
dest := unbox.any(T) source -- convert object to type T.

Description:

  • If T is a value type, extracts the value from the boxed object source and assigns it to dest. In this case, the instruction acts like unbox followed by ldobj.
  • If T is an object type, the instruction acts like castclass.
  • If T is a type parameter, the instruction behaves dependent on the actual runtime type bound to T.

Object to unbox Target type of the unbox.any.
dest := source.field -- load field and assign to destination.

Description
This instruction covers the cases of instance AND static field read instructions. If source is null, the field is static, otherwise it is an instance field. In the case where the field is a member of a value type T, then source is of type T& (reference to T).

Variable that points to the object whose field we read. null if field is static. Loaded field (can be a static one).
dest.field := source -- store source into field.

Description:
This instruction covers the cases of instance AND static field store instructions. If dest is null, the field is static, otherwise it is an instance field. In the case where the field is a member of a value type T, then dest is of type T& (reference to T).

Variable that points to the object whose field we write. null if field is static. Written field.
dest := source[index] -- load element from array Variable that points to the array whose element we read. dest[index] := source -- store array element Variable that points to the array whose element we write. dest := & source -- load address of local variable and store into dest. Variable whose address is taken. dest := & source.field -- load address of field and store into dest.

Description:
The instruction covers both instance and static fields. For static fields, source is null. For fields of reference type T, source is of type T& (reference to T).

Variable pointing to the object that contains field; may be null is field is static. Field whose address we load (may be static).
dest := & array[index] -- load address of array element and store into dest.

Description:
Takes the address of the array element indexed and stores it into dest.

dest := *((type *) pointer) -- load value stored at pointer and assign to dest

MSIL instructions: ldind.T, ldobj

Type of the loaded value.
*((type *) pointer) := source -- store value at pointer

MSIL instructions: stind.T, stobj

Type of the stored value.
dest := &source.method -- load method pointer and store into dest.

Description:
loads the address where a method code starts. This instruction covers both ldftn and ldvirtftn: for ldvirtftn, the dynamic dispatch algorithm is used to find the appropriate method. For static functions, source is null.

Address of the object whose dynamically-dispatched method we are interested in. If source is null, then the address of this method is loaded, otherwise, we load the address of the method that is invoked by a call to source.method.
dest := ldtoken token -- load meta data token (type, method, or field) and store into dest. TypeNode / Field / Method whose metadata token is assigned to dest. memcpy(destaddr, srcaddr, size) -- cpblk instruction, copies data from memory to memory Variable that stores the start address of the destination memory area. Variable that stores the start address of the source memory area. Variable that stores the number of bytes to copy. initblk addr, value, size -- initblk instruction, initializes memory to a value Variable that stores the start address of the memory area to be initialized. Variable that stores the "unsigned int8" value that will be stored in each memory byte. Variable that stores the number of bytes to initialize. *dest := initobj T -- initobj instruction, assigns a default value to dest.

Description:
dest is managed or unmanaged pointer to T. If T is a value type, this instruction initializes each field of T to its default value or zero for primitive types. If T is an object type, this instruction has the same effect as ldnull followed by stind.

The pointer to the value type to be initialized. Is either Struct or EnumNode
break -- debugger break instruction, causes the execution to transfer control to a debugger. dest := mkrefany source,type -- assign typed reference to dest dest := refanyval source,type -- load the address out of a typed reference

Description:
Throws InvalidCastException if typed reference isn't of type type. If it is extracts the object reference and stores it in dest.

dest := refanytype source --- extracts the type from a typed reference and assigns it to dest. Default visitor called by each non-overridden visitor above. Unscrambles a method target. The method call node to unscramble Returns the receiver expression. Maybe null. Returns the method, if target is a method, otherwise null. Returns the function pointer if target is a function pointer, otherwise null. Constructor We treat this kind of call as non analyzable Not support for Function Pointers Yet Visit a call statement. It also wraps set_item (a[i]=...) and get_item (dest = a[i]) The treat VisitConstrainedCall as a Std call Can be improved... Determine if the callee method is analyzable or pure and gets or compute the callee dataflow information Visitor for copy statements dest = source Visitor for load field, it computes different values for static and instance fields Visitor for store field, it computes different values for static and instance fields Visitor for dest = source[index]. In this case very similar to load field Visitor for dest[index] = source. In this case, very similar to store field. Visitor for dest = new type Visitor for dest = new type[] Visitor for init *addr dest = conts. dest doesn't point to an object. dest = op1 op op2. dest doesn't point to an object. We record the method entry Unwind. No action performed. IT IS OK?? dest = op op1. dest doesn't point to an object. dest = sizeof type. dest doesn't point to an object. dest := source as T. We assume it as a copy dest = (type) source. Assumed as a copy Have to deal with Exception??? Box We assume it as a copy Box We assume it as a copy dest := refanytype source --- extracts the type from a typed reference and assigns it to dest. A type is not a reference, so we forget dest dest := refanyval source,type -- load the address out of a typed reference Assumed as Copy. HAVE TO DEAL with exceptions

Description:
Throws InvalidCastException if typed reference isn't of type type. If it is extracts the object reference and stores it in dest.

We assumed the function as something we don't track We forget the value of dest var = catch(type) -- catch exception matching type and store in var

Description:
Starts an exception handler and acts as the test whether the handler applies to the caught exception given the type. If the exception does not apply, then control goes to the handler of the current block. Otherwise, control goes to the next instruction.

We forget the value of var SHOULD DO SOMETHING WITH THIS!
Variable that holds the caught exception. Type of the exceptions that are caught here.
CHECK CHECK I take it as dest = * pointer If take it as *pointer = source dest = & source. In most cases the analysis consider this a std copy dest = &(source.field). Assumed as std LoadField dest = &array[index]). Assumed as std LoadElement The main analysis class. Entry point to analyze a method, an assembly or a compilation unit. At construction time, you can define if the analysis is interprocedural or only intraprocedural. If you choose interprocedural, you can decide for a fixpoint based approach, using a backward traversal over a partial call graph, or an inlining simulation (when you can decide the maximum call stack depth). The analysis has 2 modes of operation. StandAlone or assuming that is using inside CCI or Boogie. The main difference is that in the standalone it tries to analyze all methods it finds in the assembly and in the other case it only analyzes methods annotated as [pure] or [confined]. The purpose of the StandAlone mode is the INFERENCE, and of the other mode is VERIFICATION. Constructor with a given Node. We used to compute the set of nodes in this node and (optionally) bound the set of analyzable methods in the interprocedural analysis. Same constructor but with the analyzer We can filter a Node if we don't want to analyze it. For the stand alone application... Idem previous Entry point to analyze a given method. Depending of the type of analysis a call to this method can lead to a fixpoint computation, the use of a precomputed intraprocedural analysis or performing the intraprocedural analysis for the first time Perform a fixpoint computation over a set of methods (in general a strongly connected component). It perform the interprocedural analysis for each method, reanalysing any callers that require updating. Check whether dataflow analysis changes or not Analyze a given method. That is, perform the intraprocedural dataflow analysis. Check if we already computed the purity of the method under analysis. If not, the method purity is computed and saved. Determines if the method was analyzed Determine if a method has to be assumed as Pure. That is because it is annotated as pure or confined Or belong to a set of well know methods (that should be annotated in the future) Determines whether the method if analyzable or not for interProc call. That is, if we can get the method body (not abstract, not interface, under our desired analysis scope). Get the method's template if it has it Show Detailed information about the results for the method Used for test purposes Tracks the symbol table (Module) associated with the current editor window Set this to True if you want drop downs showing types and members Called for completions. Called for parameter help. Called for parameter help. Called for parameter help. Whenever a matching pair is parsed, e.g. '{' and '}', this method is called with the text span of both the left and right item. The information is used when a user types "ctrl-]" in VS to find a matching brace and when auto-highlight matching braces is enabled. Matching triples are used to highlight in bold a completed statement. For example when you type the closing brace on a foreach statement VS highlights in bold the statement that was closed. The first two source contexts are the beginning and ending of the statement that opens the block (for example, the span of the "foreach(...){" and the third source context is the closing brace for the block (e.g., the "}"). In support of Member Selection, CompleteWord, QuickInfo, MethodTip, and Autos, the StartName and QualifyName methods are called. StartName is called for each identifier that is parsed (e.g. "Console") Its type is Expression since it can be this/base etc QualifyName is called for each qualification with both the text span of the selector (e.g. ".") and the text span of the name ("WriteLine"). AutoExpression is in support of IVsLanguageDebugInfo.GetProximityExpressions. It is called for each expression that might be interesting for a user in the "Auto Debugging" window. All names that are set using StartName and QualifyName are already automatically added to the "Auto" window! This means that AutoExpression is rarely used. CodeSpan is in support of IVsLanguageDebugInfo.ValidateBreakpointLocation. It is called for each region that contains "executable" code. This is used to validate breakpoints. Comments are automatically taken care of based on TokenInfo returned from scanner. Normally this method is called when a procedure is started/ended. The StartParameters, Parameter and EndParameter methods are called in support of method tip Intellisense (ECMD_PARAMINFO). [StartParameters] is called when the parameters of a method are started, i.e. "(". [NextParameter] is called on the start of a new parameter, i.e. ",". [EndParameter] is called on the end of the parameters, i.e. ")". REVIEW: perhaps this entire scheme should go away NextParameter is called after StartParameters on the start of each new parameter, i.e. ",". EndParameter is called on the end of the parameters, i.e. ")". Send a message to the development environment. The kind of message is specified through the given severity. Represents the two drop down bars on the top of a text editor window that allow types and type members to be selected by name. The language service object that created this object and calls its SynchronizeDropdowns method The list of types that appear in the type drop down list. Sorted by full type name. The list of types that appear in the type drop down list. Textual order. The list of members that appear in the member drop down list. Sorted by name. The list of members that appear in the member drop down list. Textual order. Updates the state of the drop down bars to match the current contents of the text editor window. Call this initially and every time the cursor position changes. The editor window The line on which the cursor is now positioned The column on which the cursor is now position PtAnalysisNode: Is the base class for all nodes in the pointsToGraph It has a Label representing its position in code and the Type of the heap location it represents GNode: Represents Global (static) nodes It has neither Label nor Type INode: Represents objects created by the method under analysis An Inside Node representing the returned expression An Inside Node representing a struct PNode: Represents objects pointed by a parameter PRefNode: Represents a parameter passed by Reference POutNode: Represents an out parameter PByValueNode: Represents a parameter passed by Value LNode: Represents a placeholder for an unknown node(s) read by the instruction Typically from instructions like a = b.f with b escaping Set of nodes Edge: An edge in the pointsTo graph IEdge: An internal edge. Represents a reference created by the analyzed method (writes) OEdge: An outside edge. Represents a reference to a node outside the analyzed method (reads) StructEdge: An edge that connects an Struct Type with another element. It is used to know which elements has to be copied from a struct Set of edges LocVar: A mapping between variables a nodes it may point This mapping is used to record points to information AField: A field update. An element of a write effect A points-to Graph. Is the structure that represents the points-to information for a program point. The model supports Reference types (object subclasses) and struct types. Every program variable has a related node that represents its address. An address node can point to one of several locations (the object/s in the heap). We say that the value/s of an Address are the heap locations that the variable (or address) can point to. That means, given a = Addr(v), *a represent the actual object abstraction in the heap. For struct types Addr(v) directly contains the struct element For fields the reasoning is the same. A heap location represent an object (or several) that can have several attributes (fields). If the field type is a reference type, it will have an address, and the address will point to the heap location representing the field dereference. Example: v.f = *((*Addr(v)).f) for v and v.f being reference types. For struct type the reasoning is similar as the addr/value mentioned before. A pointsToGraph G = < I , O , LV , E > I = set of inside edges: references generated by the method under analysis O = set of outside edges: read refences to elements not known if that program points to LV = a mapping from variables to the heap locations it may point to (in particular it is a one to one mapping between variables and their addresses or associated structs) There are different kind of nodes. AddrNodes: An address of a variable or field LAddrNode: An address node created to reflex a loaded (and unknown) address LNode: A node that represent a loaded (and unknown) object. There are subclasses to represent if the unknown is a value (content of an address) like LValueNode (and PLNode for parameters values) or a field value (content of a field dereference address) like LFNode PNode: A node that represent the address of a parameter. There subclasses to represent the parameter passing style (PByValue, PByRef, POut) InsideNode: A newly created object StructNode: A node that represent a struct value type Special fields used for array indexing and pointer The set of parameter nodes The method under analysis The return value of the method This variable represent a global variable Represent the location pointed by this A mapping to access the parameter nodes using parameters Get the parameter represented by a PNode Construct an empty pointsToGraph for the method Create the parameter nodes for each method's parameter It consider the type of parameter passing (value, out, or ref) Copy Constructor Inclusion test Join to PointsTo Graphs f(methodHeader) Compute the set of backward reachable nodes from n(forward or backward) using I and O edges Compute the set of forward reachable nodes from n(forward or backward) using I and O edges Used to get only the nodes reachable using owned fields. The problem is that it is not working because of the "allField" special field. Compute the set of forward reachable nodes from n(forward or backward) using O edges Compute the set of reachable nodes using a set of starting nodes Compute the set of maximal paths from n The actual computation of the path Copy contents of Struct srcSn in Struct destSn Copy the contents pointed by a set of edges f(v1 = null). v1 points to nothing (is deleted from LV) f(v1 = v2) or f(v1 = *v2) or f(v1= &v2) or copy of struct type It is an strong update Make p1 points to nothing Remove the values assigned to a variable Remove the values assigned to an addr Bind pointsTo graph of the caller with the pointsTo graph of the callee. This is the most expensive operation. Can be skipped assuming that all calls are nonanalyzable. Updates the caller points to graph to reflect the effect of a call to the non-analyzable callee. It generates different values for the return value and out parameters and also parameter and global aliasing as parameter escaping To drive the effect it has parameters that inform about freshness of return value and out parameters, which parameters escape and whether and how the callee access to global variables and callee purity. A Generic Pair Computes the mapping that binds caller's nodes with the callee parameters It helps computes the pointsToGraph after the call node2 \in mapping[node1] sNodes \in mapping[node1] LV[args] \in mapping[n] return mapping[n] return mapping[n] U ({n} - PNode) Step 1: bind parameters and arguments Step 2 Match outsides edges from the callee (reads) with inside edges from the caller (writes). Handle cases when callee read data is created by the caller. Step 3: Match outside edges with insides edges in callee using resolved aliasing from calling context. Compute the interProc mapping between callee and caller. This is a fixpoint of steps 1,2 and 3. Computes the PTGraph that binds caller with callee Bind the caller with the callee and simplify the resulting pointsToGraph by removing the load nodes that has been resolved (captured objects) Bind an output or ref result with the argument This class is used to compute the set of potentially analyzable methods I used to filter out the methods in order to limit the interprocedural analysis scope Instruction visitor used to build the partial call graph Every method call is registered Small dataflow traversal. Just for building the partial call graph. Not exception support Visit the statement. It call the instruction visitor to perform the transfer function Exception thrown when an unknown quad is encountered. Example use of QuadVisitor. Generates an appropriate string representation for each quad. Returns a string representation for each quad. Throws an exception when encountering an unknown quad or an unknown CciHelper pattern. Enumerator over a prefix of an array (System.Array.GetEnumerator returns an enumerator over ALL the elements of the array). Constructs an enumerator over the first size elements of array. NOTE: I couldn't find any way of detecting comodification errors ... Summary description for DataStructsUtil. Produces a string representation of an IEnumerable object, using o2s to produce the string representation of each element. Conversion object -> string. Useful for classes for which we cannot modify / override the ToString method. A null value should be interpreted as the classic ToString method. Represents an object that can be copied deeply, as opposed to the shallow ICloneable. Checks whether a given element is part of this set. element searched into the set true if elem is in the set, false otherwise An attempt to make coding with datatypes more pleasant in C# Use Datatype as a base class on each of your abstract data type base classes. When doing case analysis on such a value, use the method Tag to get a String representation of the dynamic class name. This can be matched in the case branches of a switch.

For pairwise matching, use x.Tag + y.Tag and case "Foo"+"Bar".

Can be extended to compute the set of tags of each abstract datatype instance and utility methods.
Functional lists. null represents the empty list. Given two sorted lists, compute their intersection sorted list sorted list sorted intersection An abstraction for maps A Map based on a hashtable. Deep copy. Checks if values implement ICopyable. If so, copies them too. A Map based on a ListDictionary Deep copy. Checks if values implement ICopyable. If so, copies them too. Interface for the set abstraction: collection of distinct elements. Adds an element to this set. element to add true if this set was modified as a result of this operation Removes an element from this set. true if this set was modified as a result of this operation Adds several elements from this set. IEnumerable that contains the elements to be added true if this set was modified as a result of this operation Removes several elements from this set. IEnumerable containing the elements to be removed true if this set was modified as a result of this operation Deletes all the elements of this set. As a result the Count property will be 0. true if this set was modified as a result of this operation Returns null if A included in B. Otherwise, returns an element in A that is not in B. uses trivial hashtable as its set implementation Has to clean out the ArrayList, since it may contain stale keys. Full implementation of the ISet interface, backed by a Hashtable. As each HashSet is backed by a Hashtable, all requirements that apply for the Hashtable keys apply for the elements of a HashSet as well.

The HashSet class overrides the methods GetHashCode and Equals (inherited from Object) in order to provide structural equality: two sets are equal iff they contain the same elements (where the semantics of "same" is defined by the Equals method of those objects). You can put HashSets into HashSets; however, to avoid infinite loops, you should never insert a HashSet into itself. The hashcode of a HashSet is defined as the "xor" of the hashcodes of the set elements.

The GetHashCode function of a HashSet executes in O(1) time: the hashcode is dynamically updated after each operation that modifies the set. If the hashcode functions used for all the other involved objects is good and is computed in O(1) time, one element addition and removal execute in O(1) time; Equals works in time linear to the number of elements of this set.

Constructs an empty HashSet. Constructs a HashSet initialized to contain all elements from an IEnumerable. Adds an entire relation to this relation. Relation that is unioned with this relation. true iff this relation changed. Full IMutableRelation implementation. Full power relation constructor that allows you to finely tune the memory consumption. Internally, a relation is a dictionary that assigns to each key the set of values that are in relation with it. This constructor allows you to specify the dictionary and the set factory. Dictionary factory used to construct the underlying dictionary. Set factory used to construct the set that will store the values associated with each key. Default constructor. Uses the default factory for dictionaries (i.e., equiv. to new Hashtable()) and sets (i.e., equiv. to new HashSet()). "Shallow" copy of a relation. Produces an independent copy of this Relation. The keys and values are not duplicated. Operations on the resulting Relation and on this Relation don't interact. An independent copy of this Relation. Deep copy of a relation. Produces an independent copy of this Relation, in which even the keys and values are duplicated (using deep copy) if they implement the ICopyable interface. Operations on the resulting Relation and on this Relation don't interact. A really deep copy of this Relation. Returns null if object should not be returned, otherwise, returns object. Can thus change objects. "Glues" together two IEnumerable objects in a single view. Construct an enumerable that enumerators over ieable1, then ieable2. Each element is passed to the filter which can decide if the element should be returned by the enumerator or not. The filter can also change the element (map). can be null passed to filter Serial composition of two enumerators. Enumerating with a multi-enumerator is equivalent to enumerating with the first enumerator, and next with the second one. Implements the full IEnumerable interface. Aliases to the enumerators are sequentially composed are internally stored and used by the encapsulating multi-enumerator. Creates a MultiEnumerator that serially chains the two enumerators passed as arguments. Union enumerator over two IEnumerable objects. Each key is visited only once Union composition of two enumerators. Enumerating with a multi-enumerator is equivalent to enumerating over the union of the elements in the first and second enumerator. Creates a UnionEnumerator over both given enumerators. Stack-based implementation of IWorkList. Queue-based implementation of IWorkList. Returns 0 if x and y are equal, less than 0 if x is less than y and greater than 0 if x is greater than y Interface for navigating into a graph. Returns the nodes that can be reached from node by navigating one level along the graph edges. Returns the nodes that can be reached from node by navigating one level AGAINST the graph edges (i.e., from edge target to the edge source). Navigator for the graph obtained by unioning two graphs. Constructs a navigator into a graph which is the union of two graphs (where the graphs are seen as edge sets). Navigator for the first graph. Navigator for the second graph. In a union graph, the list of successors of a node includes its successors in the first graph followed by its successors in the second graph. In a union graph, the list of predecessors of a node includes the its predecessors in the first graph followed by its predecessors in the second graph. Navigator for an inverse graph. The successors (i.e., NextNodes) of a node are the predecessors of the node in the original graph. Analogously for the predecessors. Constructs a BackwardGraphNavigator that reverses an IGraphNavigator. The navigator that is reversed. Only nodes in given set are considered part of the graph. Navigator in a component graph (an acyclic graph of ISCCs). Topologically sorts the graph rooted in roots and described by nav. Throws a CyclicGraphException if the graph contains a cycle. Otherwise, returns a topologically sorted list of the graph nodes. The returned list is in ascending order: it starts with the nodes that don't have any out-arc (i.e., arcs going out of them) and ends with the nodes that don't have any in-arcs (i.e., arcs going into them). If the navigator works in constant time, the topological sort works in time linear with the number of nodes plus the number of edges. Topologically sorts a component graph: a graph whose nodes are the strongly connected components of the original graph (such a graph is clearly acyclic). Calls the full-fledged TopSortComponentGraph with the standard ISCCNavigator. The set of the root SCCs, only the SCCs reachable from these roots will be considered by the topological sort. DFS traversal of the (sub)graph rooted in a set of nodes. Roots of the traversed subgraph. The subgraph rooted in the first root will be traversed in DFS order; next, if the second root wasn't reached yet, the subgraph rooted in it will be traversed in DFS order and so on. The order of the roots is given by the corresponding IEnumerator. Navigator that describes the graph structure. Encountered nodes that satisfy this predicate will be ignored by the DFS traversal (together with their attached arcs). null corresponds to the predicate that is always false (i.e., no encountered node will be ignored). Visitor for the root node of each new subgraph: the roots (see the roots parameter) are explored in order; if a root node has not been already reached by the DFS traversal of the previous roots, new_subgraph_visitor will be called on it, and next the subgraph rooted in it will be DFS traversed. Node visitor to be called when a node is reached for the first time by the DFS traversal. null corresponds to no visitor. Node visitor to be called when the exploration of a node has finished. null corresponds to no visitor. Convenient dfs function. Call the full dfs function with new_subgraph_visitor set to null. Does a breadth first traversal of the given graph The roots of the traversal. If not null, is a predicate to avoid certain nodes If not null, called for each root that is not avoided. Called for each edges in the bf traversal, i.e., only for edges going to unvisited nodes. Walks an IR looking for preconditions that have not been satisfied Walks a System.CodeDom.CodeCompileUnit and produces a corresponding CompilationUnit. Walks the supplied System.CodeDom.CodeCompileUnit and produces a corresponding CompilationUnit. Enters declarations into the supplied Module and errors into the supplied ErrorNodeList. Calls back to the supplied compiler to resolve assembly references and to create appropriate documents for code snippets. Called upon to resolve assembly references and to create Documents for snippets. The root of the CodeDOM tree to be translated into an IR CompileUnit. The module or assembly to which the compilation unit will be compiled. Errors in the CodeDOM tree that are found during translation are added to this list. ContractSerializer is used to write a serialized form of method and type contracts into custom attributes. Currently, it just creates the serialized form as a string which must be retrieved externally and stored in a custom attribute. Note that the serialization should happen before the contracts are normalized. Eventually, I suppose this should be called in a separate pass before Normalizer is called. Currently, Checker serializes each contract (and stores it in a custom attribute). Used to print the field name from a member binding, nothing more Used to print the method name, nothing more Scans individual source lines and provides coloring and trigger information about tokens. Scans individual source lines and provides coloring and trigger information about tokens. Used to cheaply (re)initialize the scanner before scanning a small portion of text, such as single source line for syntax coloring purposes The source text portion to be scanned. May not be null. The index of the first character to be scanned. Must be a valid index into source. Scan the next token and fill in syntax coloring details about it in tokenInfo. Keeps information about token. Keeps track of scanner state. In: state after last token. Out: state after current token. Used to cheaply (re)initialize the scanner before scanning a small portion of text, such as single source line for syntax coloring purposes The source text portion to be scanned. May not be null. The index of the first character to be scanned. Must be a valid index into source. Scan the next token and fill in syntax coloring details about it in tokenInfo. Keeps information about token. Keeps track of scanner state. In: state after last token. Out: state after current token. Records the source position of a token, along with information about the syntactic significance of the token. If token has one or more triggers associated with it, it may fire one of the following actions when it is typed in a smart editor: MemberSelect - a member selection tip window TriggerMatchBraces - highlight matching braces TriggerMethodTip - a method tip window The triggers exist for speed reasons: the fast scanner determines when the slow parser might be needed. The MethodTip trigger is subdivided in four other triggers. It is the best to be as specific as possible; it is better to return ParamStart than just Param (or just MethodTip) * No editor action when this token is encountered. Display a member selection list Highlight a matching pair of braces or similar delimiter pairs Display semantic information when the pointer hovers over this token Display information about the method parameter corresponding to the call argument following this token returns sv, such that sv == function(args), or null if not mapped Assumes v1 == v2 Returns true if v1 == v2 Removes the mapping from the egraph. Semantically equivalent to setting the corresponding term to a Fresh symbolic value. Removes all mappings from the egraph of the form g(from). Merge two EGraphs. Result is null if result is no different than this. true, if result is isomorphic to this graph Merge two EGraphs. Result is null if result is no different than this. MergeInfo provides the mapping of symbolic values in the result to values in the two incoming branches. return the set of unary function symbols f, such that f(symval) = sv' exists in the egraph. Return set of equivalent terms to this symbolic value getter returns sv, such that sv == function(args) setter sets function(args) == value, is equivalent to Eliminate(f, args), followed by assume (f(args) == value) Associates symval with an abstract value. getter returns current association or Top setter sets current association (forgetting old association) return the set of constant function symbols in this egraph Returns the set of defined symbolic values in the egraph that have outgoing edges. Returns true if result is different from G1 Used to represent equalities among symbolic values Copy constructor Replay update on merge state Prints some information attached to a statement. Prints some information attached to a block. Helper class for printing an ASCII representation of the representation produced by CciHelper. Convenient version of b2s: b2id is null. Convenient wrapping of CodePrinter.b2s; used in conjunction with DataStructUtil.IEnum2String for printing collections of blocks. It would have been some much easier to have a nice Block.ToString() method ... Returns a string id for "block" based on the integer identified b2id[block]. If b2id is null, then the block hashcode is used as the block identifier. Method pretty-printer. Note: the method blocks are identified by their 0-based index in the method list of blocks. Where to print. The method to be printed Returns a string describing the signature of method: return type, method name, argument types and names (param names are not really part of the method signature, but it's nice to know them anyway). Fully qualified name of method: fullname(declaring_class).method_name Block pretty-printer. Where to print. What to print Provider of statement specific information; if non-null, it will be called after printing each statement, and its result printed too. Map block -> int identifier; if null, then the block UniqueKey is used as block id. Convenient version of the block pretty-printer, with no statement specific information and block.UniqueKey used as block id. Where to print. What to print. Convenient statement pretty-printer; the b2id map is null. Returns a textual representation of the source context info attached to statement stat. Statement whose source we're interested in. Textual representation of the source context info for stat. Statement pretty-printer. Statement to print Map block -> integer id; if null, the block UniqueKey will be used instead. String representation of the statement argument. Expression pretty-printer. Expression to print. String representation of the expression argument. Compute a pre order (ignoring back edges) of the CFG reachable from the entry node As a side effect, assigns each block its DF finishing number. Walks an IR, mutating it into a form that can be serialized to IL+MD by Writer Hook for other languages to do something different. Normal C# and Spec# don't emit any particular coercion to pointer types. NodeType.Castclass, Type is Pointer Creates a block containing the given tryBlock and catchBlocks and returns it. The method is modified by having new ExceptionHandlers added to it which points to the right places in the blocks. The type of exception caught by each catch block should be the type of the corresponding local l. The method in which the try-catch block will be inserted into. A block of statements that will be the body of the try-catch statement. A sequence of blocks; each one contains the statements that will be the body of a catch clause on the try-catch statement. The local into which the exception will be assigned. Presumably, the body of the catch clause does something with this local. A single block which must be inserted into m by the client. Generates a pre-normalized contract marker block. This class is meant to be used only by the StackDepthAnalysis and the StackRemovalTransformation: given a stack depth right before a statement, it walks over the statement, update the depth and calls some code transformers to modify the code (by default, they don't do anything, StackRemoval overrides them. Subclass of the CCI StandardVisitor that visits the fields of statements in the same order that is used by the Reader. This is important if you want to (abstractly) interpret some code. Sub-expressions have side effects, hence, the order they are visited IS important. Unfortunately, this cannot be part of the CCI because many projects rely on the semantics (if any) of the current visitor. Override this if you want to replace a Pop expression with some other expression. Expression to replace; must have type NodeType.Pop. Stack Depth right before evaluating the pop. Any valid expression; by default, it returns the argument, unchanged. Override this if you want to replace a Dup statement with some other expression. Statement to replace; must be an ExpressionStatement with the type of the expression equal to NodeType.Dup. Stack depth right before executing the dup. Any valid statement; by default, it returns the argument, unchanged. Override this if you want to replace a Call(...); Pop sequence (which is modeled by CCI as a unary expression with operator Pop and a MethodCall as its only operand). ExpressionStatement to replace. Stack depth right before the push; Any valid statement; by default, it returns the argument, unchanged. Override this if you want to replace an [implicit] Push expression expression statement with something else. Expression to replace. Stack depth right before the expression statement. Any valid expression; by default, it returns the argument, unchanged. Override this if you want to replace a Pop statement with something else. Expression to replace. Stack depth right before the Pop statement. Any valid expression; by default, it returns the argument, unchanged. Computes stack depth for each basic block and whether it is reachable from the entry. Also changes all pop, dup and push instructions into corresponding actions on stack temporary locals. Examines a CFG and removes the stack manipulating instructions by introducing some explicit variables for stack locations. After this transformation, no more Pop, Dup etc. Control Flow Graph that is modified by the transformation. This argument WILL be mutated. A map that assigns to each block from cfg the stack depth at its beginning. Useful as a pseudo-liveness information. Replace a pop expression with the appropriate stack variable. Replace a dup expression with the appropriate stack variable. Remove the Pop from a CciHelper statement "sequence" of the form "Pop expr". Replace an implicit Push with an assignment to the appropriate stack variable. Replace an explicit Pop statement with a nop: stack is modeled by stack vars now. Exposes computed existential delay information for a method Encapsulation for the variables initialization states. The state is an equality graph between locations (for tracking refs) and each location is mapped to a two point lattice (top unassigned) (bot assigned). For References, we keep track of the assignment status of the contents of the location by mapping terms of the form ValueOf(s) to assignment lattice elements as well. For structs, we additionally keep track of field individual assignment status by tracking Field(Value(s)) In addition, we keep a single set (not program point specific) of variables that were assigned, and one set of variables that were referenced. If we find variables at the end that were assigned, but not referenced, we issue the C# warning. Due to generics, we see different fields for the same field Copy Constructor Use only for non-struct values. Returns null if all fields of the struct are fully assigned, otherwise the field that is not. null if no struct type Like IsAssignedRef, but returns null if true, and a witness field if false. Non-null struct type of variable Check that value in v is (universally) delayed (and if bottom, make it delayed) true if value is delayed from here on. Check that value in v is NOT delayed (and if bottom, make it not delayed) true if value is not delayed from here on. Returns element type of reference or pointer, otherwise null Must follow field edges of value types backwards as well Constructor Required by IDataFlowAnalysis interface. Called when a merge point is first encountered. Provides the ability to remove dead variables from the state to keep its size down. depth of stack at the beginning of the block Returns a canonically sorted list of non-temp variables. Definite assignment and usage analysis. Definite assignment is about locations, not values. Thus, we use egraph as follows: Variables represent locations, thus the symbolic values in the egraph represent location addresses. We track the status of these locations (assigned, referenced) To track pointers to locations, we also keep track of terms of the form Value(loc), representing the value stored in the location loc. Thus, an assignment x = y is represented as Value(sym(x)) := Value(sym(y)) DELAYED REFERENCES In order to allow static checking of initialization of circular structures, we introduce the notion of a delayed reference. A delayed reference D(T) is a pointer whose type invariants may not be valid (and thus observed) until time T. The CLR already has such a notion in the fact that "this" is delayed in constructors until the base call. We add to this "universally quantified" delays for "This" and other parameters. We track that no reads are performed on universally delayed references and that they are stored only in other universally delayed references. Reference types T that may point to fields of delayed objects are treated specially. Intuitively, these references would have to have the same delay as the pointed to object field. Within a method, that is the case. However, the common cases of ref and out parameters that create such pointers in regular C#/Spec# code can be handled slightly better. Essentially, a delayed reference or a pointer to a field of a delayed reference should never be passed as a ref parameter, since it cannot be read prior to initialization. On the other hand, passing such pointers as Out parameters seems valid and desirable and in fact does not require the parameter to be marked [Delayed], since the out aspect already guarantees initialization prior to use. This assumes that the type of the out parameter captures all the invariants that the field should ultimately possess. Ordering: A lt B iff !A.Assigned implies !B.Assigned and !A.Unassigned implies !B.Unassigned Can round up value. Can round down value. Definite assignment checker. This checker also makes sure that all non-null fields are properly initialized prior to any possible read accesses. There are two cases: 1) if the constructor is non-delayed (i.e., the this parameter is not delayed), then the fields must be initialized prior to the base .ctor call 2) if the constructor is delayed (guaranteeing that during the constructor execution, no fields of this are ever consulted), then the fields must be initialized by the end of the constructor. Current instruction visitor. Current Block under analysis. typeSystem. Only used to file errors and warnings. Current method being analyzed. This is a repository that stores errors that has been reported. It is used basically as a set. Only used in DefiniteAssignmentInstructorVisitor.check. If the invariant that all variables have their source context holds, then the key in this set could be individual variables. The exit state. Used to check assigned but not referenced variables. Entry point of the check. It create a new instance of the checker, and run the checker on the given method. Since we push the state from each block to the exception handler, we don't need to chain them here. Figure out how to instantiate the quantified delay of the method parameters (if any). We assume that each method has the form \forall T. where T is a delay. Parameters with the "Delayed" attribute are assumed to have type Delay(T). Given the concrete arguments, this method determines if any parameter whose formal is delayed is actually delayed. [existential delay change] In addition, put the checking of existential delayed actuals here For error diagnostics purpose, we also note the position of the first delay-matched argument The instantiated formal type is non-delayed unless the parameter is marked delayed and "delayedInstance" is true. [Existential Delay Change] When there is only one delayed argument, we allow it to be existential delayed This is done by keeping track of the number of possible delay matches. if actual/formal is universal/universal, then this number (possibleDelayMatch) is not changed, we allow it only when the number is not zero (the only way it can be zero at this point (delayinstance is true) is because an existential/universal delay match happened. if actual/formal is existential/universal, then match happens only when the number is 1 number is changed to zero to disallow future existential/universal match Delay match error will reported as delay not compatible with the witness. If the witness does not have an appropriate delay we do not report an error, which will be safe (see comments below). Transforms the nested CCI representation into a flat one. More precisely it does 3 things - It turns nested block structures into a 2 level structure, the method body is a block consisting of blocks. These 2nd level blocks only contain statements, not nested blocks. All branch targets are to a block in the 2nd level. - All statements are simplified to three address codes, by splitting complicated expressions so that they push their result onto the stack, whereas the continuation pops it off the stack. - Produces only CfgBlocks In order to correctly deal with nested dup expressions which appear when the input is not read in from an external DLL, but is the output of the compiler, we need to use an explicit stack model. Consider the following problem: Construct(Delegate, [ local0, BinaryExpr(ldvirtfn, dup, Test.M) ] The dup instruction acts on the local0, which is the first argument to the constructor. Previously, we would try to use this local0 directly, without stacking it. Now we have to stack everything. Stacking everything has the undesirable side effect that data flow analyses that do branch refinement have a hard time updating information about tests, since the tests always involve stack variables. Visitor that doesn't implement any Visit method (throws new ApplicationException("unimplemented") instead.) Good if all you want to do is dispatch some specific processing for each node type, without going deep into the recursive data structure. Throwing an exception for unimplemented things is also useful for catching the untreated cases. When true, an allocation is split into a separate memory alloc, followed by an explicit constructor call. When true, use branch conditions that are literals to prune infeasible execution paths. In general, we always want this to be done. But if it is done for the code that does the runtime checking of contracts, that would influence whether the "regular" code downstream of the branch is represented in the CFG or not. Contains mappings from original ExpressionStatement to new ExpressionStatement Flattens the code of the method method. Leaves the CCI representation of method intact. Returns a mutated copy. Important! don't forget to adjust handler block boundaries as well When true, then Construct expressions are expanded into a separate allocation and separate constructor call. Flattens the code of the method method. Leaves the CCI representation of method intact. Returns a mutated copy. Important! don't forget to adjust handler block boundaries as well When true, then Construct expressions are expanded into a separate allocation and separate constructor call. When true, use constant folding to prune infeasible branches. Maintains the mapping from original blocks to new blocks so we can adjust branch targets Used to build up a list of all branch statements, so that in a post pass we can adjust their targets using the orig2newBlocks mapping Used to build up a list of all switch statements, so that in a post pass we can adjust their targets using the orig2newBlocks mapping To accumulate statements from a block. The transformed statements go onto this list. Accumulates the newly created blocks. Holds either null, or the current old block that must be mapped to the next dynamically created new block in the map. Note: Because a nested block can appear as the first statement of a block, we have to be careful. There can be multiple outstanding oldBlocks that need to be mapped to the first generated block. We do this by the following invariant: Every block expansion starts with a call to FlattenBlock The FlattenBlock stack activation frame keeps track of the prior current_oldBlock and stores the new current oldBlock. At the end of FlattenBlock, the prior_oldBlock is mapped to the same new block as the oldBlock on which FlattenBlock was called. Invariant: whenever during the traversal, we find that we need to start a new block (because we reach a block that could be the target of a branch), we have to take the new_stats StatementList and if non-empty, create a new block from it and put it onto the new_blocks list. new_stats is then initialized with a new empty list. We also have to update the orig2newblock map, using the current_oldBlock as the key and the newly created block as the target. If we update the map, we null out the current_oldBlock. if oldBlock != null, then we need to update the blockMap to map oldBlock to newBlock and set oldBlock to null. POST: oldBlock == null Can be called to recursively flatten a block from the following places: 1. From within a block on a nested block 2. From within a BlockExpression 3. From the top-level method body block Because of 2 and 3, we may have a pending list of statements in new_stats that belong to the previous block. Thus we first need to end that block, then start the new one. Furthermore, since the old block could be a branch target, we also must update the orig2newBlock map once we generated the first block within this flattening. Every block expansion starts with a call to FlattenBlock The FlattenBlock stack activation frame keeps track of the prior current_oldBlock and stores the new current oldBlock. At the end of FlattenBlock, the prior_oldBlock is mapped to the same new block as the oldBlock on which FlattenBlock was called. POST: this.current_oldBlock == null. Called once after we handled all blocks (by MakeFlat) Unify all occurrences of This BIG FIXME for CCI. Base should not occur here, since it just means "this" after normalization. Cloned Cloned Cloned Cloned Cloned Cloned Cloned Cloned Cloned Cloned Cloned Class for the stack variables introduced by the stack removal transformation. Create a stack variable for a given stack depth and a specific type. Bogus stack variable useful for desugaring of NEWwithCONSTRUCT. We hope we'll never go beyond 50000 stack vars. Actually, it would be great if we could analyze method with >50000 stack variables ! Bogus stack variable useful for desugaring of NEWwithCONSTRUCT for Value types. This variable represents the contents of the newly created value object, which does not get allocated in the heap. To test the code flattener, this visitor takes a method and expands the body expressions to add a nested BlockExpression around every sub expression it encounters. It also adds a nested block for the first statement in each block. The idea is that we can then undo this expansion with the CodeFlattener. Predicate determines if a particular call is virtual or non virtual The call to test true if virtual. Checks whether a specific parameter is an "out" parameter. Parameter to test. True if "out" parameter. Checks whether a given CciHelper statement encodes an MSIL Pop instruction. The correctness of this method depends heavily on what Herman does in the CciHelper reader ... Statement to check. true iff stat encodes an MSIL Pop instruction. Find the This node in the method body if method is an instance method. Method for which we find the This node The This node or null if method is not instance. Walks the statement list of a Block gathering information from declarations for use by forward references. Does not recurse into nested blocks. This visitor is instantiated and called by Looker. Maps identifiers to Metadata nodes (e.g. varDecl.Name -> field). Maps labels (Identifiers) to the corresponding labeled blocks (single statements are promoted to blocks for this purpose). Needed to keep local variable and labels in separate namespaces. A subset of targetFor that maps only labels (Identifiers) declared in the current block. Needed to check for duplicates. A list of all the labels encountered by Declarer. Walks the statement list of the given block gathering information from declarations for use by forward references. Does not recurse into nested blocks. The block whose declarations are to be processed Maps identifiers to Metadata nodes (e.g. Fields). Maps labels (Identifiers) to the corresponding labeled blocks (single statements are promoted to blocks for this purpose). A list of all the labels encountered by Declarer. Returns true if conversion from t3 to t1 exists and is better (closer) than the conversion from t3 to t2 Call this only if both conversions exist. Returns true if conversion from t3 to t1 exists and is better (closer) than the conversion from t3 to t2 Call this only if both conversions exist. Computes an upper bound in the type hierarchy for the set of argument types. This upper bound is a type that all types in the list are assignable to. If the types are all classes, then *the* least-upper-bound in the class hierarchy is returned. If the types contain at least one interface, then *a* deepest upper-bound is found from the intersection of the upward closure of each type. Note that if one of the types is System.Object, then that is immediately returned as the unified type without further examination of the list. A list containing the set of types from which to compute the unified type. The type corresponding to the least-upper-bound. Hook for overriding current policy of considering field reads to return non-null values Should return false if null cannot be converted to targetType. This method only handles the coercion from possibly null to non-null. It does not do any Class-Type coercions! This method only performs the non-null coercion, no other type coercions This is for general errors happened in the control flow graph analysis. Current compilation being analyzed. Type system for the compilation unit. Turn the debug information on Turn the detailed DFA debug information on Turn on statistics Debugging switch. Command line switches. Implications from command line switches (with defaults) Repository that stores the CFGs that have been built. Since building Control Flow Graph is destructive, we need to keep it for later use. Get the correct CFG for the method. return the level of the most severe error. True if compilation had no errors so far, meaning the trees are well formed and we can build CFGs. Constructor. The type system for the compilation. The complication being analyzed. Analyze the given method. Language specific flow analysis. Put general analysis targeting to general IL properties. Visit each method, start the analysis. A passer is used to pass through nodes without acting on them. All callback will be called for each child node of the passed-through node. Executes an IR, typically an expression tree, resulting in a literal node representing the computed value of the IR. This class is intended for use by a debugger expression evaluator, as well as partial evaluation (constant folding) during compilation. Has no side effects on any Cci structures, just evaluates a single node if possible. Tries to return the literal obtained from constant folding the binary expression whose literal arguments are given by opnd1 and opnd2. If any of these are null, the result is null. If the binary expression cannot be constant folded the result is also null. null or literal corresponding to binary expression's 1st constant folded argument null or literal corresponding to binary expression's 2nd constant folded argument the original binary expression null, or constant folded literal Walk IR checking for semantic errors and repairing it so that subsequent walks need not do error checking Can be called after VisitExpression to have expressions of type "reference" be automatically dereferenced Need to have this override so that inherited postconditions are not checked again. This method checks all of the methods in a class or structure to make sure that if the method implements an interface method (either explicitly or implicitly), then the contract inheritance rules are not violated. It also introduces the default expose block around the body of whichever methods should get the default. The type that will have all of its methods checked. Need to have this override so that inherited preconditions are not checked again. Create a proxy method implementing the abstractMethod and which calls the implementingMethod. This is needed when the implementingMethod is supposed to be used for the implementation of the abstractMethod, but cannot be because it lives in another assembly or isn't virtual or the abstractMethod has an out-of-band contract and the implementingMethod must have an identical type signature (i.e., no optional type modifiers for the non-null types). The type containing the implementingMethod and to which the proxy will be added as a member. The abstract method that the proxy is an implementation of. The implementing method that is supposed to implement the abstractMethod, but is unable to for various reasons. The newly created proxy method. If type has an explicit or implicit implementation of a method that has an out-of-band contract, then need to create a proxy that has the same signature as the "real" interface method and have it call the one the programmer wrote. The type whose members should be checked to find such methods. The hidden base method may still end up implementing an interface explicitly implemented by this.currentType. Prevent that by marking meth as virtual (and newslot) if that means it gets to implement a local interface method returns true if t1 is less accessible than t2 Walks a CompilationUnit and creates a scope for each namespace and each type. The scopes are attached to the corresponding instances via the ScopeFor hash table. Walks an IR, mutating it by replacing identifier nodes with NameBinding/Block nodes representing the members/labels the identifiers resolve to, and replacing type expressions with the types they refer to. Most of the logic here deals with maintaining and querying the scope chain. Used to track the appropriate scope for each type Turns ClassExpression Class.BaseClass into a real class, with substitution of template parameters. Returns false if the ClassExpression does not bind to a class. Passing false for interfaceIsError will suppress the generation of an error message. This can be used to check if the first expression in an interface list is a base class. Interface for Strongly Connected Methods. Detailed text representation of this StronglyConnectedComponent. ToString will return just a unique text id of the StronglyConnectedComponent, while the detailed text representation will be produced by FullToString Returns the nodes contained into this StronglyConnectedComponent. Returns the number of nodes in this StronglyConnectedComponent. Returns the SCCs that are end points of the arcs that starts in this StronglyConnectedComponent, i.e., the successors of this StronglyConnectedComponent in the component graph. Does not contain this StronglyConnectedComponent. Returns the SCCs that are starting points for arcs that end in this StronglyConnectedComponent, i.e., the predecessors of this StronglyConnectedComponent in the component graph. Does not contain this StronglyConnectedComponent. Checks whether this StronglyConnectedComponent is a cycle, i.e., if it has more than one node or it has a single node which points to itself. The only StronglyConnectedComponent that does not contain a cycle is a StronglyConnectedComponent composed of a single node which doesn't point to itself. StronglyConnectedComponent is a full implementation of the interface ISCC. It comes with a producer static method that constructs the component graph for a given graph. Detailed text representation of this StronglyConnectedComponent. Simplified text representation for debug purposes: "StronglyConnectedComponent" + numeric id. Use the nav navigator to explore the graph rooted in the objects from the roots set, decomposes it into strongly connected components. Returns the set of strongly connected components. This class provides compilation services for various clients such as command line compilers, in memory compilers hosted by some application such as Visual Studio, and CodeDom clients such as ASP .NET. Parses all of the CompilationUnitSnippets in the given compilation, ignoring method bodies. Then resolves all type expressions. The resulting types can be retrieved from the module in compilation.TargetModule. The base types, interfaces and member signatures will all be resolved and on an equal footing with imported, already compiled modules and assemblies. Resolves all type expressions in the given (already parsed) compilation. The base types, interfaces and member signatures will all be on an equal footing with signatures from imported, already compiled modules and assemblies. Updates the specified symbol table, substituting changedDocument for originalDocument. Fires the OnSymbolTableUpdate event before returning (provided that changes occurred to member signatures). The symbol table to update or replace. The document of a CompilationUnit instance in compilation. A new version of originalDocument. A list of the changes made to orignalDocument in order to derive changedDocument. A list to which errors detected during the update must be added. The given symbol table instance, suitably updated, or a new symbol table that replaces the given table. Updates the specified symbol table, given a list of changed members from another Compilation instance on which it has a dependency. Does nothing if the symbol table does not refer to any of the changed members. Fires the OnSymbolTableUpdate event before returning (provided that changes occurred to member signatures). The symbol table to update or replace. The compilation instance to which the given symbol table currently refers. The compilation instance to which the updated symbol table must refer to instead of to originalReference. A list of the members defined in originalReference that have changed. A list to which errors detected during the update must be added. The given symbol table instance, suitably updated, or a new symbol table that replaces the given table. For each referenced assembly Xyz, looks for a contract assembly named Xyz.Contracts.dll in our installation directory. Also looks in the same directory as Xyz (just like the xml and pdb files are looked for automatically) Provides a hook to save things other than just the module. Translates the given parse tree into a normalized form that is suitable for writing out as CLI IL. This translation process is normally accomplished by a series of visitors that are language specific derivations of base class visitors provided by the Cci code generation framework. The base Compiler class does not call the visitors directly, in order to provide language implementations with the opportunity to add or replace visitors. An IR tree that represents the parse tree for the entire compilation. Errors encountered during the compilation are appended to this list. Translates the given parse tree node into a corresponding normalized node. If the node is a type node or contains type nodes, the normalized versions of the type nodes are added to the target module. Expected to be mainly useful for compiling expressions. An IR tree that represents a parse tree A symbol table for resolving free variables in the parse tree A module to which types found in the IR tree are added Errors encountered during the compilation should be appended to this list This event happens just before UpdateSymbolTable returns, provided that UpdateSymbolTable made any changes. Called when a symbol table has been updated. The updateSpecification argument specifies how the original Compilation instance (symbol table) differs from the updated symbol table. The changedMembers argument provides a list of all member signatures that have changed as a result of the recompilation. Exposure states for variables and Objects. Set v to a new value that is abstracted by av Adds assumption that sv is exposed Adds assumption that sv is not exposed Assume all accessible locations in the heap are modified. Returns null, if result of Join is the same as atMerge. Refines the given state according to the knowledge stored in the egraph about sv In addition, the state can be null when the knowledge is inconsistent. symbolic value we assume to be false Refines the given state according to the knowledge stored in the egraph about sv In addition, the state can be null when the knowledge is inconsistent. symbolic value we assume to be non-null (true) state if sv is non-null (true) Ordering: Top / \ / \ / \ IsExposed IsNotExposed \ / \ / \ / Bottom IsExposed lt Top IsNotExposed lt Top Bottom lt IsExposed Bottom lt IsNotExposed The main class for NonNull checking. Current Exposure checking visitor Current block being analyzed. Entry point to check a method. Constructor Merge the two states for current block. Implementation of visit Block. It is called from run. It calls VisitStatement. It visits an individual statement. It is called from VisitBlock. It calls NonNullInstructionVisitor It split exceptions for current handler and the next chained handler. It will: If the exception is completely intercepted by current handler, the exception will be consumed. If the exception caught but not completely, both current handler and the next handler will take the states. If the exception is irrelevant to current caught, the next handler will take over the state. Current handler is then bypassed. Visit each instruction, check whether the modification is authorized. Current ExposureChecker Used to avoid repeated error/warning report for the same Node. Important: This is absolutely necessary, since we are doing fix-point Analysis. Bypass this sometimes means hundred's of the same error messages. Error handler. Only file an error if it has not been filed yet. Requires: the node has proper source context. Otherwise, it does not help. For the possible receiver v, check if it is nonnull. if no, file an proper error/warning. A lot of the pointers are not supported. Method entry. Need to add This pointer. Does not have to deal with parameters. Copy the source to dest. If source is nonnull, no problem. If source is null and dest is nonnulltype, Error If source is possible null and dest is nonnulltype, warning. Else, nothing. Need to maintain proper heap transformation. Note: casts don't require a non-null argument. null value casts always succeed. Perform 2 checks 1) array is non-null 2) if array element type is non-null type, then the new value written must be too. Shouldn't reach this point. The error is handled by the definite assignment analysis. So here we treat it as assume(false) A version of System.IO.Path that does not throw exceptions. Partitions IR into separate composition regions Composers are compiler extensions that are given responsibility for individual regions If variable is not a reference type, the nullness applies to the variable itself. If it is a Reference type however, the nullness applies to the contents of the reference. If variable is not a reference type, the nullness applies to the variable itself. If it is a Reference type however, the nullness applies to the contents of the reference. Exposes computed non-null information for a method Provides the non-null information for the state on edge (from, to) NonNull states for variables and Objects. For indirect pointers (refs) Returns a set of definitely null variables and a set of definitely non-null variables Set v to a new value that is abstracted by av Adds assumption that sv == null Adds assumption that v != null Check whether a given object is of nonnull type. It will check: Variable, Field, return type of Method. Assume all accessible locations in the heap are modified. Returns null, if result of Join is the same as atMerge. Refines the given state according to the knowledge stored in the egraph about sv In addition, the state can be null when the knowledge is inconsistent. symbolic value we assume to be null (false) Refines the given state according to the knowledge stored in the egraph about sv In addition, the state can be null when the knowledge is inconsistent. symbolic value we assume to be non-null (true) state if sv is non-null (true) Havoc the contents of the pointed to locations, but if non-null, reestablish that invariant. Ordering: A lt B iff !A.NonNull implies !B.NonNull The main class for NonNull checking. Current NonNull checking visitor Current block being analyzed. Used to avoid repeated error/warning report for the same Node. Important: This is absolutely necessary, since we are doing fix-point Analysis. Bypass this sometimes means hundred's of the same error messages. Error handler. Only file an error if it has not been filed yet. Requires: the node has proper source context. Otherwise, it does not help. This map keeps track of which expression statements representing a non null assertion check can be eliminated. Statements not in the map cannot be eliminated, others according to the stored value. Entry point to check a method. Constructor Merge the two states for current block. Implementation of visit Block. It is called from run. It calls VisitStatement. It visit individual statement. It is called from VisitBlock. It will call NonNullInstructionVisitor It split exceptions for current handler and the next chained handler. It will: If the exception is completely intercepted by current handler, the exception will be consumed. If the exception caught but not completely, both current handler and the next handler will take the states. If the exception is irrelevant to current caught, the next handler will take over the state. Current handler is then bypassed. Visit each instruction, check whether the modification is authorized. Current NonNullChecker For the possible receiver v, check if it is nonnull. if no, file an proper error/warning. Constructor. A lot of the pointers are not supported. Method entry. Need to add This pointer. Does not have to deal with parameters. Copy the source to dest. If source is nonnull, no problem. If source is null and dest is nonnulltype, Error If source is possible null and dest is nonnulltype, warning. Else, nothing. Need to maintain proper heap transformation. Note: casts don't require a non-null argument. null value casts always succeed. The checker assumes that all methods in AssertHelpers that have a boolean as their first argument are assertions that are called with false and will not return. Perform 2 checks 1) array is non-null 2) if array element type is non-null type, then the new value written must be too. Shouldn't reach this point. The error is handled by the definite assignment analysis. So here we treat it as assume(false) Note that the type argument is the element type, or the type of the result of the load. Walks an IR, mutating it by resolving overloads and inferring expression result types Following this, all Indexers, NameBindings, QualifiedIdentifiers and non built-in operators have been replaced by MemberBindings or MethodCalls and every expression has its Type field filled in. (Exception 1: Indexers whose objects are tuples or single dimensional zero based arrays are not replaced.) (Exception 2: When resolution fails the NameBindings and QualifiedIdentifiers are not replaced. Checker uses them to generate appropriate errors.) Are we inside an assert statement, assume statement, loop invariant, requires clause, or ensures clause, but not in an object invariant? We pass the argument expression in case we are dealing with an implicit delegate construction This is the actual argument expression Go through eligible members (+ bestSoFar) returning the one with the best match to argTypes. Returns null if there is no single best match. Sets bestParamTypes to a signature that any other member (from a base class) has to equal or better to best overall. A refinement of blocks that the code flattener produces (single entry, single exit at end) The block also serves to cache various information once we build the CFG. Returns a list of CfgBlock that are handlers of the current block, handling an exception of the given type, or a subtype thereof. Type of exception thrown. It is assumed that any actual subtype could be thrown All handlers that could apply directly to this exception. In addition, if the method might not handle it, then the ExceptionExit block is part of this list. Returns best effort node with source context for the beginning of the block Returns best effort node with source context for the beginning of the block Returns an index of 0..n of this block within the CFG it is part of. Allows using arrays as tables indexed by blocks. Returns the stack depth at the beginning of the block Return statement in block. Returns number of statements in block ICFGFactory with caching. Get the CFG for a method. Results are cached, so getting the CFG for the same method twice will return the same CFG object. Method whose CFG we want to get. CFG for method; cached. Flushes the CFG for method from the internal cache. Method whose CFG we want to flush from the cache. /// Control Flow Graph (CFG) for a method. The CFG is an extra layer on top of the CCI representation; all the CFG related information (flow edges) is maintained into the CFG object. Both the normal and the exceptional flows are modeled by the CFG.

THE UNDERLYING CCI REPRESENTATION IS MUTATED *A LOT* BY THE "FINALLY" BLOCK DUPLICATION AND THE STACK REMOVAL TRANSFORMATION. YOU SHOULD MANUALLY CLONE IT BEFORE CONSTRUCTING THE CFG IF YOU NEED IT; E.G, IF YOU NEED TO WRITE THE CODE BACK TO DISK. CFG IS USED FOR PROGRAM ANALYSIS ONLY, NOT FOR CODE GENERATION.

A Control Flow Graph is basically an oriented graph whose nodes are the basic blocks from the method body; the edges reflect the normal and the exceptional flow of control (the exceptional flow is the flow that occurs when an exception is raised). In addition to the basic blocks of the original method, three more blocks are added:

  • a special CFG.NormalExitBlock that is a successor for all the basic block terminated in a return instruction; it is a merge point for all the paths on the normal (intra-procedural) control flow.
  • a special CFG.ExcpExitBlock that is the default handler for all the uncaught exceptions; it is a merge point for all the paths on the intra-procedural execution paths that may terminate with an uncaught exception.
  • a special CFG.ExitBlock that is the only successor of the aforementioned normal and the exception exit. Its only normal flow predecessor is the special block for the normal exit and its only exceptional flow predecessor is the special block for the exceptional exit.

If you are given a block and want to know if it's the [normal/excp] exit of its method, all you have to do is use the appropriate "is" test (e.g. "block is CFG.NormalExitBlock"). If you have the CFG, and want to know its [normal/excp] exit, you just have to query the appropriate method.

NOTE: If an analysis is interested in the result for the normal flow, then it can retrieve the result of the dataflow equations for the normal exit point. Similarly, if an analysis is interested in the result for the exceptional flow, then it can retrieve the result of the dataflow equations for the exceptional exit point. Finally, if the distinction between normal/exceptional flow is not important, the "unified" exit point can be used instead.

Constructs the Control Flow Graph for method. If the code for method is unavailable, throw an UnavailableCodeException (a constructor cannot return an error code). If the code for method is empty (abstract method), thrown a NoCodeException. Otherwise, examine the CciHelper representation of the method code and construct the CFG. Method whose CFG will be constructed. If true, the finally blocks will be duplicated and you'll obtain a real CFG. Otherwise, you'll have to manually deal with the finally blocks that are traversed by each "leave" instruction. HIGHLY RECOMMENDED! If true, StackRemovalTransf.Process will be called to remove the stack manipulating operations. See more commends in the class StackRemovalTransf. RECOMMENDED! Constructs the Control Flow Graph for method. If the code for method is unavailable, throw an UnavailableCodeException (a constructor cannot return an error code). If the code for method is empty (abstract method), thrown a NoCodeException. Otherwise, examine the CciHelper representation of the method code and construct the CFG. Method whose CFG will be constructed. If true, the finally blocks will be duplicated and you'll obtain a real CFG. Otherwise, you'll have to manually deal with the finally blocks that are traversed by each "leave" instruction. HIGHLY RECOMMENDED! If true, StackRemovalTransf.Process will be called to remove the stack manipulating operations. See more commends in the class StackRemovalTransf. RECOMMENDED! When true, use constant folding to prune infeasible branches. Convenient CFG constructor: by default, the finally blocks are duplicated to obtain a real CFG, the stack removal transformation is applied, and constant-folding for branches is done. Method whose CFG is produced. Convenient CFG constructor: by default, the finally blocks are duplicated to obtain a real CFG and the stack removal transformation is applied. Method whose CFG is produced. When true, prune infeasible paths due to branch conditions that are constants. Add explicit Catch statements at beginning of each catch handler. If a Catch handler has a next handler that differs from the ExceptionHandler enclosing the handler block, then we split the Catch statement into a separate block. Special case for Finally handlers that have been turned into catch handlers by the finally-elimination: - Move the special instruction FINALLYVARPREFIX<n> = pop() to the header. Construct a continuation description for each block Computes the ExceptionHandler for each block and the chaining of exception handlers. Note: Filter handlers are currently treated as starting at the filter expression and the endfilter is like a fall through into the actual handler. Returns an array containing all the blocks from this CFG. You should never mutate this array. If statement is part of this CFG, returns the containing block, otherwise null Returns the topologically sorted list of the strongly connected components of blocks (according to the control flow). The first SCC in the returned list is the one that contains the method entry block. Top-sort list of SCCs of blocks from this CFG (method entry first). Returns the successors of block on both normal and exceptional flow. Iterating over the returned IEnumerable is equivalent to iterating first over the normal successors and next over the exception flow successors. Returns the normal successors of block. You should never mutate this array. Returns the closest enclosing handler of a particular block where control goes if an exception is raised inside block. Returns the predecessors of block on both normal and exceptional flow. Iterating over the returned IEnumerable is equivalent to iterating first over the normal predecessors and next over the exception flow predecessors. Returns the normal predecessors of block. You should never mutate the returned array. Returns the predecessors of block on the exceptional flow of control. In C# terms, if block is the beginning of an exception handler, these are the blocks protected by that exception handler. Otherwise, this array has length 0. You should never mutate the returned array. Returns the successor of block on the exceptional flow of control. You should never mutate the returned array. Returns the ExceptionHandler that starts at block, if any; null otherwise. This is useful when trying to see what kind of exceptions can arrive in block Useful for finding which handler a block belongs to. This may be needed for determining rethrow information. This is NOT the handler protecting the block! Start block of handler containing the given block. Otherwise null, if given block is not part of any handler. FOR DEBUG ONLY:

Returns the list of "leave" instructions whose processing created a specific block. Returns an empty IEnumerable if an original block (not a clone) is sent as argument. In the enumeration, the innermost leave instructions come first.

Returns null if block is not a clone. NOTE: as we don't have source context info for branching instructions, we manipulate the blocks of the leave's instead of the leave instructions themselves.
FOR DEBUG ONLY:

Returns the original block that, possibly through some cloning, produced a specific block. Transitively walks over the copy2orig map until the original block is found. Acts as an identity function for an original block.

Block is unreachable from entry Returns the stack depth at the beginning of block block. Block that we are interested in. Stack depth at the beginning of block. Simplified CFG pretty printer: all the info printers are set to null. CFG pretty-printer. For each block, this method calls pre_printerc(if non-null), prints the normal/excp. successors, the code of the block, the normal/excp. predecessors and finally calls post_printer. A useful use of the pre and post printers is the debugging of a flow-sensitive analysis. Where to print. Return the entry point of this CFG. This is the point where the execution of the underlying method starts. Return the special block for the exit point of this CFG. Return the special block for the normal exit of this CFG. Return the special block for the exceptional exit of this CFG. Return the total number of blocks in this CFG Exception thrown when trying to construct the CFG of a method whose code is unavailable. Exception thrown when trying to construct the CFG of a method whose code contains some unsupported features, e.g. Filter exception handlers. Exception thrown when trying to construct the CFG of a method without code (e.g., abstract methods). When we duplicate a finally body and graft it into the normal path, the jump that goes to this new code must remain a 'leave' rather than a 'branch', because we must retain the semantics that 'leave' clears the evaluation stack before jumping. However, to correctly do recursive copies (i.e. try/finally inside a finally), we need to remember that these leaves are not actual leaves. To mark them, we derive a new class from Branch and use an 'is' test to look for this class. "Marker" interface: only the three artificial blocks introduced by the CFG implement it. You should not try to implement it in any of your classes. Captures how a block continues. Currently only for normal control flow. Special statement to identify the starting point of a method. This is the definition point for all the method parameters. (this way, each variable is defined by one or more statements). Creates the MethodHeader statement for method. List of method parameters, including This if the method is non-static. This is grabbed from the method code. If it's not found there but the method is non-static, we create one to enforce the invariant that each non-static method has a This parameter. Method that this MethodHeader belongs to; useful in case you want to grab more information. Special statement to identify the exception exit point of a method.