Before IL can be executed, it must be converted by an NGWS Just In Time (JIT) compiler to native code, which is CPU-specific code that runs on the same computer architecture that the JIT compiler is running on. Because the runtime supplies one or more JIT compilers for each CPU architecture that the runtime operates on, developers can write a set of IL that can be JIT-compiled and executed on computers with different architectures. (If your managed code calls platform-specific native APIs or a class libary that is platform-specific, your code is limited to running on a specific operating system.)
The idea behind JIT compilation recognizes the fact that some code may never get called during execution; therefore, rather than using time and memory to convert all of the IL in a PE (portable executable) file to native code, it makes sense to convert the IL as it is needed during execution and store the resulting native code so that it is accessible for subsequent calls. The loader creates and attaches a stub to each of the type's methods when the type is loaded; on the initial call to the method, the stub passes control to the JIT compiler, which converts the IL for that method into native code and modifies the stub to direct execution to the location of the native code. Subsequent calls of the JITted method proceed directly to the native code that was previously generated, reducing the time it takes to JIT compile and execute the code.
As part of compiling IL to native code, the code must pass a verification process (unless an administrator has established a security policy that allows the code to bypass verification). Verification examines IL and metadata to see whether the code can be determined to be type-safe, which means that it is known to access only the memory locations it is authorized to access. Type safety is necessary to ensure that objects are safely isolated from each other and therefore safe from inadvertent or malicious corruption. It also provides assurance that security restrictions on code can be reliably enforced.
For code that is verifiably type-safe, the runtime can rely on the following statements being true:
During the verification process, IL code and metadata are examined in an attempt to confirm that the code can access memory locations and call methods only through properly defined types. For example, code cannot allow an object's fields to be accessed in a manner that allows memory locations to be overrun. Additionally, verification inspects code to see whether the IL itself has been correctly generated, since incorrect IL could lead to a violation of the type safety rules. The verification process passes a well-defined set of type-safe code, and it passes only code that is type-safe. However, some type-safe code might not pass verification due to limitations of the verification process, and some languages by design do not produce verifiably type-safe code.