Skip to content
← Back

src/ir/emitter/srm_assembly_emitter.ghul

1
namespace IR.Emitter is
2
use System.Reflection.Metadata.BlobBuilder
3
use System.Reflection.Metadata.EntityHandle
4
use System.Reflection.Metadata.BlobHandle
5
use System.Reflection.Metadata.StringHandle
6
use System.Reflection.Metadata.UserStringHandle
7
use System.Reflection.Metadata.AssemblyReferenceHandle
8
use System.Reflection.Metadata.TypeDefinitionHandle
9
use System.Reflection.Metadata.TypeReferenceHandle
10
use System.Reflection.Metadata.MemberReferenceHandle
11
use System.Reflection.Metadata.InterfaceImplementationHandle
12
use System.Reflection.Metadata.MethodDefinitionHandle
13
use System.Reflection.Metadata.MethodAttributes
14
use System.Reflection.Metadata.MethodImplAttributes
15
use System.Reflection.Metadata.Ecma335.MetadataBuilder
16
use System.Reflection.Metadata.Ecma335.MetadataRootBuilder
17
use System.Reflection.Metadata.Ecma335.MetadataTokens
18
use System.Reflection.Metadata.Ecma335.MethodBodyStreamEncoder
19
use System.Reflection.Metadata.Ecma335.InstructionEncoder
20
use System.Reflection.Metadata.Ecma335.TableIndex
21
22
use System.Reflection.PortableExecutable.ManagedPEBuilder
23
use System.Reflection.PortableExecutable.PEHeaderBuilder
24
use System.Reflection.PortableExecutable.CorFlags
25
26
// Owns the in-flight assembly: the metadata builder, the method body
27
// stream, and the reference/member handle caches. Deduplication is
28
// this layer's job — a metadata table holds one row per distinct
29
// reference, with no merging of repeats after the fact.
30
//
31
// Emission is split across two objects: this one, and
32
// SRM_METHOD_BODY_EMITTER for one method body's instructions. The
33
// structure of the assembly comes from SRM_STRUCTURE_WALK, which
34
// reads it from the symbol table rather than accumulating it here.
35
class SRM_ASSEMBLY_EMITTER is
36
_metadata: MetadataBuilder
37
_il_builder: BlobBuilder
38
_body_stream: MethodBodyStreamEncoder
39
40
_assembly_refs: Collections.MAP[string, AssemblyReferenceHandle]
41
_type_refs: Collections.MAP[Semantic.Symbols.Symbol, TypeReferenceHandle]
42
_named_type_refs: Collections.MAP[string, TypeReferenceHandle]
43
44
45
_entry_point: MethodDefinitionHandle?
46
_want_library: bool
47
48
// Resources the build declared, embedded into the manifest
49
// resource table. Collected here and written into one blob at
50
// `write_to` time, so adding one never needs the offsets of
51
// resources added earlier.
52
_resources: Collections.LIST[(name: string, bytes: ubyte[])]
53
54
// The portable PDB format version the debug directory
55
// advertises: 1.0, as every portable PDB emitted to date is.
56
_PORTABLE_PDB_VERSION: ushort static => 0x100us
57
58
// Non-null only when the build asked for debug information.
59
_pdb: SRM_PDB_BUILDER?
60
61
// The module version id, left empty until the content that
62
// determines it has been written. Reserving the blob rather
63
// than filling it now is what lets the id be a hash of the
64
// assembly: the hash is taken over these bytes as zeros, and
65
// the result is written back into them afterwards.
66
_module_version_id: System.Reflection.Metadata.Blob field
67
68
// What an assembly-level attribute hangs off.
69
assembly_handle: EntityHandle
70
71
// `version` is the colon-separated four-part form ("0:0:0:0")
72
// module_version is already built in, so parsing here avoids a
73
// second version representation at the call site.
74
init(module_name: string, assembly_name: string, version: string, want_library: bool) is
75
let parsed_version = parse_version(version)
76
77
_metadata = MetadataBuilder(0, 0, 0, 0)
78
_il_builder = BlobBuilder(256)
79
_body_stream = MethodBodyStreamEncoder(_il_builder)
80
81
_assembly_refs = Collections.MAP[string, AssemblyReferenceHandle]()
82
_type_refs = Collections.MAP[Semantic.Symbols.Symbol, TypeReferenceHandle]()
83
_named_type_refs = Collections.MAP[string, TypeReferenceHandle]()
84
_named_member_refs = Collections.MAP[(owner: int, name: string, signature: string), MemberReferenceHandle]()
85
_method_specs = Collections.MAP[(method: int, signature: string), System.Reflection.Metadata.MethodSpecificationHandle]()
86
_type_specs = Collections.MAP[string, System.Reflection.Metadata.TypeSpecificationHandle]()
87
_resources = Collections.LIST[(name: string, bytes: ubyte[])]()
88
handles = SRM_HANDLES()
89
90
_want_library = want_library
91
92
_sequence_points = Collections.MAP[int, Collections.List[SRM_SEQUENCE_POINT]]()
93
_module_references = Collections.MAP[string, System.Reflection.Metadata.ModuleReferenceHandle]()
94
95
if IoC.CONTAINER.instance.build_flags.want_debug then
96
_pdb = SRM_PDB_BUILDER()
97
fi
98
99
// TypeDef row 1 must be <Module>, before any user type.
100
_metadata.add_type_definition(
101
_,
102
_,
103
_metadata.get_or_add_string("<Module>"),
104
_,
105
MetadataTokens.field_definition_handle(1),
106
MetadataTokens.method_definition_handle(1))
107
108
let reserved_module_version_id = _metadata.reserve_guid()
109
110
_module_version_id = reserved_module_version_id.content
111
112
_metadata.add_module(
113
0,
114
_metadata.get_or_add_string(module_name),
115
reserved_module_version_id.handle,
116
_,
117
_)
118
119
assembly_handle =
120
cast EntityHandle(
121
_metadata.add_assembly(
122
_metadata.get_or_add_string(assembly_name),
123
parsed_version,
124
_,
125
_,
126
_,
127
_))
128
si
129
130
parse_version(value: string) -> System.Version static is
131
let parts = value.split([':'])
132
133
return System.Version(
134
int.parse(parts[0]),
135
int.parse(parts[1]),
136
int.parse(parts[2]),
137
int.parse(parts[3]))
138
si
139
140
get_or_add_string(value: string) -> StringHandle =>
141
_metadata.get_or_add_string(value)
142
143
get_or_add_user_string(value: string) -> UserStringHandle =>
144
_metadata.get_or_add_user_string(value)
145
146
get_or_add_blob(bytes: ubyte[]) -> BlobHandle =>
147
_metadata.get_or_add_blob(bytes)
148
149
// Declares a managed resource embedded into this assembly,
150
// reachable at runtime through `Assembly.GetManifestResourceStream(logical_name)`.
151
add_resource(logical_name: string, bytes: ubyte[]) is
152
_resources.add((name = logical_name, bytes = bytes))
153
si
154
155
next_field_row: int => _metadata.get_row_count(TableIndex.FIELD) + 1
156
next_method_row: int => _metadata.get_row_count(TableIndex.METHOD_DEF) + 1
157
next_type_row: int => _metadata.get_row_count(TableIndex.TYPE_DEF) + 1
158
next_parameter_row: int => _metadata.get_row_count(TableIndex.PARAM) + 1
159
next_property_row: int => _metadata.get_row_count(TableIndex.PROPERTY) + 1
160
161
// The function the entrypoint pragma named during the tree
162
// walk, so the structure walk can mark its row. Which function
163
// is the entry point is not a naming rule the emitter can
164
// reapply: it can be a global function or a static method, and
165
// `@entry` overrides the name entirely.
166
entry_point_function: Semantic.Symbols.Function? public
167
168
// Every row this assembly has promised a symbol. Held here so
169
// that anything with the emitter can resolve a definition
170
// without being handed a second object.
171
handles: SRM_HANDLES
172
173
// Where the version each reference records comes from. Set before
174
// anything is emitted; without it every reference takes the
175
// registry's default.
176
referenced_assemblies: Semantic.DotNet.REFERENCED_ASSEMBLIES? public
177
178
// A reference to an assembly something being emitted refers to,
179
// written the first time it is asked for, at the version of the
180
// assembly that was loaded under that name.
181
add_assembly_reference(name: string) -> AssemblyReferenceHandle =>
182
add_assembly_reference(
183
name,
184
referenced_assemblies?.version_of(name) ?? Semantic.DotNet.REFERENCED_ASSEMBLIES.DEFAULT_VERSION,
185
referenced_assemblies?.public_key_token_of(name))
186
187
add_assembly_reference(name: string, version: string) -> AssemblyReferenceHandle =>
188
add_assembly_reference(name, version, null)
189
190
add_assembly_reference(
191
name: string, version: string, public_key_token: ubyte[]?
192
) -> AssemblyReferenceHandle is
193
if _assembly_refs.contains_key(name) then
194
return _assembly_refs[name]
195
fi
196
197
let handle = _metadata.add_assembly_reference(
198
_metadata.get_or_add_string(name),
199
parse_version(version),
200
_,
201
if let token = public_key_token then _metadata.get_or_add_blob(token) else _[BlobHandle] fi,
202
_,
203
_)
204
205
_assembly_refs[name] = handle
206
207
return handle
208
si
209
210
add_type_reference(
211
symbol: Semantic.Symbols.Symbol,
212
assembly_name: string,
213
namespace_name: string,
214
simple_name: string
215
) -> TypeReferenceHandle is
216
if _type_refs.contains_key(symbol) then
217
return _type_refs[symbol]
218
fi
219
220
assert _assembly_refs.contains_key(assembly_name) else
221
"assembly reference '{assembly_name}' must be registered before a type reference into it"
222
223
let assembly_ref = _assembly_refs[assembly_name]
224
225
let handle = _metadata.add_type_reference(
226
cast EntityHandle(assembly_ref),
227
_metadata.get_or_add_string(namespace_name),
228
_metadata.get_or_add_string(simple_name))
229
230
_type_refs[symbol] = handle
231
232
return handle
233
si
234
235
// A type nested inside another. Its scope is the enclosing
236
// type's own reference rather than the assembly, and it carries
237
// no namespace of its own — the enclosing reference supplies it.
238
//
239
// Encoded any other way the row names a type that does not
240
// exist: a top-level name containing a separator resolves to
241
// nothing, and the failure surfaces at load as
242
// `Could not load type 'List`1/Enumerator'`.
243
add_nested_type_reference(
244
assembly_name: string,
245
enclosing: TypeReferenceHandle,
246
simple_name: string
247
) -> TypeReferenceHandle is
248
let key = "{assembly_name}|{MetadataTokens.get_token(cast EntityHandle(enclosing))}|{simple_name}"
249
250
if _named_type_refs.contains_key(key) then
251
return _named_type_refs[key]
252
fi
253
254
let handle = _metadata.add_type_reference(
255
cast EntityHandle(enclosing),
256
_,
257
_metadata.get_or_add_string(simple_name))
258
259
_named_type_refs[key] = handle
260
261
return handle
262
si
263
264
// Reference to a type that no symbol names — the framework
265
// types every assembly needs as a base type regardless of what
266
// the source declares.
267
add_type_reference_by_name(
268
assembly_name: string,
269
namespace_name: string,
270
simple_name: string
271
) -> TypeReferenceHandle is
272
let key = "{assembly_name}|{namespace_name}|{simple_name}"
273
274
if _named_type_refs.contains_key(key) then
275
return _named_type_refs[key]
276
fi
277
278
add_assembly_reference(assembly_name)
279
280
let handle = _metadata.add_type_reference(
281
cast EntityHandle(_assembly_refs[assembly_name]),
282
_metadata.get_or_add_string(namespace_name),
283
_metadata.get_or_add_string(simple_name))
284
285
_named_type_refs[key] = handle
286
287
return handle
288
si
289
290
// A TypeSpec names a constructed generic where a table-level
291
// reference is needed. Cached so two references to the same
292
// instantiation are the same row.
293
//
294
// Keyed on the encoded signature rather than on how the type
295
// spells itself: a class type parameter and a method type
296
// parameter of the same name render the same but encode
297
// differently (`!0` against `!!0`), so a spelling key gives
298
// whichever was seen first to both.
299
_type_specs: Collections.MAP[string, System.Reflection.Metadata.TypeSpecificationHandle]
300
301
// The member whose metadata is currently being written, or null
302
// outside any member. A TypeSpec naming a captured type parameter
303
// has to be encoded in that member's terms: inside a frame method
304
// the parameter is one of the frame class's own, and encoding it
305
// at the declared method-level index of the function it was
306
// written in produces a reference to a method type parameter the
307
// frame method does not have.
308
current_emission_member: Semantic.Symbols.Symbol? public
309
310
add_type_specification(
311
type: Semantic.Types.Type
312
) -> System.Reflection.Metadata.TypeSpecificationHandle is
313
// Same mapping the member's own signature is written under
314
// (see SRM_SIGNATURE_ENCODER.field_signature and
315
// method_signature). A TypeSpec is built from wherever the
316
// instruction referencing it sits, so it needs the mapping
317
// opened here rather than inheriting one.
318
let mapping = SRM_SIGNATURE_ENCODER.frame_mapping_for_emission(current_emission_member)
319
320
if mapping? then
321
mapping.map_type_arguments()
322
fi
323
324
try
325
return _add_type_specification(type)
326
finally
327
if mapping? then
328
mapping.unmap_type_arguments()
329
fi
330
yrt
331
si
332
333
_add_type_specification(
334
type: Semantic.Types.Type
335
) -> System.Reflection.Metadata.TypeSpecificationHandle is
336
let signature = SRM_SIGNATURE_ENCODER(self).type_specification_signature(type)
337
338
let key = System.Convert.to_base64_string(signature)
339
340
if _type_specs.contains_key(key) then
341
return _type_specs[key]
342
fi
343
344
let handle = _metadata.add_type_specification(_metadata.get_or_add_blob(signature))
345
346
_type_specs[key] = handle
347
348
return handle
349
si
350
351
// A reference to a constructor on a type this assembly does not
352
// define. The owner is a handle rather than a symbol because the
353
// constructors that need this are named by a constructed type.
354
// A generic method is called through a MethodSpec naming the
355
// open method plus the arguments it was instantiated with. The
356
// open method keeps one reference row however many
357
// instantiations there are.
358
_method_specs: Collections.MAP[(method: int, signature: string), System.Reflection.Metadata.MethodSpecificationHandle]
359
360
// `key` is a human-readable description for diagnostics only;
361
// the cache is keyed on the open method plus the encoded
362
// arguments, for the same reason add_type_specification is.
363
add_method_specification(
364
method: EntityHandle,
365
key: string,
366
type_arguments: Collections.List[Semantic.Types.Type]
367
) -> System.Reflection.Metadata.MethodSpecificationHandle is
368
let builder = BlobBuilder(16)
369
370
let encoder =
371
System.Reflection.Metadata.Ecma335.BlobEncoder(builder)
372
.method_specification_signature(type_arguments.count)
373
374
let signatures = SRM_SIGNATURE_ENCODER(self)
375
376
for argument in type_arguments do
377
signatures.encode_type(encoder.add_argument(), argument)
378
od
379
380
let identity =
381
(
382
method = MetadataTokens.get_token(method),
383
signature = System.Convert.to_base64_string(builder.to_array())
384
)
385
386
if _method_specs.contains_key(identity) then
387
return _method_specs[identity]
388
fi
389
390
let handle =
391
_metadata.add_method_specification(method, _metadata.get_or_add_blob(builder.to_array()))
392
393
_method_specs[identity] = handle
394
395
return handle
396
si
397
398
add_constructor_reference(
399
owner: EntityHandle,
400
key: string,
401
argument_types: Collections.List[Semantic.Types.Type]
402
) -> MemberReferenceHandle =>
403
add_named_member_reference(
404
owner,
405
".ctor",
406
key,
407
SRM_SIGNATURE_ENCODER(self).constructor_signature(argument_types))
408
409
// A reference to a member of a type this assembly does not
410
// define, where the signature is built rather than read off a
411
// symbol. The owner is a handle so that a constructed generic's
412
// TypeSpec can own it.
413
//
414
// `key` is a human-readable description for diagnostics only.
415
// The cache is keyed on what actually identifies the row —
416
// owner, name and encoded signature — because a description
417
// can collide where those differ and differ where they match.
418
//
419
// Several callers have no symbol to key on: `Invoke` on a
420
// delegate type, `String::Concat`, a synthesised `.ctor`, a unit
421
// variant's interned instance. The row's own identity is what
422
// every caller does have, and it is what the CLR compares.
423
add_named_member_reference(
424
owner: EntityHandle,
425
name: string,
426
key: string,
427
signature: ubyte[]
428
) -> MemberReferenceHandle is
429
let identity =
430
(
431
owner = MetadataTokens.get_token(owner),
432
name = name,
433
signature = System.Convert.to_base64_string(signature)
434
)
435
436
if _named_member_refs.contains_key(identity) then
437
return _named_member_refs[identity]
438
fi
439
440
let handle = _metadata.add_member_reference(
441
owner,
442
_metadata.get_or_add_string(name),
443
_metadata.get_or_add_blob(signature))
444
445
_named_member_refs[identity] = handle
446
447
return handle
448
si
449
450
_named_member_refs: Collections.MAP[(owner: int, name: string, signature: string), MemberReferenceHandle]
451
452
453
// A method with locals needs a StandAloneSig naming their
454
// types, in slot order, for the body header to point at.
455
add_method_body(encoder: InstructionEncoder, body: SRM_METHOD_BODY_EMITTER) -> int is
456
let locals =
457
if body.has_locals then
458
add_local_variable_signature(body.local_types)
459
else
460
_[System.Reflection.Metadata.StandaloneSignatureHandle]
461
fi
462
463
// A fixed stack depth is reserved for every body rather
464
// than a bound being computed per body.
465
//
466
// Locals are always initialized: a method that both has
467
// locals and contains a protected region is rejected
468
// outright by the runtime otherwise — an
469
// InvalidProgramException naming the method, with nothing
470
// in the body to point at.
471
return
472
_body_stream.add_method_body(
473
encoder,
474
64,
475
locals,
476
System.Reflection.Metadata.Ecma335.MethodBodyAttributes.INIT_LOCALS)
477
si
478
479
add_local_variable_signature(
480
types: Collections.List[Semantic.Types.Type]
481
) -> System.Reflection.Metadata.StandaloneSignatureHandle is
482
// A body's locals are named in the terms of the member the
483
// body belongs to, the same as the TypeSpecs its instructions
484
// reference — see add_type_specification.
485
let mapping = SRM_SIGNATURE_ENCODER.frame_mapping_for_emission(current_emission_member)
486
487
if mapping? then
488
mapping.map_type_arguments()
489
fi
490
491
try
492
return _add_local_variable_signature(types)
493
finally
494
if mapping? then
495
mapping.unmap_type_arguments()
496
fi
497
yrt
498
si
499
500
_add_local_variable_signature(
501
types: Collections.List[Semantic.Types.Type]
502
) -> System.Reflection.Metadata.StandaloneSignatureHandle is
503
let builder = BlobBuilder(32)
504
505
let variables =
506
System.Reflection.Metadata.Ecma335.BlobEncoder(builder)
507
.local_variable_signature(types.count)
508
509
let signatures = SRM_SIGNATURE_ENCODER(self)
510
511
for type in types do
512
let referenced = SRM_SIGNATURE_ENCODER.pointee(type)
513
514
signatures.encode_type(
515
variables.add_variable().`type(referenced?, false), referenced ?? type)
516
od
517
518
return _metadata.add_standalone_signature(_metadata.get_or_add_blob(builder.to_array()))
519
si
520
521
// The source positions of the body at `offset`, recorded as the
522
// body is flushed and read back when its MethodDef row is
523
// written. Keyed on the offset because that is the only thing
524
// the two sides share: the body is encoded during the tree
525
// walk and the row is written afterwards, and every body
526
// occupies a distinct offset in the stream.
527
_sequence_points: Collections.MAP[int, Collections.List[SRM_SEQUENCE_POINT]]
528
529
// The modules P/Invoke methods call into, by name.
530
_module_references: Collections.MAP[string, System.Reflection.Metadata.ModuleReferenceHandle]
531
532
set_sequence_points(
533
body_offset: int,
534
points: Collections.List[SRM_SEQUENCE_POINT]
535
) is
536
_sequence_points[body_offset] = points
537
si
538
539
add_method_definition(
540
flags: MethodAttributes,
541
impl_flags: MethodImplAttributes,
542
name: StringHandle,
543
signature: BlobHandle,
544
body_offset: int,
545
parameter_list: System.Reflection.Metadata.ParameterHandle
546
) -> MethodDefinitionHandle is
547
let handle =
548
_metadata.add_method_definition(
549
flags, impl_flags, name, signature, body_offset, parameter_list)
550
551
// MethodDebugInformation is read positionally against
552
// MethodDef, so every row added here needs one there -
553
// including the ones with nothing to say. Doing it in the
554
// same call is what keeps the two in step; see
555
// SRM_PDB_BUILDER.
556
if let pdb = _pdb then
557
let points =
558
if _sequence_points.contains_key(body_offset) then
559
_sequence_points[body_offset]
560
else
561
null
562
fi
563
564
pdb.add_method_debug_information(points)
565
fi
566
567
return handle
568
si
569
570
// The module a P/Invoke calls into, one row per distinct name:
571
// ModuleRef is not a sorted side table, and a second row for a
572
// module already named would be a second module as far as a
573
// reader is concerned.
574
get_or_add_module_reference(name: string) -> System.Reflection.Metadata.ModuleReferenceHandle is
575
if _module_references.contains_key(name) then
576
return _module_references[name]
577
fi
578
579
let handle = _metadata.add_module_reference(get_or_add_string(name))
580
581
_module_references[name] = handle
582
583
return handle
584
si
585
586
// The ImplMap row that says what a P/Invoke method calls: the
587
// module, the name to look up in it, and how to marshal. SRM
588
// sorts this table, so the row can be added as the method is
589
// written.
590
add_method_import(
591
method: MethodDefinitionHandle,
592
attributes: System.Reflection.MethodImportAttributes,
593
name: string,
594
module: System.Reflection.Metadata.ModuleReferenceHandle
595
) is
596
_metadata.add_method_import(method, attributes, get_or_add_string(name), module)
597
si
598
599
add_parameter(
600
name: StringHandle,
601
sequence_number: int
602
) -> System.Reflection.Metadata.ParameterHandle =>
603
_metadata.add_parameter(cast System.Reflection.ParameterAttributes(0), name, sequence_number)
604
605
add_field_definition(
606
flags: System.Reflection.FieldAttributes,
607
name: StringHandle,
608
signature: BlobHandle
609
) -> System.Reflection.Metadata.FieldDefinitionHandle =>
610
_metadata.add_field_definition(flags, name, signature)
611
612
// The compile-time value of a literal field. The Constant table
613
// is one of the sorted side tables, so SRM orders the rows and
614
// they can be added as each field is written.
615
add_constant(
616
parent: EntityHandle,
617
value: object
618
) -> System.Reflection.Metadata.ConstantHandle =>
619
_metadata.add_constant(parent, value)
620
621
// A custom attribute on whatever `parent` names — a type, a
622
// method, a parameter, the assembly itself.
623
//
624
// CustomAttribute is sorted on its parent, but SRM sorts it
625
// rather than validating it, so rows can be added as the walk
626
// reaches them.
627
add_custom_attribute(
628
parent: EntityHandle,
629
constructor: EntityHandle,
630
value: ubyte[]
631
) is
632
_metadata.add_custom_attribute(parent, constructor, get_or_add_blob(value))
633
si
634
635
add_property(
636
name: StringHandle,
637
signature: ubyte[]
638
) -> System.Reflection.Metadata.PropertyDefinitionHandle =>
639
_metadata.add_property(
640
cast System.Reflection.PropertyAttributes(0),
641
name,
642
get_or_add_blob(signature))
643
644
// Points a type at the run of property rows it owns. PropertyMap
645
// is sorted on its parent, and the walk writes types in
646
// ascending row order, so adding one per type as the walk
647
// reaches it is already in order.
648
add_property_map(
649
parent: TypeDefinitionHandle,
650
first_property: System.Reflection.Metadata.PropertyDefinitionHandle
651
) is
652
_metadata.add_property_map(parent, first_property)
653
si
654
655
// Ties an accessor method to the property it reads or writes.
656
// MethodSemantics is a sorted side table that SRM orders itself,
657
// so these can be added as the walk reaches them.
658
add_property_accessor(
659
property: System.Reflection.Metadata.PropertyDefinitionHandle,
660
accessor: MethodDefinitionHandle,
661
is_getter: bool
662
) is
663
_metadata.add_method_semantics(
664
cast EntityHandle(property),
665
if is_getter then
666
System.Reflection.MethodSemanticsAttributes.GETTER
667
else
668
System.Reflection.MethodSemanticsAttributes.SETTER
669
fi,
670
accessor)
671
si
672
673
// An attribute the compiler applies itself rather than resolving
674
// from a pragma, named rather than reached through a symbol: the
675
// marker attributes carrying what metadata cannot express, the
676
// framework attributes on a return or parameter slot, and the
677
// assembly-level attributes the command line supplies.
678
//
679
// The type name is qualified, and split here rather than written
680
// as a namespace and a simple name at each call site: a
681
// reference built from the two halves out of step names a type
682
// that does not exist while still emitting cleanly.
683
add_named_attribute(
684
parent: EntityHandle,
685
assembly_name: string,
686
qualified_name: string,
687
argument_types: Collections.List[Semantic.Types.Type],
688
value: ubyte[]
689
) is
690
let dot = qualified_name.last_index_of('.')
691
692
let namespace_name =
693
if dot >= 0 then qualified_name.substring(0, dot) else "" fi
694
695
let simple_name =
696
if dot >= 0 then qualified_name.substring(dot + 1) else qualified_name fi
697
698
let attribute_type =
699
add_type_reference_by_name(assembly_name, namespace_name, simple_name)
700
701
add_custom_attribute(
702
parent,
703
cast EntityHandle(
704
add_constructor_reference(
705
cast EntityHandle(attribute_type),
706
"{qualified_name}::.ctor",
707
argument_types)),
708
value)
709
si
710
711
// The value blob of an attribute applied with no arguments: the
712
// two-byte prolog every attribute blob opens with, then a
713
// named-argument count of zero.
714
NO_ARGUMENT_ATTRIBUTE_VALUE: ubyte[] static => [1ub, 0ub, 0ub, 0ub]
715
716
add_type_definition(
717
flags: System.Reflection.TypeAttributes,
718
namespace_name: StringHandle,
719
simple_name: StringHandle,
720
base_type: EntityHandle,
721
field_list: System.Reflection.Metadata.FieldDefinitionHandle,
722
method_list: MethodDefinitionHandle
723
) -> System.Reflection.Metadata.TypeDefinitionHandle =>
724
_metadata.add_type_definition(
725
flags, namespace_name, simple_name, base_type, field_list, method_list)
726
727
// A type or method that declares type parameters needs a row per
728
// parameter. Without them the declaration is not generic, and
729
// every signature that names one by index - a field of type !0,
730
// an argument of type !!0 - is naming something the type does
731
// not have.
732
add_generic_parameter(
733
owner: EntityHandle,
734
index: int,
735
name: string,
736
attributes: System.Reflection.GenericParameterAttributes
737
) -> System.Reflection.Metadata.GenericParameterHandle =>
738
_metadata.add_generic_parameter(
739
owner,
740
attributes,
741
_metadata.get_or_add_string(name),
742
index)
743
744
// The type a parameter is bounded by (`[T: SomeBase]`). The CLR
745
// verifies shared generic code against the declared parameter
746
// rather than against each call site's substitution, so without
747
// this row the parameter is unbounded as far as the runtime is
748
// concerned: a body that relies on the bound fails verification,
749
// and passing the parameter on to a callee declaring the same
750
// bound is rejected as violating it.
751
add_generic_parameter_constraint(
752
parameter: System.Reflection.Metadata.GenericParameterHandle,
753
`type: EntityHandle
754
) is
755
_metadata.add_generic_parameter_constraint(parameter, `type)
756
si
757
758
// One row per interface a type declares. Without it the type
759
// still loads and its own methods still run, but nothing binds
760
// the interface's slots, so a call through the interface fails
761
// at dispatch rather than at load.
762
//
763
// InterfaceImpl is a sorted table, and SRM orders it on
764
// serialization, so rows go in as the walk reaches them.
765
//
766
// The returned handle is what the caller attaches a
767
// `NullableAttribute` to when the interface's own type
768
// arguments carry a reference-`?` position — see
769
// `SRM_STRUCTURE_WALK._interfaces`. `InterfaceImpl` is one of
770
// the few metadata rows .NET reflection has no `MemberInfo` for,
771
// so that attribute is the only way the optionality survives
772
// into another assembly's view of this type.
773
add_interface_implementation(
774
type: System.Reflection.Metadata.TypeDefinitionHandle,
775
`interface: EntityHandle
776
) -> InterfaceImplementationHandle is
777
return _metadata.add_interface_implementation(type, `interface)
778
si
779
780
// Binds a method this type defines to the interface slot it
781
// implements, for a member whose name cannot match the slot on
782
// its own — an explicit implementation, named for the interface
783
// it satisfies.
784
add_method_implementation(
785
type: System.Reflection.Metadata.TypeDefinitionHandle,
786
body: EntityHandle,
787
declaration: EntityHandle
788
) is
789
_metadata.add_method_implementation(type, body, declaration)
790
si
791
792
set_entry_point(method: MethodDefinitionHandle) is
793
_entry_point = method
794
si
795
796
// The identity a deterministic build gives its output: a hash of
797
// the content, rather than a fresh guid and the wall clock. Two
798
// builds of the same source then produce the same bytes, which
799
// is what lets the self-hosting check be a comparison of two
800
// assemblies rather than of two disassemblies.
801
_content_id_from_content(
802
content: Collections.Iterable[System.Reflection.Metadata.Blob]
803
) -> System.Reflection.Metadata.BlobContentId static is
804
let hasher =
805
System.Security.Cryptography.IncrementalHash.create_hash(
806
System.Security.Cryptography.HashAlgorithmName.sha256)
807
808
for blob in content do
809
let bytes = blob.get_bytes()
810
811
let array: ubyte[]? = bytes.array
812
813
hasher.append_data(array!, bytes.offset, bytes.count)
814
od
815
816
return
817
System.Reflection.Metadata.BlobContentId.from_hash(
818
System.Collections.Immutable.ImmutableArray.create[ubyte](
819
hasher.get_hash_and_reset()))
820
si
821
822
write_to(path: string) is
823
// The PDB is written first because the assembly has to name
824
// it: a debug directory entry carries the PDB's path and
825
// the identity a runtime checks the two match on, and that
826
// identity is not known until the PDB's bytes are.
827
let debug_directory =
828
if let pdb = _pdb then
829
let pdb_path = IO.Path.change_extension(path, ".pdb")!
830
831
let pdb_content_id =
832
pdb.write_to(
833
pdb_path,
834
_metadata.get_row_counts(),
835
_entry_point ?? _[MethodDefinitionHandle],
836
_content_id_from_content)
837
838
let builder =
839
System.Reflection.PortableExecutable.DebugDirectoryBuilder()
840
841
// The name is what the runtime looks for beside the
842
// assembly, so it is the file's own name rather than
843
// the path this compiler happened to write it to.
844
builder.add_code_view_entry(
845
IO.Path.get_file_name(pdb_path)!,
846
pdb_content_id,
847
_PORTABLE_PDB_VERSION)
848
849
builder
850
else
851
_[System.Reflection.PortableExecutable.DebugDirectoryBuilder?]
852
fi
853
854
let pe_header =
855
if _want_library then
856
PEHeaderBuilder.create_library_header()
857
else
858
PEHeaderBuilder.create_executable_header()
859
fi
860
861
// Every resource's bytes go into one blob as a four-byte
862
// little-endian length followed by the data, aligned to
863
// ManagedPEBuilder.ManagedResourcesDataAlignment (8) so the
864
// next resource's row offset lands where the format expects
865
// it. A nil `implementation` handle says the resource lives
866
// in this module rather than another file or assembly.
867
let managed_resources =
868
if _resources.count > 0 then
869
let blob = BlobBuilder(1024)
870
871
for r in _resources do
872
let offset = blob.count
873
874
blob.write_int32(r.bytes.count)
875
blob.write_bytes(r.bytes)
876
blob.align(8)
877
878
_metadata.add_manifest_resource(
879
System.Reflection.ManifestResourceAttributes.PUBLIC,
880
_metadata.get_or_add_string(r.name),
881
_[EntityHandle],
882
cast uint(offset))
883
od
884
885
blob
886
else
887
_[BlobBuilder?]
888
fi
889
890
let pe_builder = ManagedPEBuilder(
891
pe_header,
892
MetadataRootBuilder(_metadata, null, false),
893
_il_builder,
894
null,
895
managed_resources,
896
null,
897
debug_directory,
898
0,
899
_entry_point ?? _[MethodDefinitionHandle],
900
cast CorFlags(1), // ILOnly
901
_content_id_from_content)
902
903
let pe_blob = BlobBuilder(1024)
904
let content_id = pe_builder.serialize(pe_blob)
905
906
// Now that the content is fixed, the id derived from it can
907
// go into the space reserved for it above.
908
System.Reflection.Metadata.BlobWriter(_module_version_id).write_guid(content_id.guid)
909
910
let stream = IO.File.create(path)
911
pe_blob.write_content_to(stream)
912
stream.close()
913
si
914
si
915
si