1Scope

Partition I_alink=Partition_I of the Common Language Infrastructure
(CLI) describes the overall architecture of the CLI, and provides the normative
description of the Common Type System (CTS), the Virtual Execution System
(VES), and the Common Language Specification (CLS). It also provides a
non-normative description of the metadata and a comprehensive set of
abbreviations, acronyms (Partition I_alink=Partition_I) and definitions, included by reference
(Partition I_alink=Partition_I) from all other Partitions.

Partition II (this specification) provides the normative
description of the metadata: its physical layout (as a file format), its
logical contents (as a set of tables and their relationships), and its
semantics (as seen from a hypothetical assembler, ilasm).

2Overview

This document focuses on the structure and semantics of metadata.
The semantics of metadata, which dictate much of the operation of the VES, are
described using the syntax of ilasm, an assembler language for CIL. The ilasm
syntax itself is considered a normative part of this ECMA standard. This
constitutes Chapters 5_5_General_Syntax through 20_20_Custom_Attributes. A complete syntax for ilasm is
included in Partition V_alink=Partition_V.
The structure (both logical and physical) is covered in Chapters 21_21_Metedata_Logical_Format_Tables through 24_24_File_Format_Extensions_to_PE.

Rationale: An assembly
language is really just syntax for specifying the metadata in a file and the
CIL instructions in that file. Specifying ilasm provides a means of
interchanging programs written directly for the CLI without the use of a
higher-level language and also provides a convenient way to express examples.

The semantics of the metadata
also can be described independently of the actual format in which the metadata
is stored. This point is important because the storage format as specified
Chapters 21_21_Metedata_Logical_Format_Tables
through 24_24_File_Format_Extensions_to_PE
is engineered to be efficient for both storage space and access time but this
comes at the cost of the simplicity desirable for describing its semantics.

3Validation and Verification

Validation refers to a set of tests that can be performed
on any file to check that the file format, metadata, and CIL are
self-consistent. These tests are intended to ensure that the file conforms to
the mandatory requirements of this specification. The behavior of conforming
implementations of the CLI when presented with non-conforming files is
unspecified.

Verification refers to a check of both CIL and its related
metadata to ensure that the CIL code sequences do not permit any access to
memory outside the program’s logical address space. In conjunction with the
validation tests, verification ensures that the program cannot access memory or
other resources to which it is not granted access.

Partition III_alink=Partition_III
specifies the rules for both valid and verifiable use of CIL instructions. Partition III_alink=Partition_III
also provides an informative description of rules for validating the internal
consistency of metadata (the rules follow, albeit indirectly, from the
specification in this Partition) as well as containing a normative description
of the verification algorithm. A mathematical proof of soundness of the
underlying type system is possible, and provides the basis for the verification
requirements. Aside from these rules this standard does not specify:

·at what time (if ever) such an algorithm should be performed

·what a conforming implementation should do in case of failure of
verification.

The following graph makes this relationship clearer (see next
paragraph for a description):

Figure 1: Relationship between valid and verifiable CIL

In the above figure, the outer circle contains all code permitted
by the ilasm syntax. The next circle represents all code that is valid CIL. The
dotted inner circle represents all type safe code. Finally, the black
innermost circle contains all code that is verifiable. (The difference between
typesafe code and verifiable code is one of provability: code which
passes the VES verification algorithm is, by-definition, verifiable; but
that simple algorithm rejects certain code, even though a deeper analysis would
reveal it as genuinely typesafe). Note that even if a program follows the
syntax described in Partition V_alink=Partition_V, the code may still not be valid,
because valid code shall adhere to restrictions presented in this document and
in Partition III_alink=Partition_III.

Verification is a very stringent test. There are many programs
that will pass validation but will fail verification. The VES cannot guarantee
that these programs do not access memory or resources to which they are not
granted access. Nonetheless, they may have been correctly constructed so that
they do not access these resources. It is thus a matter of trust, rather than
mathematical proof, whether it is safe to run these programs. A conforming implementation
of the CLI may allow unverifiable code (valid code that does not pass
verification) to be executed, although this may be subject to administrative
trust controls that are not part of this standard. A conforming implementation
of the CLI shall allow the execution of verifiable code, although this may be
subject to additional implementation-specified trust controls.

Before diving into the details, it is useful to see an
introductory sample program to get a feeling for the ilasm assembly language.
The next section shows the famous Hello World program, this time in the ilasm
assembly language.

4.1Hello World Example

This section gives a simple example to illustrate the general
feel of ilasm. Below is code that prints the well known “Hello world!”
salutation. The salutation is written by calling WriteLine, a static method found in the
class System.Console
that is part of the assembly mscorlib (see PartitionIV_alink=Partition_IV).

Example
(informative):

.assembly extern mscorlib {}

.assembly hello {}

.method static public void main()cil
managed

{.entrypoint

.maxstack 1

ldstr
"Hello world!"

call void [mscorlib]System.Console::WriteLine(class
System.String)

ret

}

The .assembly extern declaration
references an external assembly, mscorlib, which defines System.Console.
The .assembly declaration in the second line
declares the name of the assembly for this program. (Assemblies are the
deployment unit for executable content for the CLI.) The .method
declaration defines the global method main. The body of the method is
enclosed in braces. The first line in the body indicates that this method is
the entry point for the assembly (.entrypoint), and
the second line in the body specifies that it requires at most one stack slot (.maxstack).

The method contains only three instructions. The ldstr instruction pushes the string constant "Hello world!"
onto the stack and the call instruction invokes System.Console::WriteLine,
passing the string as its only argument (note that string literals in CIL are
instances of the standard class System.String). As shown, call
instructions shall include the full signature of the called method. Finally,
the last instruction returns (ret) from main.

4.2Examples

This document contains integrated examples for most features of
the CLI metadata. Many sections conclude with an example showing a typical use
of the feature. All these examples are written using the ilasm assembly
language. In addition, Partition V_alink=Partition_V contains a longer example of a program
written in the ilasm assembly language. All examples are, of course,
informative only.

End informative text

5General Syntax

This section describes aspects of the ilasm syntax that are
common to many parts of the grammar. The term “ASCII” refers to the American
Standard Code for Information Interchange, a standard seven-bit code that was
proposed by ANSI in 1963, and finalized in 1968. The ASCII repertoire of
Unicode is the set of 128 Unicode characters from U+0000 to U+007F.

5.1General Syntax Notation

This document uses a modified form of the BNF syntax notation. The following is a brief summary of this notation.

Bold items are terminals. Items placed in angle brackets (e.g. <int64>) are names of syntax classes
and shall be replaced by actual instances of the class. Items placed in square
brackets (e.g. [<float>]) are optional, and any item followed by * can
appear zero or more times. The character “|” means that the items on either
side of it are acceptable. The options are sorted in alphabetical order (to be
more specific: in ASCII order, ignoring “<” for syntax classes, and
case-insensitive). If a rule starts with an optional term, the optional term is
not considered for sorting purposes.

ilasm is a case-sensitive language. All terminals shall be used
with the same case as specified in this reference.

The basic syntax classes used in the grammar are used to describe
syntactic constraints on the input intended to convey logical restrictions on
the information encoded in the metadata.

The syntactic constraints described in this clause are
informative only. The semantic constraints (e.g. “shall be represented in 32
bits”) are normative.

<int32> is either a decimal number or “0x”
followed by a hexadecimal number, and shall be represented in 32 bits.

<int64> is either a decimal number or “0x”
followed by a hexadecimal number, and shall be represented in 64 bits.

<hexbyte> is a 2-digit hexadecimal number
that fits into one byte.

<realnumber> is any syntactic representation for a floating
point number that is distinct from that for all other terminal nodes. In this
document, a period (.) is used to separate the integer and fractional parts,
and “e” or “E” separates the mantissa from the exponent. Either (but not both)
may be omitted.

Note:
A complete assembler may also provide syntax for infinities and NaNs.

<QSTRING> is a string surrounded by double
quote (″) marks. Within the quoted string the character “\” can be used
as an escape character, with “\t” for a tab character, “\n” for a new line
character, or followed by three octal digits in order to insert an arbitrary
byte into the string. The “+” operator can be used to concatenate string
literals. This way, a long string can be broken across multiple lines by using
“+” and a new string on each line. An alternative is using “\” as the last
character in a line, in which case the line break is not entered into the
generated string. Any white characters (space, line feed, carriage return, and
tab) between the “\” and the first character on the next line are ignored. See
also examples below.

Note: A
complete assembler will need to deal with the full set of issues required to
support Unicode encodings, see Partition I_alink=Partition_I
(especially CLS Rule 4).

<SQSTRING> is similar to <QSTRING> with the difference
that it is surround by single quote (′) marks instead of double quote
marks.

<ID> is a contiguous string of
characters which starts with either an alphabetic character or one of “_”, “$”,
“@” or “?” and is followed by any number of alphanumeric characters or any of
“_”, “$”, “@”, or “?”. An <ID>
is used in only two ways:

·As a label of a CIL instruction

·As an <id>
which can either be an <ID>
or an <SQSTRING>, so that
special characters can be included.

Example
(informative):

The following
examples shows breaking of strings:

ldstr "Hello
" + "World " +

"from
CIL!"

and

ldstr "Hello
World\

\040from
CIL!"

become both "Hello World from CIL!".

5.3Identifiers

Identifiers are used to name entities.
Simple identifiers are just equivalent to an <ID>. However, the ilasm
syntax allows the use of any identifier that can be formed using the Unicode
character set (see Partition I_alink=Partition_I). To achieve this an identifier is
placed within single quotation marks. This is summarized in the following
grammar.

<id> ::=

<ID>

| <SQSTRING>

Keywords may only be used as identifiers if they appear in single
quotes (see Partition V_alink=Partition_V for a list of all keywords).

Several <id>’s may be combined to form a larger <id>.
The <id>’s are separated by a dot (.). An <id> formed in this way
is called a <dottedname>.

<dottedname> ::= <id> [. <id>]*

Rationale: <dottedname>
is provided for convenience, since “.” can be included in an <id> using
the <SQSTRING> syntax. <dottedname> is used in the grammar where
“.” is considered a common character (e.g. fully qualified type names)

Implementation Specific
(Microsoft)

Names
that end with $PST
followed by a hexadecimal number have a special meaning. The assembler will
automatically truncate the part starting with the $PST. This is in support of
compiler-controlled accessibility, see Partition I_alink=Partition_V. Also, the first release of the CLI
limits the length of identifiers;­­ see Chapter 21_21_Metedata_Logical_Format_Tables for details.

Examples
(informative):

The following shows
some simple identifiers:

A

Test

$Test

@Foo?

?_X_

The following shows
identifiers in single quotes:

′Weird
Identifier′

′Odd\102Char′

′Embedded\nReturn′

The following shows
dotted names:

System.Console

A.B.C

′My
Project′.′My Component′.′My Name′

5.4Labels and Lists of Labels

Labels are provided as a programming convenience; they represent
a number that is encoded in the metadata. The value represented by a label is
typically an offset in bytes from the beginning of the current method, although
the precise encoding differs depending on where in the logical metadata
structure or CIL stream the label occurs. For details of how labels are encoded
in the metadata, see Chapters 21_21_Metedata_Logical_Format_Tables
through 24_24_File_Format_Extensions_to_PE; for their
encoding in CIL instructions see Partition III_alink=Partition_III.

A simple label is a special name that represents an address.
Syntactically, a label is equivalent to an <id>. Thus, labels may be also
single quoted and may contain Unicode characters.

A list of labels is comma separated, and can be any combination
of these simple labels.

<labeloroffset> ::= <id>

<labels> ::= <labeloroffset> [, <labeloroffset>]*

Rationale:In a real
assembler the syntax for <labeloroffset> might allow the direct
specification of a number rather than requiring symbolic labels.

Implementation Specific (Microsoft)

The following
syntax is also supported, for round-tripping purposes:

<labeloroffset> ::= <int32> | <label>

ilasm distinguishes between two kinds of labels: code
labels and data labels. Code labels are followed by a colon (“:”) and represent
the address of an instruction to be executed. Code labels appear before an
instruction and they represent the address of the instruction that immediately
follows the label. A particular code label name may not be declared more than
once in a method.

In contrast to code labels, data labels specify the location of a
piece of data and do not include the colon character. The data label may not be
used as a code label, and a code label may not be used as a data label. A
particular code label name may not be declared more than once in a module.

<codeLabel> ::= <id> :

<dataLabel> ::= <id>

Example
(informative):

The following
defines a code label, ldstr_label, that
represents the address of the ldstr
instruction:

ldstr_label: ldstr "A
label"

5.5Lists of Hex Bytes

A list of bytes consists simply of one or more hex bytes. Hex
bytes are pairs of characters 0 – 9, a – f, and A – F.

<bytes> ::= <hexbyte> [<hexbyte>*]

5.6Floating point numbers

There are two different ways to specify a floating-point number:

1.Use the dot (“.”) for the decimal point and “e” or “E” in front of the
exponent. Both the decimal point and the exponent are optional.

2.Indicate that the floating-point value is derived from an integer using
the keyword float32 or float64 and indicating the integer in parentheses.

<float64> ::=

float32( <int32> )

| float64( <int64> )

| <realnumber>

Example
(informative):

5.5

1.1e10

float64(128) //
note: this converts the integer 128 to its fp value

5.7Source Line Information

The metadata does not encode information about the lexical scope
of variables or the mapping from source line numbers to CIL instructions.
Nonetheless, it is useful to specify an assembler syntax for providing this
information for use in creating alternate encodings of the information.

Implementation Specific (Microsoft)

Source line
information is stored in the PDB (Portable Debug) file associated with each
module.

.line takes a line number, and
optional column number (preceded by a colon) and single quoted string that
specifies the name of the file the line number is referring to

<externSourceDecl> ::= .line
<int32> [ : <int32> ] [<SQSTRING>]

Implementation Specific (Microsoft)

For compatibility
reasons, ilasm allows the following:

<externSourceDecl> ::= … | #line <int32> <QSTRING>

Notice that this
requires the file name and that it shall be double quoted, not single quoted as
with .line

Some grammar elements require that a file name be supplied. A file name is like any other name where “.” is considered a normal constituent
character. The specific syntax for file names follows the specifications of the
underlying operating system.

Attributes of types and their members attach descriptive
information to their definition. The most common attributes are predefined and
have a specific encoding in the metadata associated with them (see Chapter 22_22_Metadata_Logical_Format:_Other_Structures). In
addition, the metadata provides a way of attaching user-defined attributes to
metadata, using several different encodings.

From a syntactic point of view, there are several ways for
specifying attributes in ilasm:

·Using special syntax built into ilasm. For example the keyword
private in a <classAttr>
specifies that the visibility attribute on a type should be set to allow
access only within the defining assembly.

·Using a general-purpose syntax in ilasm. The non-terminal <customDecl> describes
this grammar (see Chapter 20_20_Custom_Attributes). For some attributes, called pseudo-custom
attributes, this grammar actually results in setting special encodings
within the metadata (see clause 20.2.1_20.2.1_Pseudo_Custom_Attributes).

·Some attributes are required to be set based on the settings of
other attributes or information within the metadata and are not visible from
the syntax of ilasm at all. These attributes, called hidden attributes

·Security attributes are treated specially. There is special
syntax in ilasm that allows the XML representing security attributes to be
described directly (see Chapter 19_19_Declarative_Security). While all other attributes
defined either in the standard library or by user-provided extension are
encoded in the metadata using one common mechanism described in Section 21.10_21.9_CustomAttribute_:_0x0C, security attributes
(distinguished by the fact that they inherit, directly or indirectly from System.Security.Permissions.SecurityAttribute,
see Partition IV_alink=Partition_IV) shall be encoded as described in Section 21.11_21.10_DeclSecurity_:_0x0E.

5.10ilasm Source Files

An input to ilasm is a sequence of declarations, defined as
follows:

<ILFile> ::=

Reference

<decl>*

5.10_5.10_ilasm_source_files

The complete grammar for a top level declaration is shown below. The following sections will concentrate on the various parts of this
grammar.

The grammar for
declarations also includes the following. These are described in a separate
product specification.

Implementation
Specific (Microsoft)

<decl> ::=

Reference

.filealignment<int32>

| .imagebase <int64>

| .language<languageDecl>

| .namespace<id>

| …

6Assemblies, Manifests and
Modules

Assemblies and modules are grouping constructs, each playing a
different role in the CLI.

An assemblyis a set of one or more files deployed
as a unit. An assembly always contains a manifest that specifies (see Section 6.1):

·Version, name, culture, and security requirements for the
assembly.

·Which other files, if any, belong to the assembly along with a
cryptographic hash of each file. The manifest itself resides in the metadata
part of a file and that file is always part of the assembly.

·Which of the types defined in other files of the assembly are to
be exported from the assembly. Types defined in the same file as the manifest
are exported based on attributes of the type itself.

·Optionally, a digital signature for the manifest itself and the
public key used to compute it.

A module is a single file containing executable content in
the format specified here. If the module contains a manifest then it also specifies
the modules (including itself) that constitute the assembly. An assembly shall
contain only one manifest amongst all its constituent files. For an assembly to
be executed (rather than dynamically loaded) the manifest shall reside in the
module that contains the entry point.

While some programming languages introduce the concept of a namespace,
there is no support in the CLI for this concept. Type names are always
specified by their full name relative to the assembly in which they are
defined.

6.1Overview of Modules, Assemblies, and Files

This section contains informative text only.

The following picture should clarify the various forms of
references:

Figure 2: References

Eight files are shown in the picture. The name of each file is
shown below the file. Files that declare a module have an additional border
around them and have names beginning with M. The other two files have a name
beginning with F. These files may be resource files, like bitmaps, or other
files that do not contain CIL code.

Files M1 and M4 declare an assembly in addition to the module
declaration, namely assemblies A and B, respectively. The assembly declaration
in M1 and M4 references other modules, shown with straight lines. Assembly A
references M2 and M3. Assembly B references M3 and M5. Thus, both assemblies
reference M3.

Usually, a module belongs only to one assembly, but it is
possible to share it across assemblies. When Assembly A is loaded at runtime,
an instance of M3 will be loaded for it. When Assembly B is loaded into the
same application domain, possibly simultaneously with Assembly A, M3 will be
shared for both assemblies. Both assemblies also reference F2, for which
similar rules apply.

The module M2 references F1, shown by dotted lines. As a consequence
F1 will be loaded as part of Assembly A, when A is executed. Thus, the file
reference shall also appear with the assembly declaration. Similarly, M5
references another module, M6, which becomes part of B when B is executed. It
follows, that assembly B shall also have a module reference to M6.

End informative text

6.2Defining an Assembly

An assembly is specified as a module that contains a manifest in
the metadata; see Section 21.2.
The information for the manifest is created from the following portions of the
grammar:

<decl> ::=

Section

.assembly
<dottedname> { <asmDecl>* }

6.2

| .assembly extern
<dottedname> { <asmRefDecl>* }

6.3

| .corflags<int32>

6.2

| .file [nometadata]
<filename> .hash= ( <bytes> )
[.entrypoint]

6.2.3

| .moduleextern
<filename>

6.5

| .mresource [public |
private] <dottedname>

[( <QSTRING> )] {
<manResDecl>* }

6.2.2

| .subsystem <int32>

6.2

| …

The .assembly directive declares the
manifest and specifies to which assembly the current module belongs. A module
shall contain at most one .assembly directive. The <dottedname> specifies the name
of the assembly.

Note:
Since some platforms treat names in a case insensitive manner, two assemblies
that have names that differ only in case should not be declared.

The .corflags directive sets a field
in the CLI header of the output PE file (see clause 24.3.3.1). A conforming implementation of the CLI shall expect it to be 1. For backwards compatibility, the three least significant bits are reserved. Future versions of this standard may provide definitions for values
between 8 and 65,535. Experimental and non-standard uses should thus use values
greater than 65,535.

The .subsystem directive is used only
when the assembly is directly executed (as opposed to used as a library for
another program). It specifies the kind of application environment required
for the program, by storing the specified value in the PE file header (see clause 24.2.2).
While a full 32 bit integer may be supplied, a conforming implementation of the
CLI need only respect two possible values:

If the value is 2, the program should be run using whatever
conventions are appropriate for an application that has a graphical user
interface.

If the value is 3, the program should be run using whatever
conventions are appropriate for an application that has a direct console
attached.

Implementation Specific (Microsoft)

<decl>
::= … | .filealignment
<int32> | .imagebase <int64>

The .filealignment
directive sets the file alignment field in the PE
header of the output file. Legal values are multiples of 512. (Different
sections of the PE file are aligned, on disk, at the specified value (in
bytes))

The .imagebase
directive sets the imagebase field in the PE header of the output file. This
value specifies the virtual address at which this PE file will be loaded into
the process.

6.2.1Information about the Assembly (<asmDecl>)

The following grammar shows the information that can be specified
about an assembly.

<asmDecl> ::=

Description

Section

.custom <customDecl>

Custom attributes

20

.hashalgorithm <int32>

Hash algorithm used in the .file
directive

6.2.1.1

| .culture<QSTRING>

Culture for which this assembly is built

6.2.1.2

| .publickey = (
<bytes> )

The originator's public key.

6.2.1.3

| .ver <int32> : <int32>
: <int32> : <int32>

Major version, minor version, revision, and build

6.2.1.4

| <securityDecl>

Permissions needed, desired, or prohibited

19

6.2.1.1Hash Algorithm

<asmDecl> ::= .hash
algorithm <int32> | …

When an assembly consists of more than one file (see clause 6.2.3), the manifest for the assembly specifies both the name
of the file and the cryptographic hash of the contents of the file. The
algorithm used to compute the hash can be specified, and shall be the same for
all files included in the assembly. All values are reserved for future use,
and conforming implementations of the CLI shall use the SHA1(see Partition I_alink=Partition_I) hash function and shall specify this
algorithm by using a value of 32772 (0x8004).

Rationale: SHA1 was chosen
as the best widely available technology at the time of standardization (see Partition I_alink=Partition_I). A single algorithm is
chosen since all conforming implementations of the CLI would be required to
implement all algorithms to ensure portability of executable images.

When present, this indicates that the assembly has been
customized for a specific culture. The strings that shall be used here are
those specified in Partition IV_alink=Partition_IV
as acceptable with the class System.Globalization.CultureInfo. When used for comparison
between an assembly reference and an assembly definition these strings shall be
compared in a case insensitive manner.

Implementation Specific (Microsoft)

The
product version of ilasm and ildasm use .locale rather than .culture.

Note: The
culture names follow the IETF RFC1766 names. The format is
“<language>-<country/region>”, where <language> is a
lowercase two-letter code in ISO 639-1. <country/region> is an uppercase
two-letter code in ISO 3166

6.2.1.3Originator’s Public Key

<asmDecl> ::= .publickey
= ( <bytes> ) | …

The CLI metadata allows the producer of an assembly to compute a
cryptographic hash of the assembly (using the SHA1 hash function) and then
encrypt it using the RSA algorithm (see Partition I_alink=Partition_I) and a public/private key pair of the
producer’s choosing. The results of this (an “SHA1/RSA digital signature”) can
then be stored in the metadata along with the public part of the key pair
required by the RSA algorithm. The .publickey
directive is used to specify the public key that was used to compute the
signature. To calculate the hash, the signature is zeroed, the hash
calculated, then the result stored into the signature.

A reference to an assembly (see Section 6.3) captures some of this information
at compile time. At runtime, the information contained in the assembly
reference can be combined with the information from the manifest of the
assembly located at runtime to ensure that the same private key was used to
create both the assembly seen when the reference was created (compile time) and
when it is resolved (runtime).

The version number of the assembly, specified as four 32-bit
integers. This version number shall be captured at compile time and used as
part of all references to the assembly within the compiled module. This
standard places no other requirement on the use of the version numbers.

Note: A
conforming implementation may ignore version numbers entirely, or it may
require that they match precisely when binding a reference, or any other
behavior deemed appropriate. By convention:

the first
of these is considered the major version number and assemblies with the same
name but different major versions are not interchangeable. This would be
appropriate, for example, for a major rewrite of a product where backwards
compatibility cannot be assumed.

the second
of these is considered the minor version number and assemblies with the same
name and major version but different minor versions indicate significant
enhancements but with intention to be backward compatible. This would be
appropriate, for example, on a “point release” of a product or a fully backward
compatible new version of a product.

the third of
these is considered the revision number and assemblies with the same name,
major and minor version number but different revisions are intended to be fully
interchangeable. This would be appropriate, for example, to fix a security hole
in a previously released assembly.

the fourth
of these is considered the build number and assemblies that differ only by
build number are intended to represent a recompilation from the same source.
This would be appropriate, for example,because of processor, platform, or compiler
changes.

6.2.2Manifest Resources

A manifest resource is simply a named item of data
associated with an assembly. A manifest resource is introduced using the .mresourcedirective, which adds the manifest
resource to the assembly manifest begun by a preceding .assembly declaration.

<decl> ::=

Section

.mresource [public |
private] <dottedname>

{ <manResDecl>* }

| …

5.10

If the manifest resource is declared public it is exported from
the assembly. If it is declared private it is not exported and hence only
available from within the assembly. The <dottedname> is the name of the
resource, and the optional quoted string is a description of the resource.

<manResDecl> ::=

Description

Section

.assemblyextern <dottedname>

Manifest resource is in external assembly with name
<dottedname>.

6.3

| .custom
<customDecl>

Custom attribute.

20

| .file <dottedname> at
<int32>

Manifest resource is in file <dottedname> at byte
offset <int32>.

For a resource stored in a file that is not a module (for
example, an attached text file), the file shall be declared in the manifest
using a separate (top-level) .filedeclaration
(see clause 6.2.3)
and the byte offset shall be zero Similarly, a resource that is defined in
another assembly is referenced using .assembly extern which
requires that the assembly has been defined in a separate (top-level) .assembly extern directive (see Section 6.3).

6.2.3Files in the Assembly

Assemblies may be associated with other files, e.g. documentation
and other files that are used during execution. The declaration .file is used to add a reference to such a file to the
manifest of the assembly: (See Section 21.19)

<decl> ::=

Section

.file [nometadata]
<filename> .hash= ( <bytes> )
[.entrypoint]

| …

5.10

The attribute nometadata is specified if the file is not a
module according to this specification. Files that are marked as nometadata may have any format; they are considered pure data files.

The <bytes> after the .hash specify a hash value computed for the file. The VES shall recompute this hash
value prior to accessing this file and shall generate an exception if it does
not match. The algorithm used to calculate this hash value is specified with .hashalgorithm (see clause 6.2.1.1).

If specified, the .entrypoint
directive indicates that the entrypoint of a multi-module assembly is contained
in this file.

Implementation Specific (Microsoft)

If the hash value
is not specified, it will be automatically computed by the assembly linker al
when an assembly file is created using al. Even though the hash value is
optional in the grammar for ilasm, it is required at runtime.

6.3Referencing Assemblies

An assembly mediates all accesses from the files that it contains
to other assemblies. This is done through the metadata by requiring that the
manifest for the executing assembly contain a declaration for any assembly
referenced by the executing code. The syntax .assembly
extern as a top-level declaration is used for this purpose. The
optional as clause provides an alias which allows ilasm to
address external assemblies that have the same name, but differing in version,
culture, etc.

The dotted name used in .assembly extern shall
exactly match the name of the assembly as declared with .assembly
directive in a case sensitive manner. (So, even though an assembly might be
stored within a file, within a filesystem that is case-blind, the names stored
internally within metadata are case-sensitive, and shall match exactly.)

Implementation Specific (Microsoft)

The assembly
mscorlib contains many of the types and methods in the Base Class Library. For
convenience, ilasm automatically inserts a .assembly externmscorlibdeclaration if required

<asmRefDecl> ::=

Description

Section

.hash = (
<bytes> )

Hash of referenced assembly

6.2.3

|.custom <customDecl>

Custom attributes

20

| .culture<QSTRING>

Culture of the referenced assembly

6.2.1.2

| .publickeytoken = ( <bytes>
)

The low 8 bytes of the SHA1 hash of the originator's
public key.

6.3

| .publickey = ( <bytes>
)

The originator’s full public key

6.2.1.3

| .ver <int32> : <int32>
: <int32> : <int32>

Major version, minor version, revision, and build

6.2.1.4

These declarations are the same as those for.assembly declarations (clause 6.2.1),
except for the addition of .publickeytoken. This
declaration is used to store the low 8 bytes of the SHA1 hash of the
originator’s public key in the assembly reference, rather than the full public
key.

An assembly reference can store either a full public key or an 8
byte “publickeytoken.” Either can be used to validate that the same private key
used to sign the assembly at compile time signed the assembly used at runtime.
Neither is required to be present, and while both can be stored this is not
useful.

A conforming implementation of the CLI need not perform this
validation, but it is permitted to do so, and it may refuse to load an assembly
for which the validation fails. A conforming implementation of the CLI may
also refuse to permit access to an assembly unless the assembly reference
contains either the public key or the public key token. A conforming
implementation of the CLI shall make the same access decision independent of
whether a public key or a token is used.

Rationale:The full public
key is cryptographically safer, but requires more storage space in the assembly
reference.

Example
(informative):

.assemblyextern MyComponents

{.publickey = (BB AA BB EE 11 22 33 00)

.hash = (2A 71 E9 47 F5 15 E6 07 35 E4 CB E3 B4 A1
D3 7F 7F A0 9C 24)

.ver 2:10:2002:0

}

6.4Declaring Modules

All CIL files are modules and are referenced by a logical name
carried in the metadata rather than their file name. See Section 21.16.

<decl> ::=

Section

| .module <filename>

| …

5.10

Example
(informative):

.module CountDown.exe

Implementation Specific (Microsoft)

If the .module directive is missing, ilasm will automatically add
a .moduledirective and set the module name
to be the file name, including its extension in capital letters. e.g., if the
file is called foo and compiled into an exe, the module name will become
“Foo.EXE”.

Note that ilasm
also generates a required GUID to uniquely identify this instance of the module
and emits that into the Mvid metadata field: see clause 21.27.

6.5Referencing Modules

When an item is in the current assembly but part of a different
module than the one containing the manifest, the defining module shall be
declared in the manifest of the assembly using the .module
extern directive. The name used in the .module
extern directive of the referencing assembly shall exactly match the
name used in the .module directive (see Section 6.4) of
the defining module. See Section 21.28.

<decl> ::=

Section

| .moduleextern
<filename>

| …

5.10

Example
(informative):

.module extern Counter.dll

6.6Declarations inside a Module or Assembly

Declarations inside a module or assembly are specified by the following grammar. More information on each option can be found in the
corresponding section.

<decl> ::=

Section

| .class <classHead> {
<classMember>* }

9

| .custom <customDecl>

20

| .data<datadecl>

15.3.1

| .field<fieldDecl>

15

| .method <methodHead>
{ <methodBodyItem>* }

14

| <externSourceDecl>

5.7

| <securityDecl>

18

| …

6.7Exported Type Definitions

The manifest module, of which there can only be one per assembly,
includes the .assembly statement. To export a type
defined in any other module of an assembly requires an entry in the assembly’s
manifest. The following grammar is used to construct such an entry in the
manifest:

<decl> ::=

Section

.class extern <exportAttr>
<dottedname> { <externClassDecl>* }

<externClassDecl> ::=

Section

.file<dottedname>

| .classextern
<dottedname>

| .custom <customDecl>

20

The <exportAttr> value shall be either public or nested
public and shall match the visibility of the type.

For example, suppose an assembly consists of two modules A.EXE
and B.DLL. A.EXE contains the manifest. A public class “Foo” is defined in
B.DLL. In order to export it – that is, to make it visible by, and usable
from, other assemblies –a .class extern statement
shall be included in A.EXE.

Conversely, a public class “Bar” defined in A.EXE does not need
any .class extern statement.

Rationale: Tools should be
able to retrieve a single module, the manifest module, to determine the
complete set types defined by the assembly. Therefore, information from other
modules within the assembly is replicated in the manifest module. By
convention, the manifest module is also known as the assembly.

The metadata provides mechanisms to both define types and reference
types. Chapter 9
describes the metadata associated with a type definition, regardless of whether
the type is an interface, class or a value type.

The mechanism used to reference types is divided into two parts.
The first is the creation of a logical description of user-defined types that
are referenced but (typically) not defined in the current module. These are
stored in a logical table in the metadata (see Section 21.35).

The second is a signature that encodes one or more type
references, along with a variety of modifiers. The grammar non-terminal <type> describes an individual
entry in a signature. The encoding of a signature is specified in Section 22.1.15.n cn

7.1Types

The following grammar completely specifies all built-in types including pointer types of the CLI system. It also shows the syntax for user
defined types that can be defined in the CLI system:

Custom modifiers, defined using modreq (“required modifier”) and
modopt (“optional modifier”), are similar to custom attributes (see Chapter 20) except that modifiers are part of a signature rather than attached to a declaration. Each modifer associates a type reference with an item in the signature.

The CLI itself shall treat required and optional modifiers in the
same manner. Two signatures that differ only by the addition of a custom
modifier (required or optional) shall not be considered to match. Custom
modifiers have no other effect on the operation of the VES.

Rationale: The distinction
between required and optional modifiers is important to tools other than the
CLI that deal with the metadata, typically compilers and program analysers. A
required modifier indicates that there is a special semantics to the modified
item that should not be ignored, while an optional modifier can simply be
ignored.

For example, the concept of const
in the C programming language can be modelled with an optional modifier since
the caller of a method that has a constant parameter need not treat it in any
special way. On the other hand, a parameter that shall be copy constructed in
C++ shall be marked with a required custom attribute since it is the caller who
makes the copy.

The signature encoding for pinned shall appear only in signatures
that describe local variables (see clause 14.4.1.3).
While a method with a pinned local variable is executing the VES shall not
relocate the object to which the local refers. That is, if the implementation
of the CLI uses a garbage collector that moves objects, the collector shall not
move objects that are referenced by an active pinned local variable.

Rationale: If unmanaged
pointers are used to dereference managed objects, these objects shall be
pinned. This happens, for example, when a managed object is passed to a method
designed to operate with unmanaged data.

The CLI built-in types have corresponding value types defined in
the Base Class Library. They shall be referenced in signatures only using their
special encodings (i.e. not using the general purpose valuetype <typeReference> syntax). Partition I_alink=Partition_I specifies the built-in types.

7.3References to User-defined Types (<typeReference>)

User-defined types are referenced either using their full name
and a resolution scope or (if one is available in the same module) a type
definition (see Chapter 9).

A <typeReference> is
used to capture the full name and resolution scope.

<typeReference> ::=

[<resolutionScope>] <dottedname> [/
<dottedname>]*

<resolutionScope> ::=

[ .module
<filename> ]

| [ <assemblyRefName> ]

<assemblyRefName> ::=

Section

<dottedname>

5.1

The following resolution scopes are specified for un-nested
types:

·Current module (and, hence, assembly). This is the most
common case and is the default if no resolution scope is specified. The type
shall be resolved to a definition only if the definition occurs in the same
module as the reference.

Note: A
type reference that refers to a type in the same module and assembly is better
represented using a type definition. Where this is not possible (for example,
when referencing a nested type that has compilercontrolled
accessibility) or convenient (for example, in some one-pass compilers) a type
reference is equivalent and may be used.

·Different module, current assembly. The resolution scope
shall be a module reference syntactically reprented using the notation [.module <filename>]. The type shall be
resolved to a definition only if the referenced module (see Section 6.4)
and type (see Section 6.7)
have been declared by the current assembly and hence have entries in the
assembly’s manifest. Note that in this case the manifest is not physically
stored with the referencing module.

·Different assembly. The resolution scope shall be an
assembly reference syntactically represented using the notation [<assemblyRefName>]. The referenced assembly
shall be declared in the manifest for the current assembly (see Section 6.3),
the type shall be declared in the referenced assembly’s manifest, and the type
shall be marked as exported from that assembly (see section 6.7 and clause 9.1.1).

·For nested types, the resolution scope is always the enclosing
type. (See Section 9.6).
This is indicated syntactically by using a slash (“/”) to separate the
enclosing type name from the nested type’s name

Example
(informative):

The proper way to
refer to a type defined in the base class library. The name of the type is System.Console and it is found in the assembly named mscorlib.

.assembly extern mscorlib { }

.class
[mscorlib]System.Console

A reference to the
type named C.D in the module named x in
the current assembly.

.module extern x

.class [.module x]C.D

A reference to the
type named C nested inside of the type named Foo.Bar in another assembly, named MyAssembly.

.assembly extern MyAssembly { }

.class
[MyAssembly]Foo.Bar/C

7.4Native Data Types

Some implementations of the CLI will be hosted on top of existing
operating systems or runtime platforms that specify data types required to
perform certain functions. The metadata allows interaction with these native
data types by specifying how the built-in and user-defined types of the CLI
are to be marshalled to and from native data types. This marshalling
information can be specified (using the keyword marshal) for

·the return type of a method, indicating that a native data type
is actually returned and shall be marshalled back into the specified CLI data
type

·a parameter to a method, indicating that the CLI data type
provided by the caller shall be marshalled into the specified native data type
(if the parameter is passed by reference the updated value shall be marshalled
back from the native data type into the CLI data type when the call is
completed)

·a field of a user-defined type, indicating that any attempt to
pass the object in which it occurs to platform methods shall make a copy of the
object, replacing the field by the specified native data type (if the object is
passed by reference then the updated value shall be marshalled back when the
call is completed)

The following table lists all native types supported by the CLI
and provides a description for each of them. A more complete description can
be found in Partition IV_alink=Partition_IV in the definition of the enum System.Runtime.Interopservices.UnmanagedType,
which provides the actual values used to encode the types. All encoding values
from 0 through 63 are reserved for backward compatibility with existing
implementations of the CLI. Values 64 through 127 are reserved for future use
in this and related Standards.

<nativeType> ::=

Description

Name in
class library

[ ]

Native array. Type and size are determined at runtime from
the actual marshaled array.

A pointer to a null terminated array of ANSI characters.
Code page is implementation specific.

LPStr

| lptstr

A pointer to a null terminated array of platform
characters (ANSI or Unicode). Code page and character encoding are
implementation specific.

LPTStr

| lpvoid

An untyped pointer, platform
specifies size.

LPVoid

| lpwstr

A pointer to a null terminated array of Unicode
characters. Character encoding is implementation specific.

LPWStr

| method

A function pointer.

FunctionPtr

| <nativeType> [ ]

Array of <nativeType>. The length
is determined at runtime by the size of the actual marshaled array.

LPArray

| <nativeType> [ <int32> ]

Array of <nativeType> of length <int32>.

LPArray

| <nativeType> [+ <int32> ]

Array of <nativeType> with runtime supplied element
size. The int32 specifies a parameter to the current method (counting from
parameter number 0) that, at runtime, will contain the size of an element of
the array in bytes. Can only be applied to methods, not fields.

LPArray

| <nativeType> [ <int32> + <int32> ]

Array of <nativeType> with runtime supplied element
size. The first int32 specifies the number of elements in the array. The
second int32 specifies which parameter to the current method (counting from
parameter number 1) will specify the additional number of elements in the
array. Can only be applied to methods, not fields

LPArray

Implementation Specific (Microsoft)

The Microsoft
implementation supports a richer set of types to describe marshalling between
Windows native types and COM. These additional options are listed in the
following table:

Implementation
Specific (Microsoft)

<nativeType> ::=

Description

Name in
class library

| as any

Determines the type of an object
at runtime and marshals the Object as that type.

AsAny

| byvalstr

A string in a fixed length
buffer.

VBByRefStr

| custom ( <QSTRING>,
<QSTRING> )

Custom marshaler. The 1st string is the name
of the marshalling class, using the string conventions of Reflection.Emit to
specify the assembly and/or module. The 2nd is an arbitrary
string passed to the marshaller at runtime to identify the form of
marshalling required.

CustomMarshaler

| fixed array [ <int32> ]

A fixed size array of length <int32> bytes

ByValArray

| fixed sysstring
[ <int32> ]

A fixed size system string of length <int32>. This
can only be applied to fields, and a separate attribute specifies the
encoding of the string.

ByValTStr

| lpstruct

A pointer to a C-style
structure. Used to marshal managed formatted types.

LPStruct

| struct

A C-style structure, used to
marshal managed formatted types.

Struct

Example
(informative):

.method int32
M1( int32marshal(int32), bool[]
marshal(bool[5]) )

Method M1 takes two
arguments: an int32, and an array of 5 bools

++++++++++

.method int32
M2( int32marshal(int32), bool[] marshal(bool[+1])
)

Method M2 takes two
arguments: an int32, and an array of bools: the number of elements in that
array is given by the value of the first parameter

++++++++++

.method int32
M3( int32marshal(int32), bool[] marshal(bool[7+1])
)

Method M3 takes two
arguments: an int32, and an array of bools: the number of elements in that
array is given as 7 plus the value of the first parameter

Partition I_alink=Partition_I specifies visibility and accessibility.
In addition to these attributes, the metadata stores information about method
name hiding. Hidingcontrols
which method names inherited from a base type are available for compile-time
name binding.

Visibility is attached only to top-level types, and there are
only two possibilities: visible to types within the same assembly, or visible
to types regardless of assembly. For nested types (i.e. types that are members
of another type) the nested type has an accessibility that further
refines the set of methods that can reference the type. A nested type may have
any of the 7 accessibility modes (see Partition I_alink=Partition_I),
but has no direct visibility attribute of its own, using the visibility of its
enclosing type instead.

Because the visibility of a top-level type controls the
visibility of the names of all of its members, a nested type cannot be more
visible than the type in which it is nested. That is, if the enclosing type is
visible only within an assembly then a nested type with public accessibility is
still only available within the assembly. By contrast, a nested type that has
assembly accessibility is restricted to use within the assembly even if the
enclosing type is visible outside the assembly.

To make the encoding of all types consistent and compact, the
visibility of a top-level type and the accessibility of a nested type are
encoded using the same mechanism in the logical model of clause 22.1.14.

8.2Accessibility

Accessibility is encoded directly in the metadata. See, for
example, clause 21.24.

Hiding is a compile-time concept that applies to individual
methods of a type. The CTS specifies two mechanisms for hiding, specified by a
single bit:

·hide-by-name, meaning that the introduction of a name in a
given type hides all inherited members of the same kind (method or field) with
the same name.

·hide-by-name-and-sig, meaning that the introduction of a
name in a given type hides any inherited member of the same kind but with
precisely the same type (for fields) or signature (for methods, properties, and
events).

There is no runtime support for hiding. A conforming
implementation of the CLI treats all references as though the names were marked
hide-by-name-and-sig. Compilers that desire the effect of
hide-by-name can do so by marking method definitions with the newslot attribute (see clause 14.4.2.3)
and correctly chosing the type used to resolve a method reference (see clause 14.1.3).

9Defining Types

Types (i.e., classes, value types, and interfaces) may be defined at the top-level of a module:

<decl> ::=

Section

.class <classHead> {
<classMember>* }

9

| …

The logical metadata table created by this declaration is
specified in Section 21.34.

Rationale: For historical
reasons, many of the syntactic classes used for defining types incorrectly use
“class” instead of “type” in their name. All classes are types, but “types” is
a broader term encompassing value types, and interfaces.

The extends keyword defines the base type of a
type. A type shall extend from exactly one other type. If no type is specified,
ilasm will add an extend clause to make the type inherit from System.Object.

The implements keyword defines the interfaces of a type.
By listing an interface here, a type declares that all of its concrete implementations
will support the contract of that interface, including providing
implementations of any virtual methods the interface declares. See also Chapter 10
and Chapter 11.

Example
(informative):

.class private auto autochar CounterTextBox

extends[System.Windows.Forms]System.Windows.Forms.TextBox

implements [.moduleCounter]CountDisplay

{ // body
of the class

}

This code declares
the class CounterTextBox,
which extends the class System.Windows.Forms.TextBox in the assembly System.Windows.Forms
and implements the interface CountDisplay in the module Counter of the current assembly. The attributes private, auto and autochar
are described in the following sections.

A type can have any number of custom
attributes attached. Custom attributes are attached as described in Chapter 20. The other (predefined) attributes of a type may be grouped into attributes that specify visibility, type layout information, type semantics information, inheritance rules, interoperation information, and
information on special handling. The following subsections provide additional
information on each group of predefined attributes.

<classAttr> ::=

Description

Section

abstract

Type is abstract.

9.1.4

| ansi

Marshal strings to platform as ANSI.

9.1.5

| auto

Auto layout of type.

9.1.2

| autochar

Marshal strings to platform based on platform.

9.1.5

| beforefieldinit

Calling static methods does not initialize type.

9.1.6

| explicit

Layout of fields is provided explicitly.

9.1.2

| interface

Interface declaration.

9.1.3

| nested assembly

Assembly accessibility for nested type.

9.1.1

| nested famandassem

Family and Assembly accessibility for nested type.

9.1.1

| nested family

Family accessibility for nested type.

9.1.1

| nested famorassem

Family or Assembly accessibility for nested type.

9.1.1

| nested private

Private accessibility for nested type.

9.1.1

| nested public

Public accessibility for nested type.

9.1.1

| private

Private visibility of top-level type.

9.1.1

| public

Public visibility of top-level type.

9.1.1

| rtspecialname

Special treatment by runtime.

9.1.6

| sealed

The type cannot be subclassed.

9.1.4

| sequential

The type is laid out sequentially.

9.1.2

| serializable

Type may be serialized.

9.1.6

| specialname

Special treatment by tools.

9.1.6

| unicode

Marshal strings to platform as Unicode.

9.1.5

Implementation Specific (Microsoft)

The above grammar
also includes

<classAttr> ::= import

to indicate that
the type is imported from a COM type library

9.1.1Visibility and Accessibility Attributes

<classAttr> ::= …

| nested assembly

| nested famandassem

| nested family

| nested famorassem

| nested private

| nested public

| private

| public

See Partition I_alink=Partition_I. A type that is not nested inside
another shall have exactly one visibility (private or public) and shall not
have an accessiblity. Nested types shall have no visibility, but instead shall
have exactly one of the accessibility attributes (nested assembly, nested
famandassem, nested family, nested famorassem, nested private, or nested
public). The default visibility for top-level types is private. The default
accessibility for nested types is nested private.

9.1.2Type Layout Attributes

<classAttr> ::= …

| auto

| explicit

| sequential

The type layout specifies how the fields of an instance of a type
are arranged. A given type shall have only one layout attribute specified. By
convention, ilasm supplies auto if no layout attribute is specified.

auto: the layout shall be done by the
CLI, with no user-supplied constraints

explicit: the layout of the fields is
explicitly provided (see Section 9.7).

sequential: the CLI shall lay out the
fields in sequential order, based on the order of the fields in the logical
metadata table (see Section 21.15).

Rationale: The default auto layout should provide the best layout for the
platform on which the code is executing. sequential
layout is intended to instruct the CLI to match layout rules commonly followed
by languages like C and C++ on an individual platform, where this is possible
while still guaranteeing verifiable layout. explicit
layout allows the CIL generator to specify the precise layout semantics;
specific rules govern which explicit layouts are verifiable.

The type semantic attributes specify whether an interface, class,
or value type shall be defined. The interface attribute specifies an
interface. If this attribute is not present and the definition extends
(directly or indirectly) System.ValueType
a value type shall be defined (see Chapter 12). Otherwise, a class shall be
defined (see Chapter 10).

Note that the
runtime size of a value type shall not exceed 1 MByte (0x100000 bytes)

Implementation Specific (Microsoft)

The current
implementation allows 0x3F0000 bytes, but may be reduced in future

9.1.4Inheritance Attributes

<classAttr> ::= …

| abstract

| sealed

Attributes that specify special semantics are abstract and sealed. These attributes may be used together.

abstract specifies that this type
shall not be instantiated. If a type contains abstract methods, the type shall be
declared as an abstract
type.

sealed specifies that a type shall not
have subclasses. All value types shall be sealed.

Rationale: Virtual methods
of sealed types are effectively instance methods, since they cannot be
overridden. Framework authors should use sealed classes sparingly since they do
not provide a convenient building block for user extensibility. Sealed classes
may be necessary when the implementation of a set of virtual methods for a
single class (typically inherited from different interfaces) becomes
interdependent or depends critically on implementation details not visible to
potential subclasses.

A type that is both abstract and sealed should have only static
members, and serves as what some languages call a namespace.

These attributes are for interoperation with unmanaged code. They specify the default behavior to be used when calling a
method (static, instance, or virtual) on the class that has an argument or
return type of System.String and
does not itself specify marshalling behavior. Only one value shall be
specified for any type, and the default value isansi.

ansispecifies that marshalling
shall be to and from ANSI
strings

unicode specifies that marshalling shall be to and from
Unicode strings

autochar specifies either ANSIor Unicode behavior,
depending on the platform on which the CLI is running.

9.1.6Special Handling Attributes

<classAttr> ::= …

| beforefieldinit

| serializable

| specialname

| rtspecialname

These attributes may be combined in any way.

beforefieldinit instructs the CLI that it need not
initialize the type before a static method is called. See clause 9.5.3.

Implementation Specific (Microsoft)

serializable
indicates that the fields of the type may be serialized into a data stream by
the CLI serializer. See Partition IV_alink=Partition_IV.

specialname indicates that the name of this item may have
special significance to tools other than the CLI. See, for example, PartitionI_alink=Partition_I .

rtspecialname indicates that the name of this item has
special significance to the CLI. There are no currently defined special type
names; this is for future use. Any item marked rtspecialname shall also
be marked specialname

Rationale:If an item is
treated specially by the CLI, then tools should also be made aware of that.
The converse is not true.

9.2Body of a Type Definition

A type may contain any number of further declarations. The
directives .event, .field,.method, and .property
are used to declare members of a type. The directive .class
inside a type declaration is used to create a nested type, which is discussed
in further detail in Section 9.6.

Specifies that the first method is overridden by the
definition of the second method.

9.3.2

| .pack <int32>

Used for explicit layout of fields.

9.7

| .property <propHead> {
<propMember>* }

Declares a property of the type.

16

| .size <int32>

Used for explicit layout of fields.

9.7

| <externSourceDecl>

.line

5.7

|
<securityDecl>

.permission or .capability

19

9.3Introducing and Overriding Virtual Methods

A virtual method of a base type is overridden by providing a
direct implementation of the method (using a method definition, see Section 14.4)
and not specifying it to be newslot (see clause 14.4.2.3). An existing method body may
also be used to implement a given virtual declaration using the .override directive (see clause 9.3.2).

A virtual method is introduced in the inheritance hierarchy by
defining a virtual method (see Section 14.4). The versioning semantics differ
depending on whether or not the definition is marked as newslot (see clause 14.4.2.3):

If the definition is marked newslot then the definition always
creates a new virtual method, even if a base class provides a matching virtual
method. Any reference to the virtual method created before the new virtual
function was defined will continue to refer to the original definition.

If the definition is not marked newslot then it creates a
new virtual method only if there is no virtual method of the same name and signature
inherited from a base class. If the inheritance hierarchy changes so that the
definition matches an inherited virtual function the definition will be treated
as a new implementation of the inherited function.

9.3.2The .override DirectiveUsusally the VES

The .overridedirective
specifies that a virtual method should be implemented (overridden), in this
type, by a virtual method with a different name but with the same signature.
It can be used to provide an implementation for a virtual method inherited from
a base class or a virtual method specified in an interface implemented by this
type. The .override directive specifies a Method
Implementation (MethodImpl) in the metadata (see clause 14.1.4).

The first <typeSpec>
:: <methodName> pair specifies the virtual method that is
being overridden. It shall reference either an inherited virtual method or a
virtual method on an interface that the current type implements. The remaining
information specifies the virtual method that provides the implementation.

While the syntax specified here and the actual metadata format
(see Section 21.25 )allows any virtual method to be used to provide an implementation, a conforming program shall provide a virtual method actually implemented directly on the type containing the .overridedirective.

Rationale: The metadata is
designed to be more expressive than can be expected of all implementations of
the VES.

Example (informative):

The following
example shows a typical use of the .override
directive. A method implementation is provided for a method declared in an
interface (see Chapter 11).

.class interface I

{ .method public virtual abstract void m() cil
managed {}

}

.classC implements I

{ .method virtual public void m2()

{
// body of m2

}

.override I::m with instance void C::m2()

}

The .override directive specifies that the C::m2 body shall
provide the implementation of be used to implement I::m on objects of class C.

If a type overrides an inherited method, it may widen, but
it shall not narrow, the accessibility of that method. As a principle,
if a client of a type is allowed to access a method of that type, then it
should also be able to access that method (identified by name and signature) in
any derived type. Table 7.1 specifies narrow and widen in this
context – a “Yes” denotes that the subclass can apply that
accessibility, a “No” denotes it is illegal.

Note:
A method may be overridden even if it may not be accessed by the subclass.

If a method
has assembly accessibility, then it shall have public accessibility if it is
being overridden by a method in a different assembly. A similar rule applies to
famandassem, where also famorassem is allowed outside the assembly. In both
cases assembly or famandassem, respectively, may be used inside the same
assembly.

A special rule applies to famorassem, as shown in the
table. This is the only case where the accessibility is apparently narrowed by
the subclass. A famorassem method may be overridden with family
accessibility by a type in another assembly.

Rationale: Because there
is no way to specify “family or specific other assembly” it is not possible to
specify that the accessibility should be unchanged. To avoid narrowing access,
it would be necessary to specify an accessibility of public, which would force
widening of access even when it is not desired. As a compromise, the minor
narrowing of “family” alone is permitted.

There are three special members, all methods, that can be defined
as part of a type: instance constructors, instance finalizers, and type
initializers.

9.5.1Instance constructors

Instance constructors initialize an instance of a type. An
instance constructor is called when an instance of a type is created by the
newobj instruction (see Partition III_alink=Partition_III). Instance constructors shall be
instance (not static or virtual) methods, they shall be named .ctor and marked both rtspecialname
and specialname (see clause 14.4.2.6). Instance constructors may take parameters, but shall not return a value. Instance
constructors may be overloaded (i.e. a type may have several instance
constructors). Each instance constructor shall have a unique signature. Unlike
other methods, instance constructors may write into fields of the type that are
marked with the initonly attribute
(see clause 15.1.2).

Example
(informative):

The
following shows the definition of an instance constructor that does not take
any parameters:

The behavior of finalizers is specified in Partition I_alink=Partition_I.
The finalize method for a particular type is specified by overriding the
virtual method Finalize in System.Object.

9.5.3Type Initializer

Types may contain special methods called type initializers to initialize the type itself.

All types (classes, interfaces, and value types) may have a type
initializer. This method shall be static,
take no parameters, return no value, be marked with rtspecialname
and specialname (see clause 14.4.2.6), and be named .cctor.

Like instance initializers, type initializers may write into
static fields of their type that are marked with the initonly attribute (see clause 15.1.2).

Note: Type
initializers are often simple methods that initialize the type’s static fields
from stored constants or via simple computations. There are, however, no
limitations on what code is permitted in a type initializer.

9.5.3.1Type Initialization Guarantees

1.When type initializers are executed is specified in Partition I_alink=Partition_I

2.A type initializer shall run exactly once for any given type, unless
explicitly called by user code

3.No method other than those called directly or indirectly from the type
initializer will be able to access members of a type before its initializer
completes execution.

9.5.3.2Relaxed Guarantees

A type can be marked with the attribute beforefieldinit
(see clause 9.1.6)
to indicate that all the guarantees specified in clause 9.5.3.1 are not required. In particular, the final requirement of guarantee 1 need not be provided: the type initializer need not run before a static method is called or referenced.

Rationale: When code can
be executed in multiple application domains it becomes particularly expensive
to ensure this final guarantee. At the same time, examination of large bodies
of managed code have shown that this final guarantee is rarely required, since
type initializers are almost always simple methods for initializing static
fields. Leaving it up to the CIL generator (and hence, possibly, to the
programmer) to decide whether this guarantee is required therefore provides
efficiency when it is desired at the cost of consistency guarantees.

9.5.3.3Races and Deadlocks

In addition to the type initialization guarantees specified in clause 9.5.3.1 the CLI shall ensure two further guarantees for code that is called from a type initializer:

1.Static variables of a type are in a known state prior to any access
whatsoever.

2.Type initialization alone shall not create a deadlock unless some code
called from a type initializer (directly or indirectly) explicitly invokes
blocking operations.

Rationale:

Consider the following two class
definitions:

.class public A extends [mscorlib]System.Object

{ .field static public class Aa

.field static public class B b

.method public static rtspecialname
specialname void .cctor ()

{ ldnull // b=null

stsfldclass B A::b

ldsfldclass A B::a // a=B.a

stsfldclass A A::a

ret

}

}

.class public B extends[mscorlib]System.Object

{ .field static public class A a

.field static public class B b

.method public static rtspecialname
specialname void .cctor ()

{ ldnull // a=null

stsfldclass A B::a

ldsfldclass B A::b // b=A.b

stfldclass B B::b

ret

}

}

After loading these two classes,
an attempt to reference any of the static fields causes a problem, since the
type initializer for each of A and B requires that the type initializer of the
other be invoked first. Requiring that no access to a type be permitted until
its initializer has completed would create a deadlock situation. Instead,
the CLI provides a weaker guarantee: the initializer will have started to run,
but it need not have completed. But this alone would allow the full
uninitialized state of a type to be visible, which would make it difficult to
guarantee repeatable results.

There are similar, but more
complex, problems when type initialization takes place in a multi-threaded
system. In these cases, for example, two separate threads might start
attempting to access static variables of separate types (A and B) and then each
would have to wait for the other to complete initialization.

A rough outline of the algorithm
is as follows:

1. At class load time (hence
prior to initialization time) store zero or null into all static fields of the
type.

2. If the type is initialized you
are done.

2.1. If the type is not yet
initialized, try to take an initialization lock.

2.2. If successful, record this
thread as responsible for initializing the type and proceed to step 2.3.

2.2.1. If not, see whether this
thread or any thread waiting for this thread to complete already holds the
lock.

2.2.2. If so, return since
blocking would create a deadlock. This thread will now see an incompletely
initialized state for the type, but no deadlock will arise.

2.2.3 If not, block until the
type is initialized then return.

2.3 Initialize the parent type
and then all interfaces implemented by this type.

2.4 Execute the type
initialization code for this type.

2.5 Mark the type as initialized,
release the initialization lock, awaken any threads waiting for this type to be
initialized, and return.

9.6Nested Types

Nested types are specified in Partition I_alink=Partition_I. Interfaces may be nested inside of
classes and value types, but classes and value types shall not be nested inside
of interfaces. For information about the logical tables associated with nested
types, see Section 21.29.

Note: A
nested type is not associated with an instance of its enclosing type. The
nested type has its own base type and may be instantiated independent of the
enclosing type. This means that the instance members of the enclosing type are
not accessible using the this pointer of the nested type.

A nested
type may access any members of its enclosing type, including private members,
as long as the member is static or the nested type has a reference to an
instance of the enclosing type. Thus, by using nested types a type may give
access to its private members to another type.

On the
other side, the enclosing type may not access any private or family members of
the nested type. Only members with assembly, famorassem, or public
accessibility can be accessed by the enclosing type.

Example
(informative):

The following
example shows a class declared inside another class. Both classes declare a
field. The nested class may access both fields, while the enclosing class does
not have access to the field b.

9.7Controlling Instance Layout

The CLI supports both sequential and explicit layout control, see clause 9.1.2.
For explicit layout it is also necessary to specify the precise layout of an
instance, see also Section 21.18
and Section 21.16.

<fieldDecl> ::=

[[ <int32> ]] <fieldAttr>*
<type> <id>

The optional int32 specified in brackets at the beginning of the
declaration specifies the byte offset from the beginning of the instance of the
type. This form of explicit layout control shall not be used with global
fields specified using the at notation (see clause 15.3.2).

Offset values shall be 0 or greater; they cannot be negative. It
is possible to overlap fields in this way, even though it is not recommended.
The field may be accessed using pointer arithmetic and ldind to load the field indirectly or stind to store the field indirectly (see Partition III_alink=Partition_III). See Section 21.18 and Section 21.16 for encoding of this information.
For explicit layout, every field shall be assigned an offset.

The .pack directive specifies that fields should be placed within the runtime object at addresses which
are a multiple of the specified number, or at natural alignment for that field
type, whichever is smaller. e.g., .pack 2
would allow 32-bit-wide fields to be started on even addresses – whereas
without any .pack directive, they would be naturally
aligned – that is to say, placed on addresses that are a multiple of 4. The
integer following .pack shall be one of 0, 1, 2, 4,
8, 16, 32, 64 or 128. (A value of zero indicates that the pack size used
should match the default for the current platform). The .pack
directive shall not be supplied for any type with explicit layout control.

The directive .size specifies that a memory block of the specified amount of bytes shall be allocated for an
instance of the type. e.g., .size 32 would create a
block of 32 bytes for the instance. The value specified shall be greater than
or equal to the calculated size of the class, based upon its field sizes and
any .pack directive. Note that if this directive
applies to a value type, then the size shall be less than 1 MByte.

Note: Metadata
that controls instance layout is not a “hint,” it is an integral part of the
VES that shall be supported by all conforming implementations of the CLI.

In addition to types with static members, many languages have the
notion of data and methods that are not part of a type at all. These are
referred to as global fields and methods.

It is simplest to understand global fields and methods in the CLI
by imagining that they are simply members of an invisible abstract
public class. In fact, the CLI defines such a special class, named ′<Module>′,
that does not have a base type and does not implement any interfaces. The only
noticeable difference is in how definitions of this special class are treated
when multiple modules are combined together, as is done by a class loader.
This process is known as metadata merging.

For an ordinary type, if the metadata merges two definitions of
the same type, it simply discards one definition on the assumption they are
equivalent and that any anomaly will be discovered when the type is used. For
the special class that holds global members, however, members are unioned
across all modules at merge time. If the same name appears to be defined for
cross-module use in multiple modules then there is an error. In detail:

·If no member of the same kind (field or method), name, and
signature exists, then add this member to the output class.

·If there are duplicates and no more than one has an accessibility
other than compilercontrolled, then add them
all in the output class.

·If there are duplicates and two or more have an accessibility
other than compilercontrolled an error has occurred.

10Semantics of Classes

Classes, as specified in PartitionI_alink=Partition_I, define
types in an inheritance hierarchy. A class (except for the built-in class System.Object) shall declare exactly
one parent class. A class shall declare zero or more interfaces that it
implements (see Chapter 11). A concrete class may be instantiated to create an object, but an abstract class (see clause 9.1.4) shall not be instantiated. A class may define fields (static or instance), methods (static, instance, or virtual), events, properties, and nested types (classes, value types, or interfaces).

Instances of a class (objects) are created only by explicitly
using the newobj instruction (see Partition III_alink=Partition_III). When a variable or field that has a
class as its type is created (for example, by calling a method that has a local
variable of a class type) the value shall initially be null, a special value
that is assignment compatible with all class types even though it is not an
instance of any particular class.

11Semantics of Interfaces

Interfaces, as specified in Partition I_alink=Partition_I, define a contract that other types may
implement. Interfaces may have static fields and methods, but they shall not
have instance fields or methods. Interfaces may define virtual methods, but
only if they are abstract (see Partition I_alink=Partition_I and clause 14.4.2.4).

Rationale: Interfaces
cannot define instance fields for the same reason that the CLI does not support
multiple inheritance of base types: in the presence of dynamic loading of data
types there is no known implementation technique that is both efficient when
used and has no cost when not used. By contrast, providing static fields and
methods need not affect the layout of instances and therefore does not raise
these issues.

Interfaces may be nested inside any type (interface, class, or
value type). Classes and value types shall not be nested inside of interfaces.

11.1Implementing Interfaces

Classes and value types shall implement zero or more
interfaces. Implementing an interface implies that all concrete instances of
the class or value type shall provide an implementation for each abstract virtual method declared in the interface. In
order to implement an interface, a class or value type shall either explicitly
declare that it does so (using the implements attribute in its type definition,
see Section 9.1) or shall be derived from a base class that implements the interface.

Note: An
abstract class (since it cannot be instantiated)
need not provide implementations of the virtual methods of interfaces it
implements, but any concrete class derived from it shall provide the
implementation.

Merely
providing implementations for all of the abstract
methods of an interface is not sufficient to have a type implement that
interface. Conceptually, this represents that fact that an interface
represents a contract that may have more requirements than are captured in the
set of abstract methods. From an implementation
point of view, this allows the layout of types to be constrained only by those
interfaces that are explicitly declared.

Interfaces shall declare that they require the implementation of
zero or more other interfaces. If one interface, A, declares that it requires
the implementation of another interface, B, then A implicitly declares that it
requires the implementation of all interfaces required by B. If a class or
value type declares that it implements A, then all concrete instances shall
provide implementations of the virtual methods declared in A and all of the
interfaces A requires.

Example
(informative):

The following class
implements the interface IStartStopEventSource
defined in the module Counter.

Classes that implement an interface (see Section 11.1)
are required to provide implementations for the abstract
virtual methods defined by the interface. There are three mechanisms for
providing this implementation:

·directly specifying an implementation, using the same name and
signature as appears in the interface

The Virtual Execution System shall determine the appropriate implementation
of a virtual method to be used for an interface abstract
method using the following algorithm.

·If the parent class implements the interface, start with the same
virtual methods that it provides, otherwise create an interface that has empty
slots for all virtual functions.

·If this class explicitly specifies that it implements the
interface

oif the class defines any public virtual newslot functions
whose name and signature match a virtual method on the interface, then use
these new virtual methods to implement the corresponding interface method.

·If there are any virtual methods in the interface that still have
empty slots, see if there are any public virtual
methods available on this class (directly or inherited) and use these to
implement the corresponding methods on the interface.

·Apply all MethodImpls
that are specified for this class, thereby placing explicitly specified virtual
methods into the interface in preference to those inherited or chosen by name
matching.

·If the current class is not abstract
and there are any interface methods that still have empty slots, then the
program is not valid.

Rationale:Interfaces can
be thought of as specifying, primarily, a set of virtual methods that shall be
implemented by any class that implements the interface. The class specifies a
mapping from its own virtual methods to those of the interface. Thus it is
virtual methods, not specific implementations of those methods, that are
associated with interfaces. Overriding a virtual method on a class with a
specific implementation will thus affect not only the virtual method named in
the class but also any interface virtual methods to which that same virtual
method has been mapped.

12Semantics
of Value Types

In contrast to classes, value types (see PartitionI_alink=Partition_I) are not
accessed by using a reference but are stored directly in the location of that
type.

Rationale: Value types are
used to describe the type of small data items. They can be compared to struct (as opposed
to pointers to struct) types in C++. Compared to reference types, value types
are accessed faster since there is no additional indirection involved. As
elements of arrays they do not require allocating memory for the pointers as
well as for the data itself. Typical value types are complex numbers,
geometric points, or dates.

Like other types, value types may have fields (static or
instance), methods (static, instance, or virtual), properties, events, and
nested types. A value type may be converted into a corresponding reference
type (its boxed form, a class automatically created for this purpose by
the VES when a value type is defined) by a process called boxing. A
boxed value type may be converted back into its value type representation, the unboxed
form, by a process called unboxing. Value types shall be sealed,
and they shall have a base type of either System.ValueType
or System.Enum (see Partition IV_alink=Partition_IV). Value types shall implement zero or
more interfaces, but this has meaning only in their boxed form (seeSection 12.3).

Unboxed value types are not considered subtypes of another type
and it is not valid to use the isinst instruction (see Partition III_alink=Partition_III) on unboxed value types. The isinst
instruction may be used for boxed value types. Unboxed value types shall not
be assigned the value null and they shall not be compared to null.

Value types support layout control in the same way as reference
types do (see Section 9.7).
This is especially important when values are imported from native code.

The unboxed form of a value type shall be referred to by using
the valuetype keyword followed by a type reference. The boxed form of a value
type shall be referred to by using the boxed keyword followed by a type
reference.

<valueTypeReference> ::=

boxed <typeReference> |

valuetype <typeReference>

Implementation Specific
(Microsoft)

For historical reasons “value class”
may be used instead of “valuetype” although the
latter is preferred. V1 of the CLI does not support direct references to boxed
value types; they should be treated as object
instead.

Like classes, value types may have both instance constructors
(see clause 9.5.1)
and type initializers (see clause 9.5.3).
Unlike classes that are automatically initialized to null, however, the
following rules constitute the only guarantee about the initilisation of
(unboxed) value types:

·Static variables shall be initialized to zero when a type is
loaded (see clause 9.5.3.3),
hence statics whose type is a value type are zero-initialized when the type is
loaded.

·Local variables shall be initialized to zero if the appropriate
bit in the method header (see clause 24.4.4) is set.

Rationale:Guaranteeing
automatic initialization of unboxed value types is both difficult and
expensive, especially on platforms that support thread-local storage and allow
threads to be created outside of the CLI and then passed to the CLI for management.

Note: Boxed
value types are classes and follow the rules for classes.

The instruction initobj (see Partition III_alink=Partition_III) performs zero-initialization under
program control. If a value type has a constructor, an instance of its unboxed
type can be created as is done with classes. The newobj instruction (see
Partition III_alink=Partition_III) is used along with the initializer
and its parameters to allocate and initialize the instance. The instance of the
value type will be allocated on the stack. The Base Class Library provides the
method System.Array.Initialize
(see Partition IV_alink=Partition_IV) to zero all instances in an array of
unboxed value types.

Example
(informative):

The following code
declares and initializes three value type variables. The first variable is
zero-initialized, the second is initialized by calling an instance constructor,
and the third by creating the object on the stack and storing it into the
local.

12.3Methods of Value Types

Value types may have static, instance and virtual methods. static methods of value types are defined and called the same way as
static methods of class types. As with classes, both instance and virtual
methods of a boxed or unboxed value type may be called using the call instruction. The callvirt instruction shall not be used with unboxed value
types, but it may be used on boxed value types.

Instance and virtual methods of classes shall be coded to expect
a reference to an instance of the class as the this pointer. By
contrast, instance and virtual methods of value types shall be coded to expect
a managed pointer (see Partition I_alink=Partition_I) to an unboxed instance of the value
type. The CLI shall convert a boxed value type into a managed pointer to the
unboxed value type when a boxed value type is passed as the this pointer
to a virtual method whose implementation is provided by the unboxed value type.

Note: This
operation is the same as unboxing the instance, since the unbox
instruction (see Partition III_alink=Partition_III) is defined to return a managed
pointer to the value type that shares memory with the original boxed instance.

The
following diagrams may help understand the relationship between the boxed and
unboxed representations of a value type.

Rationale: An important
use of instance methods on value types is to change internal state of the
instance. This cannot be done if an instance of the unboxed value type is used
for the this pointer, since it would be operating on a copy of the value, not
the original value: unboxed value types are copied when they are passed as
arguments.

Virtual methods are used to allow
multiple types to share implementation code, and this requires that all classes
that implement the virtual method share a common representation defined by the
class that first introduces the method. Since value types can (and in the Base
Class Library do) implement interfaces and virtual methods defined on System.Object,
it is important that the virtual method be callable using a boxed value type
so it can be manipulated as would any other type that implements the
interface. This leads to the requirement that the EE automatically unbox value
types on virtual calls.

Table 1: Type of this
given CIL instruction and declaring type of instance method.

Value Type (Boxed or Unboxed)

Interface

Class Type

call

managed pointer to value type

illegal

object reference

callvirt

managed pointer to value type

object reference

object reference

Example
(informative):

The following
converts an integer of the value type int32 into a string. Recall that int32corresponds to the unboxed value type System.Int32
defined in the Base Class Library. Suppose the integer is declared as:

.locals init (int32 x)

Then the call is
made as shown below:

ldloca
x // load managed pointer to local variable

callinstance
stringvaluetype[mscorlib]System.Convert::ToString()

However, if System.Object (a class) is used as the type reference
rather than System.Int32 (a value type), the
value of x shall be boxed before the call is made and the code becomes:

ldloc x

box
valuetype [mscorlib]System.Int32

callvirtinstancestring [mscorlib]System.Object::ToString()

13Semantics
of Special Types

Special Types are those that are referenced from CIL, but for
which no definition is supplied: the VES supplies the definitions automatically
based on information available from the reference.

13.1Vectors

<type> ::= …

| <type> []

Vectorsare single-dimension arrays with a zero lower
bound. They have direct support in CIL instructions (newarr, ldelem,
stelem, and ldelema, see Partition III_alink=Partition_III). The CIL Framework also provides
methods that deal with multidimensional arrays, or single-dimension arrays with
a non-zero lower bound (see Section 13.2).
Two vectors are the same type if their element types are the same, regardless
of their actual upper bounds.

Vectors have a fixed size and element type, determined when they
are created. All CIL instructions shall respect these values. That is, they
shall reliably detect attempts to index beyond the end of the vector, attempts
to store the incorrect type of data into an element of a vector, and attempts
to take addresses of elements of a vector with an incorrect data type. See Partition III_alink=Partition_III.

Example
(informative):

Declaring a vector
of Strings:

.field string[] errorStrings

Declaring a vector
of function pointers:

.field method instance void*(int32) [] myVec

Create a vector of
4 strings, and store it into the field errorStrings. The four strings
lie at errorStrings[0] through errorStrings[3]:

ldc.i4.4

newarrstring

stfldstring[]
CountDownForm::errorStrings

Store the string
"First" into errorStrings[0]:

ldfld
string[] CountDownForm::errorStrings

ldc.i4.0

ldstr "First"

stelem

Vectors are subtypes of System.Array, an abstract class pre-defined by the
CLI. It provides several methods that can be applied to all vectors. See Partition IV_alink=Partition_IV.

13.2Arrays

While vectors (see Section 13.1)
have direct support through CIL instructions, all other arrays are supported by
the VES by creating subtypes of the abstract class System.Arrray (see Partition IV_alink=Partition_IV)

<type> ::= …

| <type> [ [<bound>
[,<bound>]*] ]

The rank of an array is the number of dimensions. The CLI
does not support arrays with rank 0. The type of an array (other than a
vector) shall be determined by the type of its elements and the number of
dimensions.

<bound> ::=

Description

...

lower and upper bounds unspecified. In the case of
multi-dimensional arrays, the ellipsis may be omitted

| <int32>

zero lower bound, <int32> upper bound

| <int32> ...

lower bound only specified

| <int32> ... <int32>

both bounds specified

The fundamental operations provided by the CIL instruction set
for vectors are provided by methods on the class created by the VES.

The VES shall provide two constructors for arrays. One takes a
sequence of numbers giving the number of elements in each dimension (a lower
bound of zero is assumed). The second takes twice as many arguments: a
sequence of lower bounds, one for each dimension; followed by a sequence of
lengths, one for each dimension (where length is the number of elements
required).

In addition to array constructors, the VES shall provide the
instance methods Get,
Set, and Address to
access specific elements and compute their addresses. These methods take a
number for each dimension, to specify the target element. In addition, Settakes an additional final argument specifying
the value to store into the target element.

Example
(informative):

Creates an array,
MyArray, of strings with two dimensions, with indexes 5..10 and 3..7. Stores
the string "One" into MyArray[5, 3], retrieves it and prints it out.
Then computes the address of MyArray[5, 4], stores "Test" into it,
retrieves it, and prints it out.

.assemblyTest { }

.assembly extern mscorlib { }

.method public static void Start()

{ .maxstack5

.entrypoint

.locals (class [mscorlib]System.String[,] myArray)

ldc.i4.5 //
load lower bound for dim 1

ldc.i4.6 //
load (upper bound - lower bound + 1) for dim 1

ldc.i4.3 //
load lower bound for dim 2

ldc.i4.5 //
load (upper bound - lower bound + 1) for dim 2

newobj
instance void string[,]::.ctor(int32,

int32,
int32, int32)

stloc myArray

ldloc myArray

ldc.i4.5

ldc.i4.3

ldstr "One"

call instance
void string[,]::Set(int32, int32, string)

ldloc myArray

ldc.i4.5

ldc.i4.3

call instance
string string[,]::Get(int32, int32)

call void [mscorlib]System.Console::WriteLine(string)

ldloc myArray

ldc.i4.5

ldc.i4.4

call instance
string & string[,]::Address(int32, int32)

ldstr "Test"

stind.ref

ldloc myArray

ldc.i4.5

ldc.i4.4

call instance
string string[,]::Get(int32, int32)

call void [mscorlib]System.Console::WriteLine(string)

ret

}

The following text is informative

Whilst the elements of multi-dimensional arrays can be thought of
as laid out in contiguous memory, arrays of arrays are different – each
dimension (except the last) holds an array reference. The following picture
illustrates the difference:

On the left is a [6, 10] rectangular array. On the right is not
one, but a total of five arrays. The vertical array is an array of arrays, and
references the four horizontal arrays. Note how the first and second elements
of the vertical array both reference the same horizontal array.

Note that all dimensions of a multi-dimensional array shall be of
the same size. But in an array of arrays, it is possible to reference arrays
of different sizes. For example, the figure on the right shows the vertical
array referencing arrays of lengths 8, 8, 3, null, 6 and 1.

There is no special support for these so-called jagged arrays
in either the CIL instruction set or the VES. They are simply vectors whose
elements are themselves either the base elements or (recursively) jagged
arrays.

An enum, short for enumeration, defines a set of
symbols that all have the same type. A type shall be an enum if and only if it
has an immediate base type of System.Enum. Since System.Enum itself has an immediate base
type of System.ValueType
(see Partition IV_alink=Partition_IV), enums are value types (see Chapter 12). The symbols of an enum are represented by an underlying type: one of { bool, char,
int8, unsignedint8, int16,
unsignedint16, int32,
unsignedint32, int64,
unsignedint64, float32,
float64, nativeint,
unsignednativeint
}

Note: The
CLI does not provide a guarantee that values of the enum type are
integers corresponding to one of the symbols (unlike Pascal). In fact, the CLS
(see Partition I_alink=Partition_I, CLS) defines a convention for using
enums to represent bit flags which can be combined to form integral value that
are not named by the enum type itself.

Enums obey additional restrictions beyond those on other value
types. Enums shall contain only fields as members (they shall not even define
type initializers or instance constructors); they shall not implement any
interfaces; they shall have auto field layout (see clause 9.1.2);
they shall have exactly one instance field and it shall be of the underlying
type of the enum; all other fields shall be static and literal (see Section 15.1);
and they shall not be initialized with the initobj instruction.

Rationale: These
restrictions allow a very efficient implementation of enums.

The single, required, instance field stores the value of an
instance of the enum. The static literal fields of an enum declare the mapping
of the symbols of the enum to the underlying values. All of these fields shall
have the type of the enum and shall have field init metadata that assigns them
a value (see Section 15.2).

For binding purposes (e.g. for locating a method definition from
the method reference used to call it) enums shall be distinct from their
underlying type. For all other purposes, including verification and execution
of code, an unboxed enum freely interconverts with its underlying type. Enums
can be boxed (see Chapter 12)
to a corresponding boxed instance type, but this type is not the same as
the boxed type of the underlying type, so boxing does not lose the original
type of the enum.

Example
(informative):

Declare an enum
type, then create a local variable of that type. Store a constant of the
underlying type into the enum (showing automatic coercsion from the underlying
type to the enum type). Load the enum back and print it as the underlying type
(showing automatic coersion back). Finally, load the address of the enum and
extract the contents of the instance field and print that out as well.

A pointer type shall be defined by specifying a signature
that includes the type for the location it points at. A pointer may be managed
(reported to the CLI garbage collector, denoted by &, see clause 13.4.2)
or unmanaged (not reported, denoted by *, see clause 13.4.1)

Pointers may contain the address of a field (of an object
or value type) or an element of an array. Pointers differ from object
references in that they do not point to an entire type instance, but rather to
the interior of an instance. The CLI provides two type-safe operations
on pointer:

·loading the value from the location referenced by the
pointer

·storing an assignment-compatible value into the location
referenced by the pointer

For pointers into the same array or object (see Partition I_alink=Partition_I) the following arithmetic operations are
supported:

·Adding an integer value to a pointer, where that value is
interpreted as a number of bytes, results in a pointer of the same kind

·Subtracting an integer value (number of bytes) from a pointer
results in a pointer of the same kind. Note that subtracting a pointer from an
integer value is not permitted.

·Two pointers, regardless of kind, can be subtracted from one
another, producing an integer value that specifies the number of bytes between
the addresses they reference.

The following is informative text

Pointers are compatible with unsigned int32 on 32-bit
architectures, and with unsigned int64 on 64-bit architectures. They are best
considered as unsigned int, whose size varies depending upon the runtime
machine architecture.

The CIL instruction set (see Partition III_alink=Partition_III) contains
instructions to compute addresses of fields, local variables, arguments, and
elements of vectors:

Instruction

Description

ldarga

Load address of argument

ldelema

Load address of vector element

ldflda

Load address of field

ldloca

Load address of local variable

ldsflda

Load address of static field

Once a pointer is loaded onto the stack, the ldind class of instructions may be used to load the data item to which it points.
Similarly, the stind class of instructions can be used to store data
into the location.

Note that the CLI will throw an InvalidOperationException for an ldflda
instruction if the address is not within the current application domain.
This situation arises typically only from the use of objects with a base type
of System.MarshalByRefObject(seePartition IV_alink=Partition_IV).

13.4.1Unmanaged Pointers

Unmanaged pointers (*) are the traditional pointers used
in languages like C and C++. There are no restrictions on their use, although
for the most part they result in code that cannot be verified. While it is
perfectly legal to mark locations that contain unmanaged pointers as though
they were unsigned integers (and this is, in fact, how they are treated by the
VES), it is often better to mark them as unmanaged pointers to a specific type
of data. This is done by using * in a signature for a return value, local
variable or an argument or by using a pointer type for a field or array
element.

·Unmanaged pointers are not reported to the garbage collector and
can be used in any way that an integer can be used.

·Verifiable code cannot dereference unmanaged pointers.

·Unverified code can pass an unmanaged pointer to a method that
expects a managed pointer. This is safe only if one of the following is true:

a.The unmanaged pointer refers to memory that is not in memory used by the
CLI for storing instances of objects (“garbage collected memory” or “managed
memory”).

b.The unmanaged pointer contains the address of a field within an object.

c.The unmanaged pointer contains the address of an element within an
array.

d.The unmanaged pointer contains the address where the element following
the last element in an array would be located

13.4.2Managed Pointers

Managed pointers (&) may point to an instance of a value
type, a field of an object, a field of a value type, an element of an array, or
the address where an element just past the end of an array would be stored (for
pointer indexes into managed arrays). Managed pointers cannot be null,
and they shall be reported to the garbage collector even if they do not point
to managed memory.

Managed pointers are specified by using & in a signature for
a return value, local variable or an argument or by using a by-ref type for a
field or array element.

·Managed pointers can be passed as arguments, stored in local
variables, and returned as values.

·If a parameter is passed by reference, the corresponding argument
is a managed pointer.

·A managed pointer cannot point to another managed pointer, but it
can point to an object reference or a value type.

·A managed pointer can point to a local variable, or a method
argument

·Managed pointers that do not point to managed memory can be
converted (using conv.u or conv.ovf.u) into unmanaged pointers,
but this is not verifiable.

e.Unverified code that erroneously converts a managed pointer into an
unmanaged pointer can seriously compromise the integrity of the CLI. See Partition III_alink=Partition_III (Managed
Pointers) for more details.

13.5Method Pointers

<type> ::= …

| method <callConv> <type> *( <parameters> )

Variables of type method pointer shall store the address of the
entry point to a method with compatible signature. A pointer to a static or
instance method is obtained with the ldftn instruction, while a pointer to a virtual method is obtained with the ldvirtftn instruction. A method may be called by using a
method pointer with the calli instruction. See Partition III_alink=Partition_III for the
specification of these instructions.

Note:Like other pointers, method pointers are compatible with unsigned int64
on 64-bit architectures with unsigned int32 and on 32-bit architectures. The
preferred usage, however, is unsigned native int, which works on both
32- and 64-bit architectures.

Call a method using
a pointer. The method MakeDecision::Decide returns a method pointer to either
AddOne or Negate, alternating on each call. The main program call
MakeDecision::Decide three times and after each call uses a CALLI instruction
to call the method specified. The output printed is "-1 2 –1"
indicating successful alternating calls.

.assemblyTest { }

.assembly externmscorlib { }

.method public static int32 AddOne(int32 Input)

{ .maxstack5

ldarg Input

ldc.i4.1

add

ret

}

.method public static int32 Negate(int32 Input)

{ .maxstack5

ldarg Input

neg

ret

}

.class value sealed public MakeDecision extends

[mscorlib]System.ValueType

{ .field static boolOscillate

.method public static method int32 *(int32) Decide()

{ ldsfld bool
valuetype MakeDecision::Oscillate

dup

not

stsfld bool
valuetype MakeDecision::Oscillate

brfalseNegateIt

ldftn int32 AddOne(int32)

ret

NegateIt:

ldftn int32 Negate(int32)

ret

}

}

.method public static void Start()

{ .maxstack2

.entrypoint

ldc.i4.1

call method
int32 *(int32) valuetype MakeDecision::Decide()

calli
int32(int32)

call void [mscorlib]System.Console::WriteLine(int32)

ldc.i4.1

call method
int32 *(int32) valuetype MakeDecision::Decide()

calli
int32(int32)

call void [mscorlib]System.Console::WriteLine(int32)

ldc.i4.1

call method
int32 *(int32) valuetype MakeDecision::Decide()

calli
int32(int32)

call void [mscorlib]System.Console::WriteLine(int32)

ret

}

13.6Delegates

Delegates (see Partition I_alink=Partition_I)
are the object-oriented equivalent of function pointers. Unlike function
pointers, delegates are object-oriented, type-safe, and secure. Delegates are reference types, and are declared in the form of Classes. Delegates
shall have an immediate base type of System.MulticastDelegate,
which in turns has an immediate base type of System.Delegate (see Partition IV_alink=Partition_IV).

Delegates shall be declared sealed, and the only members a
Delegate shall have are either two or four methods as specified here. These
methods shall be declared runtime and managed (see clause 14.4.3).
They shall not have a body, since it shall be automatically created by the VES.
Other methods available on delegates are inherited from the classes System.Delegate
and System.MulticastDelegate
in the Base Class Library (see Partition IV_alink=Partition_IV).

Rationale: A better design
would be to simply have delegate classes derive directly from System.Delegate. Unfortunately,
backward compatibility with an existing CLI does not permit this design.

The instance constructor (named .ctor
and marked specialname and rtspecialname,
see clause 9.5.1)
shall take exactly two parameters. The first parameter shall be of type System.Object
and the second parameter shall be of type System.IntPtr. When actually called (via
a newobj instruction, see Partition III_alink=Partition_III), the first
argument shall be an instance of the class (or one of its subclasses) that
defines the target method and the second argument shall be a method pointer to
the method to be called.

The Invoke
method shall be virtual and have the same signature
(return type, parameter types, calling convention, and modifiers, see Section 7.1) as the target
method. When actually called the arguments passed shall match the types
specified in this signature.

The BeginInvoke
method (see clause 13.6.2.1),
if present,shall
be virtual have a signature related to, but not the
same as, that of the Invoke
method. There are two differences in the signature. First, the return type
shall be System.IAsyncResult
(see Partition IV_alink=Partition_IV).
Second, there shall be two additional parameters that follow those of Invoke: the
first of type System.AsyncCallback and the second of type System.Object.

The EndInvoke
method (see clause 13.6.2) shall be virtual have the same return type as the Invoke method.
It shall take as parameters exactly those parameters of Invoke that are managed
pointers, in the same order they occur in the signature for Invoke. In
addition, there shall be an additional parameter of type System.IAsyncResult.

Example
(informative):

The following
example declares a Delegate used to call functions that take a single integer
and return void. It provides all four methods so it can be called either
synchronously or asynchronously. Because there are no parameters that are
passed by reference (i.e. as managed pointers) there are no additional
arguments to EndInvoke.

.assemblyTest { }

.assembly externmscorlib { }

.class private sealed StartStopEventHandler

extends [mscorlib]System.MulticastDelegate

{ .method public specialname rtspecialname instance

void .ctor(object Instance, native int Method)

runtime managed {}

.method public virtual void Invoke(int32 action)
runtime managed {}

.method public virtual

class [mscorlib]System.IAsyncResult

BeginInvoke(int32
action,

class [mscorlib]System.AsyncCallback callback,

object Instance) runtime managed {}

.method public virtual

void EndInvoke(class
[mscorlib]System.IAsyncResultresult)

runtime
managed {}

}

As with any class, an instance is created using the newobj
instruction in conjunction with the instance constructor. The first argument
to the constructor shall be the object on which the method is to be called, or
it shall be null if the method is a static method. The second argument shall
be a method pointer to a method on the corresponding class and with a signature
that matches that of the delegate class being instantiated.

Implementation-Specific (Microsoft)

The Microsoft
implementation of the CLI allows the programmer to add more methods to a
delegate, on the condition that they provide an implementation for those
methods (ie, they cannot be marked runtime). Note
that such use makes the resulting assembly non-portable.

13.6.1Synchronous
Calls to Delegates

The synchronous mode of calling delegates corresponds to regular
method calls and is performed by calling the virtual method named Invoke on the delegate. The delegate
itself is the first argument to this call (it serves as the this pointer),
followed by the other arguments as specified in the signature. When this call
is made, the caller shall block until the called method returns. The called
method shall be executed on the same thread as the caller.

Example
(informative):

Continuing the
previous example, define a class Test that declares a method, onStartStop,
appropriate for use as the target for the delegate.

13.6.2Asynchronous Calls to Delegates

In the asynchronous mode, the call is dispatched, and the caller
shall continue execution without waiting for the method to return. The called
method shall be executed on a separate thread.

To call delegates asynchronously, the BeginInvoke and EndInvoke methods are used.

Note: if the caller thread terminates before the callee
completes, the callee thread is unaffected. The callee thread continues
execution and terminates silently

Note: the callee may throw exceptions. Any unhandled
exception propagates to the caller via the EndInvoke method.

13.6.2.1The
BeginInvoke Method

An asynchronous call to a delegate shall begin by making a
virtual call to the BeginInvoke
method. BeginInvoke
is similar to the Invoke
method (see clause 13.6.1),
but has three differences:

·It has a two additional parameters, appended to the list, of type
System.AsyncCallback, and System.Object

·The return type of the method is System.IAsyncResult

Although the BeginInvoke method therefore includes parameters that
represent return values, these values are not updated by this method. The
results instead are obtained from the EndInvoke method (see below).

Unlike a synchronous call, an asynchronous call shall provide a
way for the caller to determine when the call has been completed. The CLI
provides two such mechanisms. The first is through the result returned from
the call. This object, an instance of the interface System.IAsyncResult, can be used to wait
for the result to be computed, it can be queried for the current status of the
method call, and it contains the System.Object
value that was passed to the call to BeginInvoke. See Partition IV_alink=Partition_IV.

The second mechanism is through the System.AsyncCallback delegate passed to BeginInvoke.
The VES shall call this delegate when the value is computed or an exception has
been raised indicating that the result will not be available. The value passed
to this callback is the same value passed to the call to BeginInvoke. A
value of nullmay be passed for System.AsyncCallback
to indicate that the VES need not provide the callback.

Rationale: This model
supports both a polling approach (by checking the status of the returned System.IAsyncResult)
and an event-driven approach (by supplying a System.AsyncCallback) to asynchronous
calls.

A
synchronous call returns information both through its return value and through
output parameters. Output parameters are represented in the CLI as parameters
with managed pointer type. Both the returned value and the values of the
output parameters are not available until the VES signals that the asynchronous
call has completed successfully. They are retrieved by calling the EndInvoke method
on the delegate that began the asynchronous call.

13.6.2.2The EndInvoke Method

The EndInvoke
method can be called at any time after BeginInvoke. It shall suspend the
thread that calls it until the asynchronous call completes. If the call
completes successfully, EndInvoke
will return the value that would have been returned had the call been made
synchronously, and its managed pointer arguments will point to values that
would have been returned to the out parameters of the synchronous call.

EndInvoke
requires as parameters the value returned by the originating call to BeginInvoke (so
that different calls to the same delegate can be distinguished, since they may
execute concurrently) as well as any managed pointers that were passed as
arguments (so their return values can be provided).

A MethodDecl, or method declaration, supplies the method
name and signature (parameter and return types), but not its body. That is, a
method declaration provides a <methodHead> but no
<methodBodyItem>s. These are used at callsites to specify the call target
(call or callvirt instructions, see Partition III_alink=Partition_III) or to
declare an abstract
method. A MethodDecl has no direct logical couterpart in the metadata;
it can be either a Method or a MethodRef.

A Method, or method definition, supplies the method name,
attributes, signature and body. That is, a method definition provides a
<methodHead> as well as one or more <methodBodyItem>s. The body
includes the method's CIL instructions, exception handlers, local variable
information, and additional runtime or custom metadata about the method. See Chapter 11.

14.1.3Method References

A MethodRef, or method reference, is a reference to a
method. It is used when a method is called whose definition lies in another
module or assembly. A MethodRef shall be resolved by the VES into a Method
before the method is called at runtime. If a matching Method cannot be
found, the VES shall throw a System.MissingMethodException. See Chapter 21.23.

14.1.4Method Implementations

A MethodImpl, or method implementation, supplies the
executable body for an existing virtual method. It associates a Method
(representing the body) with a MethodDecl or Method (representing
the virtual method). A MethodImpl is used to provide an implementation
for an inherited virtual method or a virtual method from an interface when the
default mechanism (matching by name and signature) would not provide the
correct result. See Section 21.25.

14.2Static, Instance, and
Virtual Methods

Static methods are methods that are associated with a type, not
with its instances.

Instance methods are associated with an instance of a type:
within the body of an instance method it is possible to reference the
particular instance on which the method is operating (via the this pointer).
It follows that instance methods may only be defined in classes or value types,
but not in interfaces or outside of a type (globally). However, notice

1.instance methods on classes (including boxed value types), have a this
pointer that is by default an object reference to the class on which the
method is defined

2.instance methods on (unboxed) value types, have a this pointer
that is by default a managed pointer to an instance of the type on which the
method is defined

3.there is a special encoding (denoted by the syntactic item explicit in
the calling convention, see Section 14.3) to specify the type of the this
pointer, overriding the default values specified here

4.the this pointer may be null

Virtual methods are associated with an instance of a type in much the same way as for instance methods. However,
unlike instance methods, it is possible to call a virtual method in such a way
that the implementation of the method shall be chosen at runtime by the VES
depends upon the type of object used for the this pointer. The
particular Method that implements a virtual method is determined
dynamically at runtime (a virtual call) when invoked via the callvirt
instruction; whilst the binding is decided at compile time when invoked via the
call instruction (see Partition III_alink=Partition_III).

With virtual calls (only) the notion of inheritance becomes
important. A subclass may override a virtual method inherited from its
base classes, providing a new implementation of the method. The method
attribute newslot specifies that the CLI shall not override the virtual method
definition of the base type, but shall treat the new definition as an
independent virtual method definition.

Abstract
virtual methods (which shall only be defined in abstract classes or interfaces) shall
be called only with a callvirt instruction. Similarly, the address of
an abstract
virtual method shall be computed with the ldvirtftn instruction, and the
ldftn instruction shall not be used.

Rationale: With a concrete
virtual method there is always an implementation available from the class that
contains the definition, thus there is no need at runtime to have an instance
of a class available. Abstract
virtual methods, however, receive their implementation only from a subtype or a
class that implements the appropriate interface, hence an instance of a class
that actually implements the method is required.

14.3Calling
Convention

<callConv> ::= [instance [explicit]]
[<callKind>]

A calling convention specifies how a method expects its arguments
to be passed from the caller to the called method. It consists of two parts;
the first deals with the existence and type of the this pointer, while
the second relates to the mechanism for transporting the arguments.

If the attribute instanceis present it indicates that a this
pointer shall be passed to the method. It shall be used for both instanceand virtual methods.

Implementation Specific (Microsoft)

For simplicity, the
assembler automatically sets or clears the instance bit in the calling
convention for a method definition based on the method attributes static
and virtual. In a method reference, however, the instance bit
shall be specified directly since the information about static or virtual
is not captured in a reference.

Normally, a parameter list (which always follows the calling
convention) does not provide information about the type of the this pointer,
since this can be deduced from other information. When the combination
instance explicit is specified, however, the first type in the subsequent
parameter list specifies the type of the this pointer and subsequent
entries specify the types of the parameters themselves.

<callKind> ::=

default

| unmanaged cdecl

| unmanaged fastcall

| unmanaged stdcall

| unmanaged thiscall

| vararg

Managed code shall have only the default or vararg calling kind. defaultshall be used in all cases except when a method accepts an arbitrary number
of arguments, in which case vararg shall be used.

When dealing with methods implemented outside the CLI it is
important to be able to specify the calling convention required. For this
reason there are 16 possible encodings of the calling kind. Two are used for
the managed calling kinds. Four are reserved with defined meaning across many
platforms:

·unmanaged cdecl is the calling convention used by standard C

·unmanaged stdcall specifies a standard C++ call

·unmanaged fastcall is a special optimized C++ calling convention

·unmanaged thiscall is a C++ call that passes a this pointer to the method

Four more are reserved for existing calling conventions, but
their use is not portable. Four more are reserved for future standardization,
and two are available for non-standard experimental use.

(By "portable" is meant a feature that is available on
all conforming implementations of the CLI)

The <id>, if present, is the name of the parameter. A
parameter may be referenced either by using its name or the zero-based index of
the parameter. In CIL instructions it is always encoded using the zero-based
index (the name is for ease of use in ilasm).

Note that, in contrast to calling a varargmethod, the definition
of a vararg method does not include any ellipsis (“…”)

<paramAttr> ::=

[in]

| [opt]

| [out]

The parameter attributes shall be attached to the parameters (see
Section 21.30) and hence
are not part of a method signature.

Note:Unlike parameter attributes, custom modifiers (modopt
and modreq) are part of the signature. Thus,
modifiers form part of the method’s contract while parameter attributes are
not.

inand outshall only be attached to parameters of pointer (managed or unmanaged)
type. They specify whether the parameter is intended to supply input to the
method, return a value from the method, or both. If neither is specified in is
assumed. The CLI itself does not enforce the semantics of these bits, although
they may be used to optimize performance, especially in scenarios where the
call site and the method are in different application domains, processes, or
computers.

opt specifies that this parameter is intended to be optional from
an end-user point of view. The value to be supplied is stored using the .param syntax (see clause 14.4.1.4).

14.4.1Method Body

The method body shall contain the instructions of a program.
However, it may also contain labels, additional syntactic forms and many
directives that provide additional information to ilasm and are helpful in the
compilation of methods of some languages.

<methodBodyItem> ::=

Description

Section

.custom<customDecl>

Definition of custom attributes.

20

| .data<datadecl>

Emits data to the data section

15.3

| .emitbyte <unsigned int8>

Emits a byte to the code section of the method.

14.4.1.1

| .entrypoint

Specifies that this method is the entry point to the
application (only one such method is allowed).

14.4.1.2

| .locals [init]

( <localsSignature> )

Defines a set of local variables for this method.

14.4.1.3

| .maxstack <int32>

int32 specifies the maximum number of elements on the
evaluation stack during the execution of the method

Emits an unsigned 8 bit value directly into the CIL stream of the
method. The value is emitted at the position where the directive appears.

Note:
the .emitbyte directive is used for generating
tests. It is not required in generating regular programs

14.4.1.2.entrypoint

<methodBodyItem> ::= …

| .entrypoint

The .entrypoint directive marks the current method, which shall be static, as the entry point to an
application. The VES shall call this method to start the application. An
executable shall have exactly one entry point method. This entry point method
may be a global method or may appear inside a type. (The effect of the
directive is to place the metadata token for this method into the CLI header of
the PE file)

The entry point method shall either accept no arguments or a
vector of strings. If it accepts a vector of strings, the strings shall
represent the arguments to the executable, with index 0 containing the first
argument. The mechanism for specifying these arguments is platform-specific
and is not specified here.

The return type of the entry point method shall be void, int32,
or unsigned int32. If an int32 or unsigned int32 is returned, the executable
may return an exit code to the host environment. A value of 0 shall indicate
that the application terminated ordinarily.

The accessibility of the entry point method shall not prevent its
use in starting execution. Once started the VES shall treat the entry point as
it would any other method.

Example (informative):

The following
example prints the first argument and return successfully to the operating
system:

ilasm allows nested
local variable scopes to be provided and allows locals in nested scopes to
share the same location as those in the outer scope. The information about
local names, scoping, and overlapping of scoped locals is persisted to the PDB
(debugger symbol) file rather than the PE file itself.

<local> ::= [[<int32>]]
<type> [<id>]

The integer in
brackets that precedes the <type>, if present, specifies the local number
(starting with 0) being described. This allows nested locals to reuse the same
location as a local in the outer scope. It is not legal to overlap two local
variables unless they have the same type. When no explicit index is specified,
the next unused index is chosen. That is, two locals never share and index
unless the index is given explicitly.

If init is used,
all local variables will be initialized to their default values, even variables
in another .locals directive in the same method,
which does not have the init directive.

14.4.1.4.param

<methodBodyItem> ::= …

| .param [ <int32> ] [= <fieldInit>]

Stores in the metadata a constant value associated with method
parameter number <int32>, see Section 21.9.
While the CLI requires that a value be supplied for the parameter, some tools
may use the presence of this attribute to indicate that the tool rather than
the user is intended to supply the value of the parameter. Unlike CIL
instructions, .param uses index 0 to specify the
return value of the method, index 1 is the first parameter of the method, and
so forth.

Note:
The CLI attaches no semantic whatsoever to these values – it is entirely up to
compilers to implement any semantic they wish (eg so-called default argument
values)

14.4.2Predefined Attributes on Methods

<methAttr> ::=

Description

Section

abstract

The method is abstract (shall
also be virtual).

14.4.2.4

| assembly

Assembly accessibility

14.4.2.1

| compilercontrolled

Compiler-controlled accessibility.

14.4.2.1

| famandassem

Family and Assembly accessibility

14.4.2.1

| family

Family accessibility

14.4.2.1

| famorassem

Family or Assembly accessibility

14.4.2.1

| final

This virtual method cannot be overridden by subclasses.

14.4.2.2

| hidebysig

Hide by signature. Ignored by the runtime.

14.4.2.2

| newslot

Specifies that this method shall get a new slot in the
virtual method table.

14.4.2.3

| pinvokeimpl (<QSTRING> [as <QSTRING>]
<pinvAttr>* )

Method
is actually implemented in native code on the underlying platform

14.4.2.5

| private

Private accessibility

14.4.2.1

| public

Public accessibility.

14.4.2.1

| rtspecialname

The method name needs to be treated in a special way by the
runtime.

14.4.2.6

| specialname

The method name needs to be treated in a special way by
some tool.

14.4.2.6

| static

Method is static.

14.4.2.2

| virtual

Method is virtual.

14.4.2.2

Implementation Specific
(Microsoft)

The
following syntax is supported:

<methAttr> ::= … | unmanagedexp |
reqsecobj

unmanagedexp indicates that the method is exported to
unmanaged code using COM interop; reqsecobj
indicates that the method calls another method with security attributes.

Note
that in the first release, ilasm does not recognize the compilercontrolled
keyword. Instead, use privatescope.

The
following combinations of predefined attributes are illegal:

·static combined with any of final, virtual,
or newslot

·abstract combined with any of final
or pinvokeimpl

·compilercontrolled combined with any of virtual, final,
specialname or rtspecialname

These attributes may be combined, except a method shall not be both
static and virtual; only virtual methods may be final;
and abstract methods shall not be final.

final methods shall not be overridden
by subclasses of this type.

hidebysig is supplied for the use of
tools and is ignored by the VES. It specifies that the declared method hides
all methods of the parent types that have a matching method signature; when
omitted the method should hide all methods of the same name, regardless of the
signature.

Rationale: Some languages
use a hide-by-name semantics (C++) while others use a
hide-by-name-and-signature semantics (C#, Java™)

14.4.2.3Overriding Behavior

14.4.2.4Method
Attributes

<methAttr> ::= …

| abstract

abstract shall only be used with
virtual methods that are not final. It specifies that an implementation of the
method is not provided but shall be provided by a subclass. Abstract methods
shall only appear in abstract types (see clause 9.1.4).

These attributes are exclusive, they specify the type of code the
method contains.

cil specifies that the method body consists of cil code.
Unless the method is declared abstract, the body of
the method shall be provided if cil is used.

native specifies that a method was implemented using
native code, tied to a specific processor for which it was generated. native
methods shall not have a body but instead refer to a native method that declares
the body. Typically, the PInvoke functionality (see clause 14.5.2) of the CLI is used to refer to a
native method.

runtime specifies that the implementation of the method is
automatically provided by the runtime and is primarily used for the method of
delegates (see Section 13.6).

forwardref specifies that the body of
the method is provided elsewhere. This attribute shall not be present when an
assembly is loaded by the VES. It is used for tools (like a static linker)
that will combine separately compiled modules and resolve the forward
reference.

internalcall specifies that the method
body is provided by this CLI (and is typically used by low-level methods in a
system library). It shall not be applied to methods that are intended for use
across implementations of the CLI.

Implementation Specific (Microsoft)

internalcall allows the lowest level parts of the Base
Class Library to wrap unmanaged code built into the CLI.

noinlining specifies that the body of
this method should not be included into the code of any caller methods, by a
CIL-to-native-code compiler; it shall be kept as a separate routine.

Rationale:specifying that
a method not be inlined ensures that it remains 'visible' for debugging (eg
displaying stack traces) and profiling. It also provides a mechanism for the
programmer to override the default heuristics a CIL-to-native-code compiler
uses for inlining.

synchronized specifies that the whole
body of the method shall be single threaded. If this method is an instance or
virtual method a lock on the object shall be obtained before the method is
entered. If this method is a static method a lock on the type shall be obtained
before the method is entered. If a lock cannot be obtained the requesting
thread shall not proceed until it is granted the lock. This may cause
deadlocks. The lock is released when the method exits, either through a normal
return or an exception. Exiting a synchronized method using a tail.call shall be implemented as though the tail. had not been specified. noinlining
specifies that the runtime shall not inline this method. Inlining refers to the
process of replacing the call instruction with the body of the called method.
This may be done by the runtime for optimization purposes.

14.4.4Scope
Blocks

Implementation Specific (Microsoft)

Scope blocks are syntactic sugar and primarily serve for readability and debugging purposes.

<scopeBlock> ::= {
<methodBodyItem>* }

A scope block
defines the scope in which a local variable is accessible by its name. Scope
blocks may be nested, such that a reference of a local variable will first be
resolved in the innermost scope block, then at the next level, and so on until
the top-most level of the method, is reached. A declaration in an inner scope
block hides declarations in the outer layers.

If duplicate
declarations are used, the reference will be resolved to the first occurrence.
Even though valid CIL, duplicate declarations are not recommended.

Scoping does not
affect the lifetime of a local variable. All local variables are created (and
if specified initialized) when the method is entered. They stay alive until the
execution of the method is completed.

The scoping does
not affect the accessibility of a local variable by its zero based index. All
local variables are accessible from anywhere within the method by their index.

The index is
assigned to a local variable in the order of declaration. Scoping is ignored
for indexing purposes. Thus, each local variable is assigned the next available
index starting at the top of the method. This behavior can be altered by
specifying an explicit index, as described by a <localsSignature>
as shown in clause 14.4.1.3.

14.4.5vararg Methods

vararg methods accept a variable number of arguments. They shall use the vararg
calling convention (see Section 14.3).

At each call site, a method reference shall be used to describe
the types of the actual arguments that are passed. The fixed part of the
argument list shall be separated from the additional arguments with an ellipsis
(see Partition I_alink=Partition_I).

The vararg arguments shall be accessed
by obtaining a handle to the argument list using the CIL instruction arglist (see Partition III_alink=Partition_III). The
handle may be used to create an instance of the value type System.ArgIteratorwhich
provides a typesafe mechanism for accessing the arguments (see Partition IV_alink=Partition_IV).

Example
(informative):

The following
example shows how a vararg method is declared and
how the first vararg argument is accessed, assuming
that at least one additional argument was passed to the method:

14.5Unmanaged
Methods

In addition to supporting managed code and managed data, the CLI
provides facilities for accessing pre-existing native code from the underlying
platform, known as unmanaged code. These facilities are, by necessity,
platform dependent and hence are only partially specified here.

This standard specifies:

·A mechanism in the file format for providing function pointers to
managed code that can be called from unmanaged code (see clause 14.5.1).

·A mechanism for marking call sites used with method pointers to
indicate that the call is to an unmanaged method (see clause 14.5.3).

·A small set of pre-defined data types that can be passed
(marshaled) using these mechanisms on all implementations of the CLI (see clause 14.5.5). The set of types is extensible through the use of custom attributes and modifiers, but these extensions are platform-specific.

14.5.1Method Transition Thunks

Note: This
mechanism is not part of the Kernel Profile, so it may not be present in all
conforming implementations of the CLI. See Partition IV_alink=Partition_IV.

In order to call from unmanaged code into managed code some
platforms require a specific transition sequence to be performed. In addition,
some platforms require that the representation of data types be converted (data
marshalling). Both of these problems are solved by the .vtfixup directive. This directive may appear several times only at the top level of a CIL
assembly file, as shown by the following grammar:

<decl> ::=

Section

.vtfixup<vtfixupDecl>

| …

5.10

The .vtfixup directive declares that
at a certain memory location there is a table that contains metadata tokens
referring to methods that shall be converted into method pointers. The CLI will
do this conversion automatically when the file is loaded into memory for
execution. The declaration specifies the number of entries in the table, what
kind of method pointer is required, the width of an entry in the table, and the
location of the table:

<vtfixupDecl> ::=

[ <int32> ] <vtfixupAttr>* at
<dataLabel>

<vtfixupAttr> ::=

fromunmanaged

| int32

| int64

The attributes int32 and int64 are mutually exclusive and int32
is the default. These attributes specify the width of each slot in the table.
Each slot contains a 32-bit metadata token (zero-padded if the table has 64 bit
slots), and the CLI converts it into a method pointer of the same width as the
slot.

If fromunmanaged is specified, the CLI will generate a thunk that will convert the unmanaged method call to a managed
call, call the method, and return the result to the unmanaged environment. The
thunk will also perform data marshalling in the platform-specific manner
described for platform invoke.

The ilasm syntax does not specify a mechanism for creating
the table of tokens, but a compiler may simply emit the tokens as byte literals
into a block specified using the .data directive.

14.5.2Platform Invoke

Methods defined in native code may be invoked using the platform
invoke(also know as PInvoke or p/invoke)
functionality of the CLI. Platform invoke will switch from managed to
unmanaged state and back and also handle necessary data marshalling. Methods
that need to be called using PInvoke are marked as pinvokeimpl. In addition, the methods shall have the implementation attributes native and unmanaged (see clause 14.4.2.4).

<methAttr> ::=

Description

Section

pinvokeimpl ( <QSTRING> [as <QSTRING>]<pinvAttr>* )

Implemented
in native code

| …

14.4.2

The first quoted string is a platform-specific description
indicating where the implementation of the method is located (for example, on
Microsoft Windows™ this would be the name of the DLL that implements the
method). The second (optional) string is the name of the method as it exists
on that platform, since the platform may use name-mangling rules that force the
name as it appears to a managed program to differ from the name as seen in the
native implementation (this is common, for example, when the native code is
generated by a C++ compiler).

Only static methods, defined at global scope (ie, outside of any
type), may be marked pinvokeimpl. A method declared
with pinvokeimpl shall not have a body specified as
part of the definition.

<pinvAttr> ::=

Description (platform specific, suggestion only)

ansi

ANSI
character set.

| autochar

Determine character set automatically.

| cdecl

Standard C style call

| fastcall

C style fastcall.

| stdcall

Standard C++ style call.

| thiscall

The method accepts an implicit this pointer.

| unicode

Unicode character set.

| platformapi

Use call convention appropriate to target platform.

Implementation Specific
(Microsoft)

In
first release, platformapi is not recognized by ilasm. Instead
use winapi.

The attributes ansi, autochar, and unicode are
mutually exclusive. They govern how strings will be marshaled for calls to
this method: ansi indicates that the native code
will receive (and possibly return) a platform-specific representation that
corresponds to a string encoded in the ANSI character set (typically this
would match the representation of a C or C++ string constant); autochar indicates a platform-specific representation that
is “natural” for the underlying platform; and unicode
indicates a platform-specific representation that corresponds to a string
encoded for use with Unicode methods on that platform.

The attributes cdecl, fastcall, stdcall, thiscall, and platformapi are
mutually exclusive. They are platform-specific and specificy the calling
conventions for native code.

Implementation Specific (Microsoft)

In addition, the
Microsoft implementation of the CLI on Microsoft Windows™ supports the
following attributes:

lasterr to indicate that the native method supports C
style last error querying.

nomangle to indicate that the name in the DLL should be
used precisely as specified, rather than attempting to add A (for ascii) or W
(widechar) to find platform-specific variants based on the type of string
marshalling requested.

Example
(informative):

The following shows
the declaration of the method MessageBeep located
in the Microsoft Windows™ DLL user32.dll:

14.5.3Via Function Pointers

Unmanaged functions can also be called via function pointers.
There is no difference between calling managed or unmanaged functions with
pointers. However, the unmanaged function needs to be declared with pinvokeimpl as described in clause 14.5.2. Calling managed methods with function pointers is described in Section 13.5

Unmanaged COM operates primarily by publishing uniquely identified interfaces and then sharing
them between implementers (traditionally called “servers”) and users
(traditionally called “clients”) of a given interface. It supports a rich set
of types for use across the interface, and the interface itself can supply
named constants and static methods, but it does not supply instance fields,
instance methods, or virtual methods.

The CLI provides
mechanisms useful to both implementers and users of existing classical COM
interfaces. The goal is to permit programmers to deal with managed data types
(thus eliminating the need for explicit memory management) while at the same
time allowing interoperability with existing unmanaged servers and clients. COM
Interop does not support the use of global functions (i.e. methods that are not
part of a managed type), static functions, or parameterized constructors.

Given an existing
classical COM interface definition as a type library, the tlbimptool
produces a file that contains the metadata describing that interface. The types
it exposes in the metadata are managed counterparts of the unmanaged types in
the original interface.

Implementers
of an existing classical COM interface can import the metadata produced
by tlbimp and then write managed types that provide the implementation of the
methods required by that interface. The metadata specifies the use of managed
data types in many places, and the CLI provides automatic marshaling (i.e.
copying with reformatting) of data between the managed and unmanaged data
types.

Implementers
of a new service can simply write a managed program whose publicly visible
types adhere to a simple set of rules. They can then run the tlbexp tool to produce a type library for classical COM users. This set of rules guarantees
that the data types exposed to the classical COM user are unmanaged types that
can be marshaled automatically by the CLI.

Implementers
need to run the RegAsm tool to register their implementation with classical COM
for location and activation purposes – if they wish to expose managed services
to unmanaged code

Users of existingclassical COM interfaces simply import the metadata produced by tlbimp. They can then reference the (managed) types defined there and the CLI uses the
assembly mechanism and activation information to locate and instantiate
instances of objects implementing the interface. Their code is the same whether
the implementation of the interfaces is provided using classical COM
(unmanaged) code or the CLI (managed) code: the interfaces they see use managed
data types, and hence do not need explicit memory management.

For some existing
classical COM interfaces, the CLI provides an implementation of the interface.
In some cases the EE allows the user to specify all or parts of the
implementation; for others it provides the entire implementation.

14.5.5Data Type Marshaling

While data type marshaling is necessarily platform-dependent,
this standard specifies a minimum set of data types that shall be supported by
all conforming implementations of the CLI. Additional data types may be
supported in an implementation-dependent manner, using custom attributes and/or
custom modifiers to specify any special handling required on the particular
implementation.

The following data types shall be marshaled by all conforming
implementations of the CLI; the native data type to which they conform is
implementation specific:

·All floating point data types (float32
and float64), if they are supported by the CLI
implementation for managed code.

·The type string.

·Unmanaged pointers to any of the above types.

In addition, the following types shall be supported for
marshaling from managed code to unmanaged code, but need not be supported in
the reverse direction (i.e. as return types when calling unmanaged methods or
as parameters when calling from unmanaged methods into managed methods)

·One-dimensional zero-based arrays of any of the above

·Delegates (the mechanism for calling from unmanaged code into a
delegate is platform-specific; it should not be assumed that marshaling a
delegate will produce a function pointer that can be used directly from unmanaged
code)

Finally, the type GCHandle can be used to marshal an
object to unmanaged code. The unmanaged code receives a platform-specific data
type that can be used as an “opaque handle” to a specific object. See Partition IV_alink=Partition_IV.

14.5.6Managed
Native Calling Conventions (x86)

Implementation Specific (Microsoft)

This section is
intended for an advanced audience. It describes the details of a native method
call from managed code on the x86 architecture. The information provided in
this section may be important for optimization purposes. This section is not
important for the further understanding of the CLI and may be skipped.

There are two
managed native calling conventions used on the x86. They are described here for
completeness and because knowledge of these conventions allows an unsafe
mechanism for bypassing the overhead of a managed to unmanaged code
transition.

The standard native
calling convention is a variation on the fastcall convention used by VC. It
differs primarily in the order in which arguments are pushed on the stack.

The only values
that can be passed in registers are managed and unmanaged pointers, object
references, and the built-in integer types int8, unsigned int8, int16, unsigned
int16, int32, unsigned it32, native int and native unsigned int. Enums are
passed as their underlying type. All floating point values and 8-byte integer
values are passed on the stack. When the return type is a value type that
cannot be passed in a register, the caller shall create a buffer to hold the
result and pass the address of this buffer as a hidden parameter.

Arguments are
passed in left-to-right order, starting with the this pointer (for
instance and virtual methods), followed by the return buffer pointer if needed,
followed by the user-specified argument values. The first of these that can be
placed in a register is put into ECX, the next in EDX, and all subsequent ones
are passed on the stack.

The return value is
handled as follows:

Floating point
values are returned on the top of the hardware FP stack.

Integers up to 32
bits long are returned in EAX.

64-bit integers are
passed with EAX holding the least significant 32 bits and EDX holding the most
significant 32 bits.

All other cases
require the use of a return buffer, through which the value is returned.

In addition, there
is a guarantee that if a return buffer is used a value is stored there only
upon ordinary exit from the method. The buffer is not allowed to be used for
temporary storage within the method and its contents will be unaltered if an
exception occurs while executing the method.

Example
(informative)

static
System.Int32 f(int32 x)

The incoming
argument (x) is placed in ECX; the return value is in EAX

staticfloat64
f(int32 x, int32 y, int32 z)

x is passed in ECX,
y in EDX, z on the top of stack; the return value is on the top of the floating
point (FP) stack

staticfloat64
f(int32 x, float64 y, float64 z)

x is passed in ECX,
y on the top of the stack (not FP stack), z in EDX; the return value is on the
top of the FP stack

virtualfloat64
f(int32 x, int64 y, int64 z)

this is
passed in ECX, x in EDX, y pushed on the stack, then z pushed on the stack
(hence z is top of the stack); the return value is on the top of the FP stack

virtualint64
f(int32 x, float64 y, float64 z)

this is
passed in ECX, x in EDX, y pushed on the stack, then z pushed on the stack
(hence z is on top of the stack); the return value is in EDX/EAX

virtual[mscorlib]System.Guid
f(int32 x, float64 y, float64 z)

Since System.Guidis a value type the thispointer
is passed in ECX, a pointer to the return buffer is passed in EDX, x is pushed,
then y, and then z (hence z is on top the of stack); the return value is stored
in the return buffer.

All user-specified
arguments are passed on the stack, pushed in left-to-right order. Following
the last argument (hence on top of the stack upon entry to the method body) a
special cookie is passed which provides information about the types of
the arguments that have been pushed.

As with the
standard calling convention, the this pointer and a return buffer (if
either is needed) are passed in ECX and/or EDX.

Values are returned
in the same way as for the standard calling convention.

Transitions from
managed to unmanaged code require a small amount of overhead to allow
exceptions and garbage collection to correctly determine the execution context.
On an x86 processor, under the best circumstances, these transitions take
approximately 5 instructions per call/return from managed to unmanaged code. In
addition, any method that includes calls with transitions incurs an 8
instruction overhead spread across the calling method’s prolog and epilog.

This overhead can
become a factor in performance of certain applications. For use in unverifiable
code only, there is a mechanism to call from managed code to unmanaged code
without the overhead of a transition. A “fast native call” is accomplished by
the use of a calli instruction which indicates that the destination is managed
even though the code address to which it refers is unmanaged. This can be
arranged, for example, by initializing a variable of type function pointer
in unmanaged code.

Clearly, this
mechanism shall be tightly constrained since the transition is essential if
there is any possibility of a garbage collection or exception occurring while
in the unmanaged code. The following restrictions apply to the use of this
mechanism:

The unmanaged code
shall follow one of the two managed calling conventions (regular and vararg)
that are specified below. In V1, only the regular calling convention is
supported for fast native calls.

The unmanaged code
shall not execute for any extended time, since garbage collection cannot begin
while executing this code. It is wise to keep this under 100 instructions under
all control flow paths.

The unmanaged code
shall not throw an exception (managed or unmanaged), including access
violations, etc. Page faults are not considered an exception for this purpose.

The unmanaged code
shall not call back into managed code.

The unmanaged code
shall not trigger a garbage collection (this usually follows from the
restriction on calling back to managed code).

The unmanaged code
shall not block. That is, it shall not call any OS-provided routine that might
block the thread (synchronous I/O, explicitly acquiring locks, etc.) Again,
page faults are not a problem for this purpose.

The managed code
that calls the unmanaged method shall not have a long, tight loop in which it
makes the call. The total time for the loop to execute should remain under 100
instructions or the loop should include at least one call to a managed method.
More technically, the method including the call shall produce “fully
interruptible native code.” In future versions, there may be a way to indicate
this as a requirement on a method.

Note:
restrictions 2 through 6 apply not only to the unmanaged code called directly,
but to anything it may call.

15Defining
and Referencing Fields

Fields are typed memory locations that store the data of a
program. The CLI allows the declaration of both instance and static fields.
While static fields are associated with a type and shared across all instances
of that type, instance fields are associated with a particular instance of that
type. When instantiated, the instance has its own copy of that field.

The CLI also supports global fields, which are fields declared
outside of any type definition. Global fields shall be static.

·an optional integer specifying the byte offset of the field
within an instance (see Section 9.7). If present, the type containing this field shall have the explicit layout attribute. An offset shall not be supplied for global or static fields.

Global fields shall have a data label associated with them. This
specifies where, in the PE file, the data for that field is located. Static
fields of a type may, but do not need to, be assigned a data label.

Example
(informative):

.field private class [.moduleCounter.dll]Counter counter

15.1Attributes of Fields

Attributes of a field specify information about accessibility,
contract information, interoperation attributes, as well as information on
special handling.

The following subsections contain additional information on each
group of predefined attributes of a field.

<fieldAttr> ::=

Description

Section

assembly

Assembly accessibility.

15.1.1

| famandassem

Family and Assembly accessibility.

15.1.1

| family

Family accessibility.

15.1.1

| famorassem

Family or Assembly accessibility.

15.1.1

| initonly

Marks a constant field.

15.1.2

| literal

Specifies metadata field. No memory is allocated at
runtime for this field.

15.1.2Field Contract Attributes

Field contract attributes are initonly, literal, static and notserialized. These attributes may be combined.
Only static fields may be literal. The default is an instance field that may
be serialized.

static specifies that the field is
associated with the type itself rather than with an instance of the type.
Static fields can be accessed without having an instance of a type, e.g. by
static methods. As a consequence, a static field is shared, within an
application domain, between all instances of a type, and any modification of
this field will affect all instances. If static is
not specified, an instance field is created.

initonly marks fields which are
constant after they are initialized. These fields may only be mutated inside a
constructor. If the field is a static field, then it may be mutated only inside
the type initializer of the type in which it was declared. If it is an instance
field, then it may be mutated only in one of the instance constructors of the
type in which it was defined. It may not be mutated in any other method or in
any other constructor, including constructors of subclasses.

Note:
The VES need not check whether initonly fields are
mutated outside the constructors. The VES need not report any errors if a
method changes the value of a constant. However, such code is not valid and is
not verifiable.

Implementation Specific (Microsoft)

notserialized specifies that this field is not serialized
when an instance of this type is serialized (see clause 9.1.6). It has no meaning on global or static fields, nor if the type does not have the serializable attribute.

literalspecifies that this
field represents a constant value; they shall be assigned a value. In contrast
to initonly fields, literal
fields do not exist at runtime. There is no memory allocated for them. literalfields become part of the metadata but
cannot be accessed by the code. literalfields
are assigned a value by using the <fieldInit> syntax (see Section 15.2).

Note: It
is the responsibility of tools generating CIL to replace source code references
to the literal with its actual value. Hence changing the value of a literal
requires recompilation of any code that references the literal. Literal values
are, thus, not version-resilient.

There is one attributefor interoperation with
pre-existing native applications; it is platform-specific and shall not be used
in code intended to run on multiple implementations of the CLI. The attribute
is marshal and specifies that the field’s contents
should be converted to and from a specified native data type when passed to
unmanaged code. Every conforming implementation of the CLI will have default
marshaling rules as well as restrictions on what automatic conversions can be
specified using the marshalattribute. See
also clause 14.5.5

Note: Marshaling
of user-defined types is not required of all implementations of the CLI. It is
specified in this standard so that implementations which choose to provide it
will allow control over its behavior in a consistent manner. While this is not
sufficient to guarantee portability of code that uses this feature, it does
increase the likelihood that such code will be portable.

The attribute rtspecialname indicates that the field name shall be treated in a special way by the runtime.

Rationale: There are
currently no field names that are required to be marked with rtspecialname. It is provided for extensions, future
standardization, and to increase consistency between the declaration of fields
and methods (instance and type initializer methods shall be marked with this
attribute).

The attribute specialname indicates that the field name has special meaning to tools other than the runtime,
typically because it marks a name that has meaning for the Common Language
Specification (CLS, see Partition I_alink=Partition_I).

15.2Field Init Metadata

The <fieldInit> metadata can be optionally added to a field
declaration. The use of this feature may not be combined with a data label.

The <fieldInit> information is stored in metadata and this
information can be queried from metadata. But the CLI does not use this
information to automatically initialize the corresponding fields. The field
initializer is typically used with literal fields
(see clause 15.1.2) or parameters with default values. See Section 21.9

The following table lists the options for a field initializer.
Note that while both the type and the field initializer are stored in metadata
there is no requirement that they match. (Any importing compiler is
responsible for coercing the stored value to the target field type). The
description column in the table below provides additional information.

<fieldInit> ::=

Description

bool ( true | false)

Boolean value, encoded as true or false

| bytearray ( <bytes> )

String of bytes, stored without conversion. May be be
padded with one zero byte to make the total byte-count an even number

| char ( <int32> )

16 bit unsigned integer (Unicode character)

| float32( <float64> )

32 bit floating point number, with the floating point
number specified in parentheses.

| float32( <int32> )

<int32> is binary representation of float

| float64( <float64> )

64 bit floating point number, with the floating point
number specified in parentheses.

| float64( <int64> )

<int64> is binary representation of double

| [ unsigned ] int8( <int8> )

8 bit integer with the integer specified in parentheses.

| [ unsigned ] int16(
<int16> )

16 bit integer with the integer specified in parentheses.

| [ unsigned ] int32(
<int32> )

32 bit integer with the integer specified in parentheses.

| [ unsigned ] int64(
<int64> )

64 bit integer with the integer specified in parentheses.

| <QSTRING>

String. <QSTRING> is stored as Unicode

| nullref

Null object reference

Implementation Specific (Microsoft)

ilasmdoes
not recognize the optional unsignedmodifier
before the int8, int16, int32 or int64 keywords

Example
(informative):

The
following example shows a typical use of this:

.fieldpublic static literal valuetype ErrorCodes
no_error = int8(0)

The
field named no_error is a literal of type ErrorCodes (a value type) for which no memory is
allocated. Tools and compilers can look up the value and detect that it is
intended to be an 8 bit signed integer whose value is 0.

15.3Embedding Data in a PE File

There are several ways to declare a data field that is stored in
a PE file. In all cases, the .data directive is used.

Data can be embedded in a PE file by using the .data directive at the top-level.

<decl> ::=

Section

.data<datadecl>

| …

6.6

Data may also be declared as part of a type:

<classMember> ::=

Section

.data<datadecl>

| …

9.2

Yet another alternative is to declare data inside a method:

<methodBodyItem> ::=

Section

.data<datadecl>

| …

14.4.1

15.3.1Data Declaration

A .data directive contains an optional
data label and the body which defines the actual data. A data label shall be
used if the data is to be accessed by the code.

<dataDecl> ::= [<dataLabel> =] <ddBody>

The body consists either of one data item or a list of data items
in braces. A list of data items is similar to an array.

<ddBody> ::=

<ddItem>

| { <ddItemList> }

A list of items consists of any number of items:

<ddItemList> ::= <ddItem> [, <ddItemList>]

The list may be used to declare multiple data items associated
with one label. The items will be laid out in the order declared. The first
data item is accessible directly through the label. To access the other items,
pointer arithmetic is used, adding the size of each data item to get to the
next one in the list. The use of pointer arithmetic will make the application
not verifiable. (Each data item shall have a <dataLabel> if it is to be
referenced afterwards; missing a <dataLabel> is useful in order to insert
alignment padding between data items)

A data item declares the type of the data and provides the data
in parentheses. If a list of data items contains items of the same type and
initial value, the grammar below can be used as a short cut for some of the
types: the number of times the item shall be replicated is put in brackets
after the declaration.

<ddItem> ::=

Description

&( <id> )

Address of label

| bytearray(
<bytes> )

Array of bytes

| char*( <QSTRING> )

Array of (Unicode) characters

| float32 [( <float64> )] [[
<int32> ]]

32-bit floating point number, may be replicated

| float64 [( <float64> )] [[
<int32> ]]

64-bit floating point number, may be replicated

| int8 [( <int8> )] [[
<int32> ]]

8-bit integer, may be replicated

| int16 [( <int16> )] [[
<int32> ]]

16-bit integer, may be replicated

| int32 [( <int32> )] [[
<int32> ]]

32-bit integer, may be replicated

| int64 [( <int64> )] [[
<int32> ]]

64-bit integer, may be replicated

Example
(informative):

The following
declares a 32 bit signed integer with value 123:

.data theInt =int32(123)

The following
declares 10 replications of an 8 bit unsigned integer with value 3:

.datatheBytes = int8 (3) [10]

15.3.2Accessing Data from the PE File

The data stored in a PE File using the .data
directive can be accessed through a static variable,
either global or a member of a type, declared at a particular position of the
data:

<fieldDecl> ::= <fieldAttr>* <type>
<id> at <dataLabel>

The data is then accessed by a program as it would access any
other static variable, using instructions such as ldsfld,
ldsflda, and so on (see Partition III_alink=Partition_III).

The ability to access data from within the PE File may be subject
to platform-specific rules, typically related to section access permissions
within the PE File format itself.

Example
(informative):

The following
accesses the data declared in the example of clause 15.3.1. First a static variable needs to be declared for the data, e.g. a global static variable:

.fieldpublic static int32 myInt at theInt

Then the static variable
can be used to load the data:

ldsfldint32
myInt

// data on stack

15.3.3Unmanaged
Thread-local Storage

Implementation Specific (Microsoft)

Each PE file has a
particular section whose initial contents are copied whenever a new thread is
created. This section is called unmanagedthread local storage. The
Microsoft implementation of ilasm allows the creation of this unmanaged
thread local storage by extending the data declaration to include an option
attribute, tls:

<dataDecl> ::=
[tls] [<dataLabel> =] <ddBody>

The CLI provides two mechanisms for dealing with thread-local storage (tls): an unmanaged mechanism and a managed mechanism. The unmanaged mechanism has a
number of restrictions which are carried forward directly from the underlying
platform into the CLI. For example, the amount of thread local storage is
determined when the PE file is loaded and cannot be expanded. The amount is
computed based on the static dependencies of the PE file, DLLs that are loaded
as a program executes cannot create their own thread local storage through this
mechanism. The managed mechanism, which does not have these restrictions, is
part of the Base Class Library.

For unmanaged tls there is a particular native code sequence that can be
used to locate the start of this section for the current thread. The CLI
respects this mechanism. That is, when a reference is made to a static variable
with a fixed RVA in the PE file and that RVA is in the thread-local section of
the PE, the native code generated from the CIL will use the thread-local access
sequence.

This has two
important consequences:

A static variable
with a specified RVA shall reside entirely in a single section of the PE file.
The RVA specifies where the data begins and the type of the variable specifies
how large the data area is.

When a new thread
is created it is only the data from the PE file that is used to initialize the
new copy of the variable. There is no opportunity to run the type initializer.
For this reason it is probably wise to restrict the use of unmanaged thread
local storage to the primitive numeric types and value types with explicit
layout that have a fixed initial value and no type initializer.

Many languages that support static data (i.e. variables that have
a lifetime that is the entire program) provide for a means to initialize that
data before the program begins running. There are three common mechanisms for
doing this, and each is supported in the CLI.

When the correct value to be stored into the static data is known
at the time the program is linked (or compiled for those languages with no
linker step), the actual value can be stored directly into the PE file,
typically into the data area (see Section 15.3). References to the variable are made directly to the location where this data has been placed in memory, using the OS supplied fix-up mechanism to adjust any references to this area if the file loads at an
address other than the one assumed by the linker.

In the CLI, this technique can be used directly if the static
variable has one of the primitive numeric types or is a value type with
explicit type layout and no embedded references to managed objects. In this
case the data is laid out in the data area as usual and the static variable is
assigned a particular RVA (i.e. offset from the start of the PE file) by using
a data label with the field declaration (using the at
syntax).

This mechanism, however, does not interact well with the CLI
notion of an application domain (see Partition I_alink=Partition_I).
An application domain is intended to isolate two applications running in the
same OS process from one another by guaranteeing that they have no shared data.
Since the PE file is shared across the entire process, any data accessed via
this mechanism is visible to all application domains in the process, thus
violating the application domain isolation boundary.

When the correct value is not known until the PE file is loaded
(for example, if it contains values computed based on the load addresses of
several PE files) it may be possible to supply arbitrary code to run as the PE
file is loaded, but this mechanism is platform-specific and may not be
available in all conforming implementations of the CLI.

Implementation Specific (Microsoft)

This mechanism,
while available in the CLI, is strongly discouraged. The code runs under the
process-wide loader lock, and the restrictions imposed by the underlying
operating system make this a fragile mechanism. The details are provided in clause 24.3.3.3.

When the correct value cannot be determined until type layout is
computed, the user shall supply code as part of a type initializer to
initialize the static data. The guarantees about type initialization are
covered in clause 9.5.3.1. As will be explained below, global statics are modeled in the CLI as though they belonged to a type, so the same guarantees apply to both global and type statics.

Because the layout of managed types need not occur until a type
is first referenced, it is not possible to statically initialize managed types
by simply laying the data out in the PE file. Instead, there is a type
initialization process that proceeds in the following steps:

1.All static variables are zeroed.

2.The user-supplied type initialization procedure, if any, is invoked as
described in clause 9.5.3.

Within a type initialization procedure there are several
techniques:

·Generate explicit code that stores constants into the
appropriate fields of the static variables. For small data structures this can
be efficient, but it requires that the initializer be converted to native code,
which may prove to be both a code space and an execution time problem.

·Box value types. When the static variable is simply a
boxed version of a primitive numeric type or a value type with explicit layout,
introduce an additional static variable with known RVA that holds the unboxed
instance and then simply use the box instruction to
create the boxed copy.

·Create a managed array from a static native array of data.
This can be done by marshaling the native array to a managed array. The
specific marshaler to be used depends on the native array. E.g., it may be a
safearray.

·Default initialize a managed array of a value type. The
Base Class Library provides a method that zeroes the storage for every element
of an array of unboxed value types (System.Runtime.CompilerServices.InitializeArray)

Implementation Specific (Microsoft)

Use Base Class Library deserialization. The Base Class Library provides
serialization and deserialization services. These services can be found in the System.Runtime.Serialization namespace. An object can be
converted to a serialized form, stored in the data section and accessed using a
static variable with known RVA of type unsigned int8[]. The
corresponding deserialization mechanism can then be used in the type
initializer.

End informative text

16Defining
Properties

A Property is declared by the using the .property directive. Properties may only be declared inside of types (ie global Properties
are not supported)

The property directive specifies a calling convention (see Section 14.3), type, name, and parameter in parentheses. specialname marks the Property as special to other
tools, while rtspecialname marks Property as special
to the CLI. The signature for the property (i.e., the <propHead> production)
shall match the signature of the property's .get
method (see below)

Rationale: There are
currently no property names that are required to be marked with rtspecialname. It is provided for extensions, future
standardization, and to increase consistency between the declaration of
properties and methods (instance and type initializer methods shall be marked
with this attribute).

While the CLI places no constraints on the methods that make up a
property, the CLS (see Partition I_alink=Partition_I)
specifies a set of consistency constraints..

A property may contain any number of methods in its body. The
following table shows these and provides short descriptions of each item:

.get specifies the getter for
this property. The <typeSpec> defaults to the current type. Only
one getter may be specified for a property. To be CLS compliant, the
definition of getter shall be marked specialname.

.set specifies the setter for
this property. The <typeSpec> defaults to the current type. Only
one setter may be specified for a property. To be CLS compliant, the
definition of setter shall be marked specialname.

.other is used to specify any other
methods that this property comprises.

In addition, custom attributes (see Chapter 20) or source line declarations may be specified.

Example
(informative):

This example shows
the declaration of the property used in the example in Part 5.

The event head may contain the keywords specialname
or rtspecialname. specialname
marks the name of the property for other tools, while rtspecialname
marks the name of the event as special for the runtime.

Rationale: There are
currently no event names that are required to be marked with rtspecialname. It is provided for extensions, future
standardization, and to increase consistency between the declaration of events
and methods (instance and type initializer methods shall be marked with this
attribute).

The .addon directive specifies the add
method , and the <typeSpec> defaults to the same type as the event. The
CLS specifies naming conventions and consistency constraints for events, and
requires that the definition of the add method be marked with specialname.

The .removeon directive specifies the remove
method , and the <typeSpec> defaults to the same type as the event. The
CLS specifies naming conventions and consistency constraints for events, and
requires that the definition of the remove method be marked with specialname.

The .fire directive specifies the fire
method , and the <typeSpec> defaults to the same type as the event. The
CLS specifies naming conventions and consistency constraints for events, and
requires that the definition of the fire method be marked with specialname.

An event may contain any number of other methods specified with
the .other directive. From the point of view of the
CLI, these methods are only associated with each other through the event. If
they have special semantics, this needs to be documented by the implementer.

Events may also have custom attributes (Chapter 20) associated with them and they may declare source line information.

Example
(informative):

This shows the
declaration of an event, its corresponding delegate, and typical
implementations of the add, remove, and fire method of the event. The event and
the methods are declared in a class called Counter.

18Exception Handling

In the CLI, a method may define a range of CIL instructions that
are said to be protected. This is called the try block. It can then
associate one or more handlers with that try block. If an exception
occurs during execution anywhere within the try block, an exception object is
created that describes the problem. The CIL then takes over, transferring
control from the point at which the exception was thrown, to the block of code
that is willing to handle that exception. See Partition I_alink=Partition_I.

<sehBlock> ::=

<tryBlock> <sehClause>
[<sehClause>*]

The next few sections expand upon this simple description, by
describing the five kinds of code block that take part in exception processing:
try, catch, filter, finally, and fault. (note that there are restrictions upon how many,
and what kinds of <sehClause> a given <tryBlock> may have; see Partition I_alink=Partition_I.
for details.

The remaining syntax items are described in detail below; they
are collected here for reference.

<tryBlock> ::=

Description

.try<label> to
<label>

Protect region from first label to prior to second

| .try <scopeBlock>

<scopeBlock> is protected

<sehClause> ::=

Description

catch <typeReference>
<handlerBlock>

Catch all objects of the specified type

| fault <handlerBlock>

Handle all exceptions but not normal exit

| filter <label> <handlerBlock>

Enter handler only if filter succeeds

| finally <handlerBlock>

Handle all exceptions and normal exit

<handlerBlock> ::=

Description

handler <label> to <label>

Handler range is from first label to prior to second

| <scopeBlock>

<scopeBlock> is the handler block

18.1Protected Blocks

A try, or protected, or guarded, block is declared with the .try directive.

<tryBlock> ::=

Descriptions

.try <label> to
<label>

Protect region from first label to prior to second.

| .try <scopeBlock>

<scopeBlock> is protected

In the first, the protected block is delimited by two labels.
The first label is the first instruction to be protected, while the second
label is the instruction just beyond the last one to be protected. Both labels
shall be defined prior to this point.

The second uses a scope block (see clause 14.4.4) after the .try directive – the instructions within that scope are
the ones to be protected.

In the first syntax, the labels enclose the instructions of the
handler block, the first label being the first instruction of the handler while
the second is the instruction immediately after the handler. Alternatively, the
handler block is just a scope block.

Implementation Specific (Microsoft)

ilasm requires
labels used to specify any exceptions blocks to be defined beforehand in
the source. ilasm supports the following additional syntax for use in
round-tripping:

<handlerBlock> ::= handler <int32> to <int32>

18.3Catch

A catch block is declared using the catch keyword. This specifies the type of exception object the clause is designed to
handle, and the handler code itself.

The filter code begins at the specified label and ends at the
first instruction of the handler block. (Note that the CLI demands that the
filter block shall immediately precede, within the CIL stream, its
corresponding handler block)

A fault block is declared using the fault keyword. This
specifies the handler code, with this grammar:

<sehClause> ::= …

| fault <handlerBlock>

The last possible CIL instruction that can be executed in a
fault handler shall be endfault.

Example
(informative):

.method public static void m() {

startTry:

... //
protected instructions

leave exitSEH //
shall use leave

endTry:

startFault:

... //
fault handler instructions

endfault

endFault:

.try startTry to endTry fault handler
startFault to endFault

exitSEH: //
back to normal

}

19Declarative
Security

Many languages that target the CLI use attribute syntax to attach
declarative security attributes to items in the metadata. This information is
actually converted by the compiler into an XML-based representation that is
stored in the metadata, see Section 21.11. By contrast, ilasm requires the conversion information to be represented in its input.

<securityDecl> ::=

.permissionset<secAction>
= ( <bytes> )

| .permission
<secAction> <typeReference> ( <nameValPairs> )

In .permission, <typeReference> specifies the permission class and <nameValPairs> specifies the
settings. See Section 21.11

In .permissionset the bytes specify the serialized version of the security settings:

<secAction> ::=

Description

assert

Assert permission so that callers do not need it.

| demand

Demand permission of all callers.

| deny

Deny permission so checks will fail.

| inheritcheck

Demand permission of a subclass.

| linkcheck

Demand permission of caller.

| permitonly

Reduce permissions so check will fail.

| reqopt

Request optional additional permissions.

| reqrefuse

Refuse to be granted these permissions.

| request

Hint that permission may be required.

Implementation Specific (Microsoft)

The following
security action is Microsoft-specific. A conforming implementation of the CLI
may ignore this security action if present in an assembly

Implementation
Specific (Microsoft)

<secAction> ::=

Description

| prejitgrant

Persisted denied set at prejit time.

<nameValPairs> ::= <nameValPair> [, <nameValPair>]*

<nameValPair> ::= <SQSTRING> =
<SQSTRING>

20Custom
Attributes

Custom attributes add user-defined annotations to the metadata.
Custom attributes allow an instance of a type to be stored with any element of
the metadata. This mechanism can be used to store application specific
information at compile time and access it either at runtime or when another
tool reads the metadata. While any user-defined type can be used as an
attribute, CLS compliance requires that attributes will be instances of types
whose parent is System.Attribute. The CLI predefines some attribute types and uses them to control runtime
behavior. Some languages predefine attribute types to represent language
features not directly represented in the CTS. Users or other tools are welcome
to define and use additional attribute types.

Custom attributes are declared using the directive .custom. Followed by this directive is the method
declaration for a type constructor, optionally followed by a <bytes> in
parentheses:

<customDecl> ::=

<ctor> [ =( <bytes> )
]

The <ctor> item represents a method declaration (see Section 14.4), specific for the case where the method's name is .ctor.

Custom attributes can be attached to any item in metadata,
except a custom attribute itself. Commonly, custom attributes are attached to
assemblies, modules, classes, interfaces, value types, methods, fields,
properties and events (the custom attribute is attached to the immediately
preceding declaration)

The <bytes> item is not required if the constructor takes
no arguments. In these cases, all that matters is the presence of the custom
attribute.

If the constructor takes parameters, their values shall be
specified in the <bytes> item. The format for this ‘blob’ is defined in Section 22.3.

Example
(informative):

The following
example shows a class that is marked with the System.SerializableAttribute
and a method that is marked with the System.Runtime.Remoting.OneWayAttribute.
The keyword serializable corresponds to the System.SerializableAttribute.

There are two kinds of Custom Attributes, called (genuine) Custom
Attributes, and Pseudo Custom Attributes. Custom Attributes and Pseudo Custom
Attributes are treated differently, at the time they are defined, as follows:

·A Custom Attribute is stored directly into the metadata;
the‘blob’ which holds its defining data is stored as-is. That ‘blob’ can be
retrieved later.

·A Pseudo Custom Attribute is recognized because its name is one
of a short list. Rather than store its ‘blob’ directly in metadata, that
‘blob’ is parsed, and the information it contains is used to set bits and/or
fields within metadata tables. The ‘blob’ is then discarded; it cannot be
retrieved later.

Pseudo Custom Attributes therefore serve to capture user
directives, using the same familiar syntax the compiler provides for regular
Custom Attributes, but these user directives are then stored into the more
space-efficient form of metadata tables. Tables are also faster to check at
runtime than (genuine) Custom Attributes.

Many Custom Attributes are invented by higher layers of software.
They are stored and returned by the CLI, without its knowing or caring what
they ‘mean’. But all Pseudo Custom Attributes, plus a collection of regular
Custom Attributes, are of special interest to compilers and to the CLI. An
example of such Custom Attributes is System.Reflection.DefaultMemberAttribute.
This is stored in metadata as a regular Custom Attribute ‘blob’, but reflection
uses this Custom Attribute when called to invoke the default member (property)
for a type.

The following subsections list all of the Pseudo Custom
Attributes and distinguished Custom Attributes, where distinguished
means that the CLI and/or compilers pay direct attention to them, and their
behavior is affected in some way.

In order to prevent name collisions into the future, all custom
attributes in the System
namespace are reserved for standardization.

20.2.1Pseudo Custom Attributes

The following table lists the CLI Pseudo Custom Attributes. They
are defined in either the System
or the System.Reflection
namespaces.

Attribute

Description

AssemblyAlgorithmIDAttribute

Records the ID of the hash algorithm used (reserved only)

AssemblyFlagsAttribute

Records the flags for this assembly (reserved only)

DllImportAttribute

Provides information about code implemented within an
unmanaged library

FieldOffsetAttribute

Specifies the byte offset of fields within their enclosing
class or value type

InAttribute

Indicates that a method parameter is an [in] argument

MarshalAsAttribute

Specifies how a data item should be marshalled between
managed and unmanaged code -- see Section 22.4.

MethodImplAttribute

Specifies details of how a method is implemented

OutAttribute

Indicates that a method parameter is an [out] argument

StructLayoutAttribute

Allows the caller to control how the fields of a class or
value type are laid out in managed memory

Not all of these Pseudo Custom Attributes are specified in this
standard, but all of them are reserved and shall not be used for other
purposes. For details on these attributes, see the documentation for the
corresponding class in Partition IV_alink=Partition_IV.

The Pseudo Custom Attributes above affect bits and fields in
metadata, as follows:

AssemblyAlgorithmIDAttribute: sets the Assembly.HashAlgId field

AssemblyFlagsAttribute: sets the Assembly.Flags field

DllImportAttribute
: sets the Method.Flags.PinvokeImpl bit for the attributed method; also,
adds a new row into the ImplMap table (setting MappingFlags, MemberForwarded,
ImportName and ImportScope columns)

FieldOffsetAttribute
: sets the FieldLayout.OffSet value for the attributed field

InAttribute
: sets the Param.Flags.In bit for the attributed parameter

MarshalAsAttribute
: sets the Field.Flags.HasFieldMarshal bit for the attributed field (or
the Param.Flags.HasFieldMarshal bit for the attributed parameter); also
enters a new row into the FieldMarshal table for both Parent and NativeType
columns.

MethodImplAttribute
: sets the Method.ImplFlags field of the attributed method

OutAttribute
: sets the Param.Flags.Out bit for the attributed parameter

StructLayoutAttribute
: sets the TypeDef.Flags.LayoutMask sub-field for the attributed type.
And, optionally, the TypeDef.Flags.StringFormatMask sub-field, the ClassLayout.PackingSiz
,and ClassLayout.ClassSize fields for that type.

Implementation Specific (Microsoft)

Use of the
following Pseudo Custom Attributes renders the assembly that contains them
non-portable; a conforming implementation of the CLI may reject such an
assembly when it is loaded, or throw an exception at runtime if any attempt is
made to access the metadata items set by those Custom Attributes.

Implementation
Specific (Microsoft)

Attribute

Description

ComImportAttribute

Provides information about native code reached as a COM
component

OptionalAttribute

Marks a method parameter as optional

NonSerializedAttribute

Indicates that a field should not be serialized

PreserveSigAttribute

Specifies HRESULT or retval signature transformation

SerializableAttribute

Indicates that a type can be serialized

Implementation Specific (Microsoft)

The Pseudo Custom
Attributes above affect bits and fields in metadata, as follows:

ComImportAttribute
: sets the TypeDef.Flags.Import bit for the attributed type

OptionalAttribute
: sets the Param.Flags.Optional bit for the attributed parameter

NonSerializedAttribute
: sets the Field.Flags.NotSerialized bit for the attributed field

PreserveSigAttribute
: sets the Method.ImplFlags.PreserveSig bit of the attributed method

SerializableAttribute
: sets the TypeDef.Flags.Serializable bit for the attributed type

20.2.2Custom
Attributes Defined by the CLS

The CLS specifies certain Custom Attributes and requires that
conformant languages support them. These attributes are located under System.

Attribute

Description

AttributeUsageAttribute

Used to specify how an attribute is intended to be used.

ObsoleteAttribute

Indicates that an element is not to be used.

CLSCompliantAttribute

Indicates whether or not an element is declared to be CLS
compliant through an instance field on the attribute object.

The following Custom
Attributes that control the runtime behavior of a CIL-to-native-code compiler
and a runtime debugger are defined in the System.Diagnostics namespace. Their
use renders the assembly that contains them non-portable; a conforming
implementation of the CLI may reject such an assembly when it is loaded, or
throw an exception at runtime if any attempt is made to access those Custom
Attributes.

Implementation
Specific (Microsoft)

Attribute

Description

DebuggableAttribute

Controls a CIL-to-native-code compiler to produce code
that is easier to debug

DebuggerHiddenAttribute

Specifies a debugger should step over the attributed
method or property

DebuggerStepThroughAttribute

Specifies a debugger should step through the attributed
method or property (it may step into a method called by this one)

20.2.4Custom
Attributes for Remoting

Implementation Specific (Microsoft)

The following Custom
Attributes are used to control the behavior of remoting. They are defined in
the System.Runtime.Remoting namespace. Their use renders the assembly that
contains them non-portable; a conforming implementation of the CLI may reject
such an assembly when it is loaded, or throw an exception at runtime if any
attempt is made to access those custom attributes.

The following Custom Attributes are defined in the System.Security.Permissions.
namespace. Note that these are all base classes; the actual instances of
security attributes found in assemblies will be sub-classes of these.

Attribute

Description

CodeAccessSecurityAttribute

This is the base attribute
class for declarative security using custom attributes.

Note that any other security-related Custom Attributes (ie, any
Custom Attributes that derive from System.Security.Permissions.SecurityAttribute)
included into an assembly, may cause a conforming implementaion of the CLI to
reject such an assembly when it is loaded, or throw an exception at runtime if
any attempt is made to access those security-related Custom Attributes. (This
statement in fact holds true for any Custom Attributes that cannot be resolved;
security-related Custom Attributes are just one particular case)

Implementation Specific (Microsoft)

The following
security-related Custom Attributes are defined in the
System.Security.Permissions namespace. Their use renders the assembly that
contains them non-portable; a conforming implementation of the CLI may reject
such an assembly when it is loaded, or throw an exception at runtime if any
attempt is made to access those Custom Attributes.

The following Pseudo Custom Attributes are used by the al tool to transfer information
between modules and assemblies (they are temporarily attached to a TypeRef to a
class called AssemblyAttributesGoHere)
then merged by al and attached to the assembly. These attributes are
defined in the System.Runtime.CompilerServices
namespace. Their use renders the assembly that contains them non-portable; a
conforming implementation of the CLI may reject such an assembly when it is
loaded, or throw an exception at runtime if any attempt is made to access those
Pseudo Custom Attributes.

Implementation
Specific (Microsoft)

Attribute

Description

AssemblyCultureAttribute

Specifies which culture an assembly supports

AssemblyVersionAttribute

String holding version of assembly (in the format major.minor.build.revision)

Implementation Specific (Microsoft)

The Pseudo Custom Attributes above affect bits and
fields in metadata, as follows:

The following Custom
Attributes are used to control the interoperation with COM 1.x and classical
COM. These attributes are located under System.Runtime.InteropServices. More
information can also be found in the Partition IV_alink=Partition_IV.
Their use renders the assembly that contains them non-portable; a conforming
implementation of the CLI may reject such an assembly when it is loaded, or
throw an exception at runtime if any attempt is made to access those Custom
Attributes.

Implementation
Specific (Microsoft)

Attribute

Description

ClassInterfaceAttribute

Specifies how the class is exported to COM (as
DispInterface, as a Dual Interface, or not at all)

ComAliasNameAttribute

Applied to a parameter or field
to indicate the COM alias for the parameter or field type.

ComConversionLossAttribute

Indicates that information was lost about a class or
interface when it was imported from a type library to an assembly

ComEmulateAttribute

Used on a type to indicate that
it is an emulator type for a different type.

ComRegisterFunctionAttribute

Used on a method to indicate
that the method should be called when the assembly is registered for use from
COM.

ComSourceInterfacesAttribute

Identifies the list of interfaces
that are sources of events for the type.

ComUnregisterFunctionAttribute

Used on a method to indicate
that the method should be called when the assembly is unregistered for use
from COM.

ComVisibleAttribute

Can be applied to an individual
type or to an entire assembly to control COM visibility.

DispIdAttribute

Custom attribute to specify the
COM DISPID of a Method or Field.

GuidAttribute

Used to supply the GUID of a
type, interface or an entire type library.

HasDefaultInterfaceAttribute

Used to specify that a class has
a COM default interface.

IdispatchImplAttribute

Indicates which IDispatch implementation the CLI
uses when exposing dual interfaces and dispinterfaces to COM

ImportedFromTypeLibAttribute

Custom attribute to specify that
a module is imported from a COM type library.

InterfaceTypeAttribute

Indicates whether a managed interface is dual, IDispatch
or IUnknown when exposed to COM

NoComRegistrationAttribute

Used to indicate that an
otherwise public, COM-creatable type should not be registered for use form
COM applications.

NoIDispatchAttribute

This attribute is used to
control how the class responds to queries for an IDispatch Interface.

ProgIdAttribute

Custom attribute that allows the
user to specify the prog ID of a class.

TypeLibFuncAttribute

Contains the FUNCFLAGS that were
originally imported for this function from the COM type library.

TypeLibTypeAttribute

Contains the TYPEFLAGS that were
originally imported for this type from the COM type library.

TypeLibVarAttribute

Contains the VARFLAGS that were
originally imported for this variable from the COM type library.

Used to mark methods as callable, based on some
compile-time condition. If the condition is false, the method will not be
called

DecimalConstantAttribute

Stores the value of a decimal constant in metadata

DefaultMemberAttribute

Defines the member of a type
that is the default member used by reflection’s InvokeMember.

FlagsAttribute

Custom attribute indicating an enumeration should be
treated as a bitfield; that is, a set of flags

IndexerNameAttribute

Indicates the name by which an indexer will be known in
programming languages that do not support indexers directly

ParamArrayAttribute

Indicates that the method will allow a variable number of
arguments in its invocation

21Metadata
Logical Format: Tables

This section defines the structures that describe metadata, and
how they are cross-indexed. This corresponds to how metadata is laid out,
after being read into memory from a PE file. (For a description of metadata
layout inside the PE file itself, see Chapter 23)

Metadata is stored in two kinds of structure – tables (arrays of
records), and heaps. There are four heaps in any module: String, Blob,
Userstring and Guid. The first three are byte arrays (so valid indexes into
these heaps might be 0, 23, 25, 39, etc). The Guid heap is an array of GUIDs,
each 16 bytes wide. Its first element is numbered 1, its second 2, and so on.

Each entry in each column of each table is either a constant or
an index.

Constants are either literal values (eg ALG_SID_SHA1 = 4, stored
in the HashAlgId column of the Assembly table), or, more
commonly, bitmasks. Most bitmasks (they are almost all called “Flags”)
are 2 bytes wide (eg the Flags column in the Field table), but
there are a few that are 4 bytes (eg the Flags column in the TypeDef
table)

Each index is either 2 bytes wide, or 4 bytes wide. The index
points into another (or the same) table, or into one of the four heaps. The
size of each index column in a table is only made 4 bytes if it needs to be,
for that particular module. So, if a particular column indexes a table, or
tables, whose highest row number fits in a 2-byte value, the indexer column
need only be 2 bytes wide. Conversely, for huge tables, containing 64K rows or
more, an indexer of that table will be 4 bytes wide.

Note that indexes begin at 1, meaning the first row in any given
metadata table. An index value of zero denotes that it does not index a row at
all (it behaves like a null reference)

The columns that index a metadata table are of two sorts:

·Simple – that column indexes one, and only one, table. e.g., the
FieldList column in the TypeDef table always indexes the Field
table. So all values in that column are simple integers, giving the row number
in the target table

·Coded – that column indexes any of several tables. e.g., the Extends
column in the TypeDef table can index into the TypeDef table, or
into the TypeRef table. A few bits of that index value are reserved to
define which table it targets. For the most part, this specification talks of
index values after being decoded into row numbers within the target table.
However, the specification includes a description of these coded indexes in the
section that describes the physical layout of Metadata (Chapter 23).

Metadata preserves name strings, as created by a compiler or code
generator, unchanged. Essentially it treats each string as an opaque 'blob'.
In particular, it preserves case. The CLI imposes no limit on the size of
names stored in metadata and subsequently processed by the CLI

Implementation Specific (Microsoft)

For first release,
strings are limited in length. Depending on its purpose, a string can be no
larger than MAX_CLASS_NAME
(defined as 1024) or MAX_PATH_NAME
(defined as 260). These values refer to the
maximum number of bytes that the string, after being converted into UTF8
format, may occupy; that includes a terminating null character. It is intended
that this limitation be removed in a future release. Within this document, the
above restrictions are abbreviated to the phrase: “… is limited to MAX_CLASS_NAME”
or “… is limited to MAX_PATH_NAME”

Matching AssemblyRefs and ModuleRefs to their corresponding
Assembly and Module shall be performed case-blind (see Partition I_alink=Partition_I).
However, all other name matches (type, field, method, property, event) is exact
– so that this level of resolution is the same across all platforms, whether
their OS is case-sensitive or not.

Tables are given both a name (eg "Assembly") and
numbered (eg 0x20). The number for each table is listed immediately with its
title in the following sections.

A few of the tables represent extensions to regular CLI files.
Specifically, ENCLog and ENCMap, which occur in temporary images, generated
during "Edit and Continue" or "incremental compilation"
scenarios, whilst debugging. Both table types are reserved for future use.

References to the methods or fields of a Type are stored together
in a metadata table called the MemberRef table. However, sometimes, for
clearer explanation, this specification distinguishes between these two kinds
of reference, calling them “MethodRef” and “FieldRef”.

The sections that follow describe the schema for each kind of
metadata table, and explain the detailed rules that guarantee metadata emitted
into any PE file is valid. Checking that metadata is valid ensures that later
processing - checking the CIL instruction stream for type safety, building
method tables, CIL-to-native-code compilation, data marshalling, etc will not
cause the CLI to crash or behave in an insecure fashion.

In addition, some of the rules are used to check compliance with
the CLS requirements (see Partition I_alink=Partition_I)
even though these are not related to valid Metadata. These are marked with a
trailing [CLS] tag.

The rules for valid metadata refer to an individual module. A
module is any collection of metadata that could typically be saved to a
disk file. This includes the output of compilers and linkers, or the output of
script compilers (where often the metadata is held only in memory, but never
actually saved to a file on disk).

The rules address intra-module validation only. So, validator
software, for example, that checks conformance with this spec, need not resolve
references or walk type hierarchies defined in other modules. However, it
should be clear that even if two modules, A and B, analyzed separately, contain
only valid metadata, they may still be in error when viewed together (e.g., a
call from Module A, to a method defined in module B, might specify a callsite
signature that does not match the signatures defined for that method in B)

All checks are categorized as ERROR, WARNING or CLS.

·An ERROR reports something that might cause a CLI to crash or
hang, it might run but produce wrong answers; or it might be entirely benign.
There may exist conforming implementations of the CLI that will not accept
metadata that violates an ERROR rule, and therefore such metadata is invalid
and is not portable.

·A WARNING reports something, not actually wrong, but possibly a
slip on the part of the compiler. Normally, it indicates a case where a
compiler could have encoded the same information in a more compact fashion or
where the metadata represents a construct that can have no actual use at
runtime. All conforming implementations will support metadata that violate
only WARNING rules; hence such metadata is both valid and portable.

·A CLS reports lack of compliance with common language
specification (see Partition I_alink=Partition_I).
Such metadata is both valid and portable, but there may exist programming
languages that cannot process it, even though all conforming implementations of
the CLI support the constructs.

Validation rules fall into a few broad categories, as follows:

·Number of Rows A few tables are allowed only one row
(e.g. Module table). Most have no such restriction.

·Unique Rows No table may contain duplicate rows, where
“duplicate” is defined in terms of its key column, or combination of
columns

·Valid Indexes Columns which are indexes shall point
somewhere sensible, as follows:

oEvery index into the String, Blob or Userstring heaps shall point into
that heap, neither before its start (offset 0), nor after its end

oEvery index into the Guid heap shall lie between 1 and the maximum
element number in this module, inclusive

oEvery index (row number) into another metadata table shall lie between 0
and that table’s row count + 1 (for some tables, the index may point just past
the end of any target table, meaning it indexes nothing)

·Valid Bitmasks Columns which are bitmasks shall only have
valid permutations of bits set

·Valid RVAs There are restrictions upon fields and methods
that are assigned RVAs (Relative Virtual Addresses; these are byte offsets,
expressed from the address at which the corresponding PE file is loaded into
memory)

Note that some of the rules listed below say "nothing"
- for example, some rules state that a particular table is allowed zero or
more rows - so there is no way that the check can fail. This is done simply
for completeness, to record that such details have indeed been addressed,
rather than overlooked.

End informative text

The CLI imposes no limit on the size of names stored in metadata,
and subsequently processed by a CLI implementation.

The Assembly table is defined using the .assembly directive (see Section 6.2); its columns are obtained from the
respective .hashalgorithm,
.ver, .publickey, and .culture (see clause 6.2.1 For an example
see Section 6.2.

This contains informative text only

1.The Assembly table may contain zero or one row [ERROR]

2.HashAlgId should be one of the specified values [ERROR]

Implementation Specific (Microsoft)

The Microsoft
implementation treats this as a WARNING rather than an error, using numbers
based on the Crypto APIs. This means that the Microsoft implementation can
handle additional algorithms based on the constants of type ALG_CLASS_HASH
in WinCrypt.h as well as those dynamically discovered at runtime.

3.Flags may have only those values set that are specified [ERROR]

4.PublicKey may be null or non-null

5.Name shall index a non-null string in the String heap [ERROR]

6.The string indexed by Name can be of unlimited length

7.Culture may be null or non-null

8.If Culture is non-null, it shall index a single string from the
list specified (see clause 22.1.3) [ERROR]

Note: Name
is a simple name (e.g., “Foo” - no drive letter, no path, no file extension);
on POSIX-compliant systems Name contains no colon, no forward-slash, no
backslash, no period.

·PublicKeyOrToken (index into Blob heap – the public key or
token that identifies the author of this Assembly)

·Name (index into String heap)

·Culture (index into String heap)

·HashValue (index into Blob heap)

The table is defined by the .assembly extern directive
(see Section 6.3).
Its columns are filled using directives similar to those of the Assembly
table except for the PublicKeyOrToken column which is defined using the .publickeytoken directive. For an example see Section 6.3.

This contains informative text only

1.MajorVersion, MinorVersion, BuildNumber, RevisionNumber can each have
any value

2.Flags may have only one possible bit set – the PublicKey
bit (see clause 22.1.2). All other bits shall be zero. [ERROR]

3.PublicKeyOrToken my be null, or non-null (note that the Flags.PublicKey bit specifies whether the 'blob' is a full
public key, or the short hashed token)

9.The AssemblyRef table shall contain no duplicates, where
duplicate rows have the same MajorVersion, MinorVersion, BuildNumber,
RevisionNumber, PublicKeyOrToken, Name and Culture [WARNING]

Note: Name
is a simple name (e.g., “Foo” - no drive letter, no path, no file extension);
on POSIX-compliant systems Name contains no colon, no forward-slash, no
backslash, no period.End informative text

The ClassLayout table is used to define how the fields of
a class or value type shall be laid out by the CLI (normally, the CLI is free
to reorder and/or insert gaps between the fields defined for a class or value
type).

Rationale: This feature is
used to make a managed value type be laid out in exactly the same way as an
unmanaged C struct – with this condition true, the managed value type can be
handed to unmanaged code, which accesses the fields exactly as if that block of
memory had been laid out by unmanaged code.

The information held in the ClassLayout table depends upon
the Flags value for {AutoLayout, SequentialLayout, ExplicitLayout}
in the owner class or value type.

A type has layout if it is marked SequentialLayout
or ExplicitLayout. If any type within an inheritance chain has layout,
then so shall all its parents, up to the one that descends immediately from System.Object,
or from System.ValueType.

This contains informative text only

Layout cannot begin part way down the chain. But it is legal
to stop “having layout” at any point down the chain.

For example, in the diagrams below, Class A derives from System.Object;
class B derives from A; class C derives from B. System.Object has no layout. But A, B
and C are all defined with layout, and that is legal.

Similarly with Classes E, F and G. G has no layout. This too is
legal. The following picture shows two illegal setups:

On the left, the “chain with layout” does not start at the
‘highest’ class. And on the right, there is a ‘hole’ in the “chain with
layout”

Layout information for a class or value type is held in two
tables – the ClassLayout and FieldLayout tables, as shown in this
diagram:

This example shows how row 3 of the ClassLayout table
points to row 2 in the TypeDef table (the definition for a Class, called
“MyClass”). Rows 4 through 6 of the FieldLayout table point to
corresponding rows in the Field table. This illustrates how the CLI
stores the explicit offsets for the three fields that are defined in “MyClass”
(there is always one row in the FieldLayout table for each field in the
owning class or value type) So, the ClassLayout table acts as an extension
to those rows of the TypeDef table that have layout info; since many
classes do not have layout info, this design overall saves space

End informative text

The ClassLayout table has the following columns:

·PackingSize (a 2 byte constant)

·ClassSize (a 4 byte constant)

·Parent (index into TypeDef table)

The rows of the ClassLayout table are defined by placing .pack and .size directives on
the body of a parent type declaration (see Section 9.2). For
an example see Section 9.7.

This contains informative text only

1.A ClassLayout table may contain zero or more or rows

2.Parent shall index a valid row in the TypeDef table,
corresponding to a Class or ValueType (not to an Interface) [ERROR]

3.The Class or ValueType indexed by Parent shall not be AutoLayout
- i.e., it shall be one of SequentialLayout or ExplicitLayout.
(See clause 22.1.14). Put another way, AutoLayout types shall not own any rows in the ClassLayout table. [ERROR]

4.If Parent indexes a SequentialLayout type, then: [ERROR]

oPackingSize shall be one of {0, 1, 2, 4, 8, 16, 32, 64, 128} (0
means use the default pack size for the platform that the application is
running on)

oif ClassSize is non-zero, then it shall be greater than or equal
to the calculated size of the class, based upon its field sizes and PackingSize
(compilers request padding at the end of a class by providing a value for ClassSize
that is larger than its calculated size) [ERROR]

oa ClassSize of zero does not mean the class has zero size. It
means, no size was specified at definition time. Instead, the actual size is
calculated from the field types, taking account of packing size (default or
specified) and natural alignment on the target, runtime platform

oif Parent indexes a ValueType, then ClassSize shall be
less than 1 MByte (0x100000 bytes)

Implementation Specific (Microsoft)

Current
implementation of desktop CLI allows 0x3F0000 bytes, but may be reduced in
future

5.Note that ExplicitLayout types might result in verifiable
types, so long as that layout does not create union types.

6.If Parent indexes an ExplicitLayout type, then [ERROR]

oif ClassSize is non-zero, then it shall be greater than or equal
to the calculated size of the class, based upon the rows it owns in the FieldLayout
table (compilers create padding at the end of a class by providing a value
for ClassSize that is larger than its calculated size)

oa ClassSize of zero does not mean the class has zero size. It
means, no size was specified at definition time. Instead, the actual size is
calculated from the field types, their specified offsets, and any beyond-end alignment packing performed by the target platform

oif Parent indexes a ValueType, then ClassSize shall be
less than 1 MByte (0x100000 bytes)

Implementation Specific (Microsoft)

Current
implementation allows 0x3F0000 bytes, but may be reduced in future

oPackingSize shall be 0 (because it makes no sense to provide
explicit offsets for each field, as well as a packing size)

7.Layout along the length of an inheritance chain shall follow the rules
specified above (starts at ‘highest’ Type, with no ‘holes’, etc) [ERROR]

End informative text

21.9Constant : 0x0B

The Constant table is used to store compile-time, constant
values for fields, parameters and properties.

·Parent (index into the Param or Field or Property
table; more precisely, a HasConst coded index)

·Value (index into Blob heap)

Note that Constant information does not directly influence
runtime behavior. Compilers inspect this information, at compile time, when
importing metadata; but the value of the constant itself, if used, becomes
embedded into the CIL stream the compiler emits. There are no CIL instructions
to access the Constant table at runtime.

A row in the Constant table for a parent is created
whenever a compile-time value is specified for that parent, for an example see Section 15.2.

Note: the last option, ELEMENT_TYPE_CLASS
with a Value of zero, is used to denote a null object reference, and
corresponds to the nullref value for <fieldInit> in ilasm
(see Section 15.2) . Unlike uses of ELEMENT_TYPE_CLASS
in signatures, this one is notfollowed by a type token, because
null can apply legally to any and all object types.

3.Parent shall index a valid row in the Field or Property
or Param table [ERROR]

4.There shall be no duplicate rows, based upon Parent [ERROR]

5.Constant.Type must match exactly the declared type of the Param,
Field or Property identified by Parent (in the case where
the parent is an enum, it must match exactly the underlying type of that enum)
[CLS]

End informative text

21.10CustomAttribute : 0x0C

The CustomAttribute table has the following columns:

·Parent (index into any metadata table, except the CustomAttribute
tableitself; more precisely, a HasCustomAttribute coded
index)

·Type (index into the Method or MethodRef table;
more precisely, a CustomAttributeType coded index)

·Value (index into Blob heap)

The CustomAttribute table stores data that can be used to
instantiate a Custom Attribute (more precisely, an object of the specified
Custom Attribute class) at runtime. The column called Type is slightly
misleading – it actually indexes a constructor method – the owner of that
constructor method is the Type of the Custom Attribute.

A row in the CustomAttribute table for a parent is created
by the .custom attribute, which gives the value of
the Type column and optionally that of the Value column (see Chapter 20)

This contains informative text only

All binary values
are stored in little-endian format (except PackedLen items - used only
as counts for the number of bytes to follow in a UTF8 string)

1.It is legal for there to be no CustomAttribute present at all -
that is, for the CustomAttribute.Value field to be null

2.Parent can be an index into any metadata table, except
the CustomAttribute table itself [ERROR]

3.Type shall index a valid row in the Method or MethodRef
table. That row shall be a constructor method (for the class of which this
information forms an instance) [ERROR]

4.Value may be null or non-null

5.If Value is non-null, it shall index a 'blob' in the Blob heap
[ERROR]

6.The following rules apply to the overall structure of the Value 'blob'(see
Section 22.3):

oProlog shall be 0x0001 [ERROR]

oThere shall be as many occurrences of FixedArg as are declared in
the Constructor method [ERROR]

oNumNamed may be zero or more

oThere shall be exactly NumNamed occurrences of NamedArg
[ERROR]

oEach NamedArg shall be accessible by the caller [ERROR]

oIf NumNamed = 0 then there shall be no further items in the CustomAttrib
[ERROR]

7.The following rules apply to the structure of FixedArg (see Section 22.3):

oIf this item is not for a vector (a single-dimension array with lower
bound of 0), then there shall be exactly one Elem [ERROR]

oIf this item is for a vector, then:

oNumElem shall be 1 or more [ERROR]

oThis shall be followed by NumElem occurrences of Elem
[ERROR]

8.The following rules apply to the structure of Elem (see Section 22.3):

oIf this is a simple type or an enum (see Section 22.3 for how this is defined), then Elem consists simply of its value [ERROR]

oIf this is a string, or a Type, then Elem consists of a SerString
–PackedLen count of bytes, followed by the UTF8 characters[ERROR]

9.The following rules apply to the structure of NamedArg (see Section 22.3):

oThe single byte FIELD
(0x53) or PROPERTY
(0x54) [ERROR]

oThe type of the field or property -- one of ELEMENT_TYPE_BOOLEAN, ELEMENT_TYPE_CHAR,
ELEMENT_TYPE_I1, ELEMENT_TYPE_U1, ELEMENT_TYPE_I2, ELEMENT_TYPE_U2,
ELEMENT_TYPE_I4, ELEMENT_TYPE_U4, ELEMENT_TYPE_I8, ELEMENT_TYPE_U8,
ELEMENT_TYPE_R4, ELEMENT_TYPE_R8, ELEMENT_TYPE_STRING or the
constant 0x50 (for an argument of type System.Type)

oThe name of the Field or Property, respectively with the previous item,
as a SerString –PackedLen count of bytes, followed by the UTF8
characters of the name [ERROR]

oA FixedArg (see above) [ERROR]

End informative text

21.11DeclSecurity : 0x0E

Security attributes, which derive from System.Security.Permissions.SecurityAttribute(see Partition IV_alink=Partition_IV),
can be attached to a TypeDef, a Method or to an Assembly.
All constructors of this class shall take a System.Security.Permissions.SecurityAction
value as their first parameter, describing what should be done with the
permission on the type, method or assembly to which it is attached. Code
access security attributes, which derive from System.Security.Permissions.CodeAccessSecurityAttribute,
may have any of the security actions.

These different security actions are encoded in the DeclSecurity
table as a 2-byte enum (see below). All security custom attributes for a given
security action on a method, type or assembly shall be gathered together and
one System.Security.PermissionSet
instance shall be created, stored in the Blob heap, and referenced from the DeclSecurity
table.

Note:The general flow from a compiler’s point
of view is as follows. The user specifies a custom attribute through some
language-specific syntax that encodes a call to the attribute’s constructor. If
the attribute’s type is derived (directly or indirectly) from System.Security.Permissions.SecurityAttribute
then it is a security custom attribute and requires special treatment, as
follows (other custom attributes are handled by simply recording the
constructor in the metadata as described in Section 21.10). The attribute object is
constructed, and provides a method (CreatePermission) to convert it into
a security permission object (an object derived from System.Security.Permission). All the
permission objects attached to a given metadata item with the same security
action are combined together into a System.Security.PermissionSet. This
permission set is converted into a form that is ready to be stored in XML using
its ToXML
method to create a System.Security.SecurityElement.
Finally, the XML that is required for the metadata is created using the ToString
method on the security element.

The DeclSecurity table has the following columns:

·Action (2 byte value)

·Parent (index into the TypeDef, Method or Assembly
table; more precisely, a HasDeclSecurity coded index)

·PermissionSet (index into Blob heap)

Action is a 2-byte representation of Security Actions, see
System.Security.SecurityAction
in Partition IV_alink=Partition_IV.
The values 0 through 0xFF are reserved for future standards use. Values 0x20
through 0x7F and 0x100 through 0x07FF are for uses where the action may be
ignored if it is not understood or supported. Values 0x80 through 0xFF and
0x0800 through 0xFFFF are for uses where the action shall be implemented for
secure operation; in implementations where the action is not available no
access to the assembly, type, or method shall be permitted.

Note 2: Attribute shall derive from System.Security.Permissions.SecurityAttribute,
but shall not derive from System.Security.Permissions.CodeAccessSecurityAttribute

Parent is a Meta Data token that identifies the Method,
Type or Assembly on which security custom attributes serialized
in PermissionSet was defined.

PermissionSet is a 'blob' that contains the XML
serialization of a permission set. The permission set contains the permissions
that were requested with an Action on a specific Method, Type
or Assembly (see Parent).

The rows of the DeclSecurity table are filled by attaching
a .permission or .permissionset
directive that specifies the Action and PermissionSet on a parent
assembly (see Section 6.6)
or parent type or method (see Section 9.2).

This contains informative text only

1.Action may have only those values set that are specified
[ERROR]

2.Parent shall be one of TypeDef, MethodDef,
orAssembly. That is, it shall index a valid row in the TypeDef
table, the MethodDef table, or the Assembly table [ERROR]

3.If Parent indexes a row in the TypeDef table, that row
should not define an Interface. The security system ignores any such parent;
compilers should not emit such permissions sets [WARNING]

4.If Parent indexes a TypeDef, then its TypeDef.Flags.HasSecurity
bit should be set [ERROR]

5.If Parent indexes a MethodDef, then its MethodDef.Flags.HasSecurity
bit should be set [ERROR]

6.PermissionSet should index a 'blob' in the Blob heap [ERROR]

7.The format of the 'blob' indexed by PermissionSet should
represent a valid, serialized CLI object graph. The serialized form of all
standardized permissions is specified in Partition IV_alink=Partition_IV.
[ERROR]

·EventList (index into Event table). It marks the
first of a contiguous run of Events owned by this Type. The run continues to
the smaller of:

othe last row of the Event table

othe next run of Events, found by inspecting the EventList of the
next row in the EventMap table

Note that EventMap info does not directly influence
runtime behavior; what counts is the info stored for each method that the event
comprises.

This contains informative text only

1.EventMap table may contain zero or more rows

2.There shall be no duplicate rows, based upon Parent (a given
class has only one ‘pointer’ to the start of its event list) [ERROR]

3.There shall be no duplicate rows, based upon EventList (different
classes cannot share rows in the Event table)[ERROR]

End informative text

21.13Event : 0x14

Events are treated within metadata much like Properties – a way
to associate a collection of methods defined on given class. There are two
required methods – add_ and remove_, plus optional raise_
and others. All of the methods gathered together as an Event shall be
defined on the class.

The association between a row in the TypeDef table and the
collection of methods that make up a given Event, is held in three separate
tables (exactly analogous to that used for Properties) – see the below:

Row 3 of the EventMap table indexes row 2 of the TypeDef
table on the left (MyClass), whilst indexing row 4 of the Event
table on the right – the row for an Event called DocChanged. This setup
establishes that MyClass has an Event called DocChanged. But
what methods in the Method table are gathered together as ‘belonging’ to
event DocChanged? That association is contained in the MethodSemantics
table – its row 2 indexes event DocChanged to the right, and row 2 in
the Method table to the left (a method called add_DocChanged). Also,
row 3 of the MethodSemantics table indexes DocChanged to the
right, and row 3 in the Method table to the left (a method called remove_DocChanged).
As the shading suggests, MyClass has another event, called TimedOut,
with two methods, add_TimedOut and remove_TimedOut.

Event tables do a little more than group together existing rows
from other tables. The Event table has columns for EventFlags, Name
(eg DocChanged and TimedOut in the example here) and EventType.
In addition, the MethodSemantics table has a column to record
whether the method it points at is an add_, a remove_, a raise_,
or other.

The ExportedType table holds a row for each type, defined
within other modules of this Assembly, that is exported out of this
Assembly. In essence, it stores TypeDef row numbers of all types that
are marked public in other modules that this Assembly comprises.

The actual target row in a TypeDef table is given by the
combination of TypeDefId (in effect, row number) and Implementation (in
effect, the module that holds the target TypeDef table). Note that this
is the only occurrence in metadata of foreign tokens – that is token
values that have a meaning in another module. (Regular token values
are indexes into table in the current module)

The full name of the type need not be stored directly. Instead,
it may be split into two parts at any included “.” (although typically this
done at the last “.” in the full name). The part preceding the “.” is stored
as the TypeNamespace and that following the “.” is stored as the TypeName.
If there is no “.” in the full name, then the TypeNamespace shall be the
index of the empty string.

·TypeDefId (4 byte index into a TypeDef table of
another module in this Assembly). This field is used as a hint only. If the
entry in the target TypeDef table matches the TypeName and TypeNamespace
entries in this table, resolution has succeeded. But if there is a
mismatch, the CLI shall fall back to a search of the target TypeDef
table

·TypeName (index into the String heap)

·TypeNamespace (index into the String heap)

·Implementation. This can be an index (more precisely, an Implementation
coded index) into one of 2 tables, as follows:

oFile table, where that entry says which module in the current
assembly holds the TypeDef

oExportedType table, where that entry is the enclosing Type of the
current nested Type

The rows in the ExportedType table are the result of the .class extern directive (see Section 6.7).

This contains informative text only

The term “FullName” refers to the string created as
follows: if the TypeNamespace is null, then use the TypeName,
otherwise use the concatenation of Typenamespace, “.”, and TypeName.

1.The ExportedType table may contain zero or more rows

2.There shall be no entries in the ExportedType table for Types
that are defined in the current module - just for Types defined in other
modules within the Assembly [ERROR]

6.If non-null, TypeDefId should index a valid row in a TypeDef table
in a module somewhere within this Assembly (but not this module), and
the row so indexed should have its Flags.Public = 1 (see see clause 22.1.14) [WARNING]

7.TypeName shall index a non-null string in the String heap
[ERROR]

Implementation Specific
(Microsoft)

This string is limited to MAX_CLASS_NAME

8.TypeNamespace may be null, or non-null

9.If TypeNamespace is non-null, then it shall index a non-null
string in the String heap [ERROR]

Implementation Specific
(Microsoft)

This string is limited to MAX_CLASS_NAME. Also, the FullName (concatenated
TypeNamespace + "." + TypeName)shall be less than MAX_CLASS_NAME

10.FullName shall be a valid CLS identifier [CLS]

11.If this is a nested Type, then TypeNamespace should be null, and TypeName
should represent the unmangled, simple name of the nested Type [ERROR]

12.Implementation shall be a valid index into either: [ERROR]

·the File table; that file shall hold a definition of the
target Type in its TypeDef table

·a different row in the current ExportedType table -
this identifies the enclosing Type of the current, nested Type

13.FullName shall match exactly the corresponding FullName for
the row in the TypeDef table indexed by TypeDefId [ERROR]

14.Ignoring nested Types, there shall be no duplicate rows, based upon FullName
[ERROR]

15.For nested Types, there shall be no duplicate rows, based upon TypeName
and enclosing Type [ERROR]

16.The complete list of Types exported from the current Assembly is given
as the catenation of the ExportedType table with all public Types in the
current TypeDef table, where “public” means a Flags.tdVisibilityMask of
either Public or NestedPublic. There shall be no duplicate rows,
in this concatenated table, based upon FullName (add Enclosing Type into
the duplicates check if this is a nested Type)[ERROR]

End informative text

21.15Field : 0x04

Conceptually, each row in the Field table is owned by one,
and only one, row in the TypeDef table. However, the owner of any row in
the Field table is not stored anywhere in the Field table
itself. There is merely a ‘forward-pointer’ from each row in the TypeDef
table (the FieldList column), as shown in the following illustration.

The TypeDef table has rows 1 through 4. The first row in
the TypeDef table corresponds to a pseudo type, inserted automatically
by the CLI. It is used to denote those rows in the Field table
corresponding to global variables. The Field table has rows 1 through
6. Type 1 (pseudo type for ‘module’) owns rows 1 and 2 in the Field
table. Type 2 owns no rows in the Field table, even though its FieldList
indexes row 3 in the Field table. Type 3 owns rows 3 through 5 in
the Field table. Type 4 owns row 6 in the Field table. (The next
pointers in the diagram show the next free row in each table) So, in the Field
table, rows 1 and 2 belong to Type 1 (global variables); rows 3 through 5
belong to Type 3; row 6 belongs to Type 4.

Each row in the Field table results from a toplevel .field directive (see Section 5.10), or a .field directive inside a Type (see Section 9.2). For an example see Section 13.5.

This contains informative text only

1.Field table may contain zero or more rows

2.Each row shall have one, and only one, owner row in the TypeDef
table [ERROR]

16.If the owner of this field is the internally-generated type called <Module>, it denotes that
this field is defined at module scope (commonly called a global variable). In
this case:

oFlags.Static shall be 1 [ERROR]

oFlags.MemberAccessMask subfield shall be one of Public, Compilercontrolled,
or Private(see clause 22.1.5) [ERROR]

omodule-scope fields are not allowed[CLS]

17.There shall be no duplicate rows in the Field table, based upon
owner+Name+Signature (where owner is the owning row in the TypeDef table,
as described above) (Note however that if Flags.Compilercontrolled = 1,
then this row is completely excluded from duplicate checking) [ERROR]

18.There shall be no duplicate rows in the Field table, based upon
owner+Name, where Name fields are compared using CLS
conflicting-identifier-rules. So, for example,"int i" and "float i"
would be considered CLS duplicates. (Note however that if Flags.Compilercontrolled
= 1, then this row is completely excluded from duplicate checking, as noted
above) [CLS]

21.16FieldLayout : 0x10

Note that each Field in any Type is defined by its Signature.
When a Type instance (ie, an object) is laid out by the CLI, each Field is one
of three kinds:

·Scalar – for any member of built-in, such as int32. The
size of the field is given by the size of that intrinsic, which varies between
1 and 8 bytes

·ObjectRef – for CLASS, STRING, OBJECT, ARRAY, SZARRAY

·Pointer – for PTR, FNPTR

·ValueType – for VALUETYPE. The instance of that ValueType is actually
laid out in this object, so the size of the field is the size of that ValueType

(This lists above use an abbreviation – each all-caps name should
be prefixed by ELEMENT_TYPE_
so, for example, STRING
is actually ELEMENT_TYPE_STRING.
See clause 22.1.15)

Note that metadata specifying explicit structure layout may be
valid for use on one platform but not another, since some of the rules
specified here are dependent on platform-specific alignment rules.

A row in the FieldLayout table is created if the .field directive for the parent field has specified a
field offset (see Section 9.7).

This contains informative text only

1.A FieldLayout table may contain zero or more or rows

2.The Type whose Fields are described by each row of the FieldLayout table
shall have Flags.ExplicitLayout (see clause 22.1.14) set [ERROR]

3.Offset shall be zero or more (cannot be negative) [ERROR]

4.Field shall index a valid row in the Field table [ERROR]

5.The row in the Field table indexed by Field shall be
non-static (ie its Flags.Static shall be 0) [ERROR]

6.Among the rows owned by a given Type there shall be no duplicates, based
upon Field. That is, a given Field of a Type cannot be given two
offsets.[ERROR]

7.Each Field of kind ObjectRef shall be naturally aligned within
the Type [ERROR]

8.No Field of kind ObjectRef may overlap any other Field no matter
what its kind, wholly or partially [ERROR]

9.Among the rows owned by a given Type it is perfectly legal for several
rows to have the same value of Offset, so long as they are notof type ObjectRef(used to define C unions, for example)
[ERROR]

10.If ClassSize in the owner ClassLayout row is non-zero,
then no Field may extend beyond that ClassSize (ie, the Field Offset
value plus the Field’s calculated size shall not exceed ClassSize)
(note that it is legal, and common, for ClassSize to be supplied as larger
than the calculated object size - the CLI pads the object with trailing bytes
up to the ClassSize value) [ERROR]

11.Every Field of an ExplicitLayout Type shall be given an offset - that
is, it shall have a row in the FieldLayout table [ERROR]

Implementation Specific
(Microsoft)

Note that the rules above specify what is legal, or non-legal,
metadata. However, there is a finer distinction that can be drawn - what
layouts permit type-safe access by code? For example, a class that overlaps
two ValueTypes constitutes legal metadata, but accesses to that class may
result in code that is not provably typesafe. At runtime, it is the Class
loader that will perform these type-safety checks. Version 1 takes a simple
approach - if the type has any explicit layout, it is not typesafe. [This may
be refined in future versions.]

End informative text

21.17FieldMarshal : 0x0D

The FieldMarshal table has two columns. It ‘links’ an
existing row in the Field or Param table, to information in the
Blob heap that defines how that field or parameter (which, as usual, covers the
method return, as parameter number 0) should be marshalled when calling to or
from unmanaged code via PInvoke dispatch.

Note that FieldMarshal information is used only by code
paths that arbitrate operation with unmanaged code. In order to execute such
paths, the caller, on most platforms, would be installed with elevated security
permission. Once it invokes unmanaged code, it lies outside the regime that
the CLI can check - it is simply trusted not to violate the type system.

The FieldMarshal table has the following columns:

·Parent (index into Field or Param table;
more precisely, a HasFieldMarshal coded index)

A row in the FieldMarshal table is created if the .field directive for the parent field has specified a .marshall attribute (see Section 15.1).

This contains informative text only

1.A FieldMarshal table may contain zero or more rows

2.Parent shall index a valid row in the Field or Param table (Parent
values are encoded to say which of these two tables each refers to)
[ERROR]

3.NativeType shall index a non-null 'blob' in the Blob heap
[ERROR]

4.No two rows can point to the same parent. In other words, after the Parent
values have been decoded to determine whether they refer to the Field or
the Param table, no two rows can point to the same row in the Field table or in
the Param table [ERROR]

a.NativeIntrinsic shall be exactly one of the constant values in
its production [ERROR]

b.If NativeIntrinsic has the value BYVALSTR, then Parent shall point
to a row in the Field table, not the Param table [ERROR]

c.If FIXEDARRAY,
then Parent shall point to a row in the Field table, not the Param
table [ERROR]

d.If FIXEDARRAY,
then NumElem shall be 1 or more [ERROR]

e.If FIXEDARRAY,
then ArrayElemType shall be exactly one of the constant values in its
production [ERROR]

f.If ARRAY,
then ArrayElemType shall be exactly one of the constant values in its
production [ERROR]

g.If ARRAY,
then ParamNum may be zero

h.If ARRAY,
then ParamNum cannot be < 0 [ERROR]

i.If ARRAY,
and ParamNum > 0, then Parent shall point to a row in the
Param table, not in the Field table [ERROR]

j.If ARRAY,
and ParamNum > 0, then ParamNum cannot exceed the number of
parameters supplied to the MethodDef (or MethodRef if a VARARG call) of
which the parent Param is a member [ERROR]

k.If ARRAY,
then ElemMult shall be >= 1 [ERROR]

l.If ARRAY
and ElemMult <> 1 issue a warning, because it is probably a
mistake [WARNING]

m.If ARRAY
and ParamNum == 0, then NumElem shall be >= 1 [ERROR]

n.If ARRAY
and ParamNum != 0 and NumElem != 0 then issue a warning, because it is
probably a mistake [WARNING]

Implementation Specific
(Microsoft)

The following rules apply to Microsoft-specific features:

a. If CUSTOMMARSHALLER,
then Guid shall be an in-place, counted-UTF8 string, that represents a string
format GUID. Its length, when expanded from UTF8, shall be exactly 38
characters, to include lead { and trailing } [ERROR]

b. If CUSTOMMARSHALLER,
then UnmanagedType shall be a non-empty, counted-UTF8 string [ERROR]

c. If CUSTOMMARSHALLER,
then ManagedType shall be a non-empty, counted-UTF8 string, that represents the
fully-qualified namespace+"."+name of a Class or ValueType defined
somewhere within the current Assembly [ERROR]

d. If CUSTOMMARSHALLER,
then the Cookie shall be a counted-UTF8 string - its size may legally be zero
[ERROR]

e. If SAFEARRAY,
then SafeArrayElemType shall be exactly one of the constant values in its
production [ERROR]

End informative text

21.18FieldRVA : 0x1D

The FieldRVA table has the following columns:

·RVA (a 4 byte constant)

·Field (index into Field table)

Conceptually, each row in the FieldRVA table is an
extension to exactly one row in the Field table, and records the RVA
(Relative Virtual Address) within the image file at which this field’s initial
valueis stored.

A row in the FieldRVA table is created for each static
parent field that has specified the optional data
label (see Chapter 15). The RVA column is the relative virtual address of the data in the PE file (see Section 15.3).

This contains informative text only

1.RVA shall be non-zero [ERROR]

2.RVA shall point into the current module’s data area (not its
metadata area) [ERROR]

3.Field shall index a valid table in the Field table
[ERROR]

4.Any field with an RVA shall be a ValueType (not a Class, and not an
Interface). Moreover, it shall not have any private fields (and likewise for
any of its fields that are themselves ValueTypes). (If any of these conditions
were breached, code could overlay that global static and access its private
fields.) Moreover, no fields of that ValueType can be Object References (into
the GC heap) [ERROR]

5.So long as two RVA-based fields comply with the previous conditions, the
ranges of memory spanned by the two ValueTypes may overlap, with no further
constraints. This is not actually an additional rule; it simply clarifies the
position with regard to overlapped RVA-based fields

The CLI also checks dynamically against opening a device, which
can be assigned an arbitrary name by the user

3.HashValue shall index a non-empty 'blob' in the Blob heap
[ERROR]

4.There shall be no duplicate rows - rows with the same Name value
[ERROR]

5.If this module contains a row in the Assembly table (that is, if
this module “holds the manifest”) then there shall not be any row in the File
table for this module - i.e., no self-reference [ERROR]

6.If the File table is empty, then this, by definition, is a
single-file assembly. In this case, the ExportedType table should be
empty [WARNING]

End informative text

21.20ImplMap : 0x1C

The ImplMap table holds information about unmanaged
methods that can be reached from managed code, using PInvoke dispatch.

Each row of the ImplMap table associates a row in the Method
table (MemberForwarded) with the name of a routine (ImportName)
in some unmanaged DLL (ImportScope).

Note: A
typical example would be: associate the managed Method stored in row N of the Method
table (so MemberForwarded would have the value N) with the routine
called “GetEnvironmentVariable” (the string indexed by ImportName) in
the DLL called “kernel32” (the string in the ModuleRef table indexed by ImportScope).
The CLI intercepts calls to managed Method number N, and instead forwards them
as calls to the unmanged routine called “GetEnvironmentVariable” in
“kernel32.dll” (including marshalling any arguments, as required)

The CLI
does not support this mechanism to access fields that are exported from
a DLL -- only methods.

·MemberForwarded (index into the Field or Method
table; more precisely, a MemberForwarded coded index. However, it only
ever indexes the Method table, since Field export is not
supported.

·ImportName (index into the String heap)

·ImportScope (index into the ModuleRef table)

A row is entered in the ImplMap table for each parent
Method (see Section 14.5) that is defined with a .pinvokeimpl interoperation attribute specifying the MappingFlags,
ImportName and ImportScope. For an example see Section 14.5.

This contains informative text only

1.ImplMap may contain zero or more rows

2.MappingFlags may have only those values set that are specified
[ERROR]

3.MemberForwarded shall index a valid row in the Method table
[ERROR]

4.The MappingFlags.CharSetMask (see clause 22.1.7) in the row of the Method table indexed by MemberForwarded shall have at most one of the following bits set: CharSetAnsi, CharSetUnicode,
or CharSetAuto}
(if none set, the default is CharSetNotSpec) [ERROR]

Implementation Specific
(Microsoft)

The MappingFlags.CallConvMask in the row of the Method
table indexed by MemberForwarded may have at most one of the following
values: CallConvWinapi,
CallConvCdecl,
CallConvStdcall.
It cannot have the value CallConvFastcallor CallConvThiscall [ERROR]

5.ImportName shall index a non-null string in the String heap
[ERROR]

Implementation Specific
(Microsoft)

This string is limited to MAX_CLASS_NAME

6.ImportScope shall index a valid row in the ModuleRef
table [ERROR]

7.The row indexed in the Method table by MemberForwarded shall have
its Flags.PinvokeImpl = 1, and Flags.Static = 1 [ERROR]

·Implementation (index into File table, or AssemblyRef
table, or null; more precisely, an Implementation coded index)

The Offset specifies the byte offset within the referenced
file at which this resource record begins. The Implementation specifies
which file holds this resource. The rows in the table result from .mresource directives on the Assembly (see clause 6.2.2).

This contains informative text only

1.The ManifestResource table may contain zero or more rows

2.Offset shall be a valid offset into the target file, starting
from the Resource entry in the COR header [ERROR]

3.Flags may have only those values set that are specified [ERROR]

4.The VisibilityMask (see clause 22.1.8) subfield of Flags shall be one of Public orPrivate [ERROR]

5.Name shall index a non-null string in the String heap [ERROR]

Implementation Specific
(Microsoft)

This string is limited to MAX_CLASS_NAME

6.Implementation may be null or non-null (if null, it means the
resource is stored in the current file)

7.If Implementation is null, then Offset shall be a valid
offset in the current file, starting from the Resource entry in the CLI header
[ERROR]

8.If Implementation is non-null, then it shall index a valid row in
the File or AssemblyRef table [ERROR]

9.There shall be no duplicate rows, based upon Name [ERROR]

10.If the resource is an index into the File table, Offset
shall be zero [ERROR]

End informative text

21.23MemberRef : 0x0A

The MemberRef table combines two sorts of references – to
Fields and to Methods of a class, known as ‘MethodRef’ and ‘FieldRef’,
respectively. The MemberRef table has the following columns:

An entry is
made into the MemberRef table whenever a reference is made, in the CIL
code, to a method or field which is defined in another module or assembly.
(Also, an entry is made for a call to a method with a VARARG signature, even when it is defined
in the same module as the callsite)

This contains informative text only

1.Class shall be one of ... [ERROR]

a.a TypeRef token, if the class that defines the member is defined
in another module. (Note: it is unusual, but legal, to use a TypeRef
token when the member is defined in this same module - its TypeDef token
can be used instead)

b.a ModuleRef token, if the member is defined, in another module of
the same assembly, as a global function or variable

c.a MethodDef token, when used to supply a call-site signature for
a varargs method that is defined in this module. The Name shall match
the Name in the corresponding MethodDef row. The Signature
shall match the Signature in the target method definition [ERROR]

d.a TypeSpec token, if the member is a member of a constructed type

2.Class shall not be null (this would indicate an unresolved
reference to a global function or variable) [ERROR]

3.Name shall index a non-null string in the String heap [ERROR]

Implementation Specific
(Microsoft)

This string is limited to MAX_CLASS_NAME

4.The Name string shall be a valid CLS identifier [CLS]

5.Signature shall index a valid field or method signature in the
Blob heap. In particular, it shall embed exactly one of the following ‘calling
conventions’: [ERROR]