Skip to content
← Back

src/ir/emitter/srm_signature_encoder.ghul

1
namespace IR.Emitter is
2
use System.Reflection.Metadata.BlobBuilder
3
use System.Reflection.Metadata.EntityHandle
4
use System.Reflection.Metadata.SignatureCallingConvention
5
use System.Reflection.Metadata.Ecma335.BlobEncoder
6
use System.Reflection.Metadata.Ecma335.MethodSignatureEncoder
7
use System.Reflection.Metadata.Ecma335.ReturnTypeEncoder
8
use System.Reflection.Metadata.Ecma335.ParametersEncoder
9
use System.Reflection.Metadata.Ecma335.SignatureTypeEncoder
10
11
// Encodes a function's argument and return types as an ECMA-335
12
// signature blob.
13
//
14
// The shape mirrors Symbols.Classy.gen_type's decision tree — the
15
// il_is_primitive_type short-circuit, then a class-versus-valuetype
16
// split, then generic instantiation — with metadata handles in place
17
// of the text that path appends. Keeping the two in step matters:
18
// a methodref whose signature disagrees with the target's own
19
// declaration resolves to nothing at run time.
20
class SRM_SIGNATURE_ENCODER(_assembly: SRM_ASSEMBLY_EMITTER) is
21
// The innate string and object symbols, resolved once: every
22
// signature the assembly emits passes through encode_type.
23
_string_symbol: Semantic.Symbols.Symbol?
24
_object_symbol: Semantic.Symbols.Symbol?
25
26
string_symbol: Semantic.Symbols.Symbol is
27
if !_string_symbol? then
28
_string_symbol = (cast Semantic.Types.NAMED(IoC.CONTAINER.instance.innate_symbol_lookup.get_string_type())).symbol
29
fi
30
31
return _string_symbol
32
si
33
34
object_symbol: Semantic.Symbols.Symbol is
35
if !_object_symbol? then
36
_object_symbol = (cast Semantic.Types.NAMED(IoC.CONTAINER.instance.innate_symbol_lookup.get_object_type())).symbol
37
fi
38
39
return _object_symbol
40
si
41
42
// The closure to install a mapping from while a member of its
43
// frame is encoded, or null for a member that needs none.
44
//
45
// A member's signature is written in its owner's terms, and for
46
// a closure frame that does not happen on its own: the frame's
47
// type parameters are the enclosing function's symbols, which
48
// carry their own method-level position until the closure
49
// installs the frame's class-level one. A body is emitted inside
50
// that window, so its local signature comes out right unaided.
51
// These blobs are not: the structure walk builds them long after
52
// the window has closed, and a call site builds them from
53
// wherever it happens to be. Without reopening it, a field of
54
// `T` encodes as `!!0` on a type whose one parameter is `!0`.
55
//
56
// The reference the member hangs off is deliberately outside
57
// this: it names the frame at the arguments the *caller* passes,
58
// which are the caller's own parameters and are spelled where
59
// the caller stands.
60
// As frame_mapping, but for the member a body is being emitted
61
// into rather than a member whose own signature is being written,
62
// so a null member (outside any) answers null rather than faulting.
63
frame_mapping_for_emission(member: Semantic.Symbols.Symbol?) -> Semantic.Symbols.Closure? static is
64
if member? then
65
return frame_mapping(member)
66
fi
67
68
return null
69
si
70
71
frame_mapping(member: Semantic.Symbols.Symbol) -> Semantic.Symbols.Closure? static is
72
// The closure's own method is emitted as a member of its
73
// frame, but it is declared into the function the literal
74
// was written in, so its owner does not name the frame. A
75
// frameless closure needs the mapping too when it captures
76
// type arguments: its method-level type parameters are
77
// numbered in capture order, so its signature must be
78
// written in that space rather than the enclosing
79
// function's declaration order.
80
if let closure: Semantic.Symbols.Closure = member then
81
if closure.frame? \/ closure.captured_type_arguments? then
82
return closure
83
fi
84
fi
85
86
if let frame: Semantic.Symbols.FRAME = member.owner then
87
return frame.closure
88
fi
89
90
// The frame's own rows need it too: its type parameters are
91
// the enclosing function's symbols, so a bound naming one
92
// encodes at that symbol's method-level position unless the
93
// frame's class-level one is installed.
94
if let frame: Semantic.Symbols.FRAME = member then
95
return frame.closure
96
fi
97
98
return null
99
si
100
101
// A property's signature is its accessors' shape rather than its
102
// own: the calling convention says PROPERTY, and an instance
103
// property sets HASTHIS to match the getter it is read through.
104
// ghūl declares no indexed properties, so the parameter list is
105
// always empty and the signature carries only the type.
106
property_signature(property: Semantic.Symbols.Property) -> ubyte[] is
107
let builder = BlobBuilder(16)
108
109
let return_type: ReturnTypeEncoder mut
110
let parameters: ParametersEncoder mut
111
112
BlobEncoder(builder)
113
.property_signature(_is_instance_property(property))
114
.parameters(0, return_type ref, parameters ref)
115
116
encode_type(return_type.`type(false), property.type!)
117
118
return builder.to_array()
119
si
120
121
_is_instance_property(property: Semantic.Symbols.Property) -> bool static =>
122
isa Semantic.Symbols.INSTANCE_PROPERTY(property)
123
124
field_signature(`field: Semantic.Symbols.Field) -> ubyte[] is
125
let closure = frame_mapping(`field)
126
127
if closure? then
128
closure.map_type_arguments()
129
fi
130
131
try
132
let builder = BlobBuilder(16)
133
134
// Same recovery as for a method signature: a field
135
// inherited from a constructed-generic base in another
136
// assembly surfaces via reflection with its type
137
// parameter substituted, and the open form is carried as
138
// unspecialized_type. Prefer it when present.
139
encode_type(
140
BlobEncoder(builder).field_signature(),
141
`field.unspecialized_type ?? `field.type!)
142
143
return builder.to_array()
144
finally
145
if closure? then
146
closure.unmap_type_arguments()
147
fi
148
yrt
149
si
150
151
// The same, for a field the back end synthesises and so has no
152
// symbol to read a type off.
153
field_signature(type: Semantic.Types.Type) -> ubyte[] is
154
let builder = BlobBuilder(16)
155
156
encode_type(BlobEncoder(builder).field_signature(), type)
157
158
return builder.to_array()
159
si
160
161
// A static constructor's signature: static, void, no arguments.
162
static_initializer_signature() -> ubyte[] is
163
let builder = BlobBuilder(8)
164
165
let return_type: ReturnTypeEncoder mut
166
let parameters: ParametersEncoder mut
167
168
BlobEncoder(builder)
169
.method_signature(SignatureCallingConvention.DEFAULT, 0, false)
170
.parameters(0, return_type ref, parameters ref)
171
172
return_type.void()
173
174
return builder.to_array()
175
si
176
177
// An instance method taking nothing and returning the given
178
// reference type, named by handle rather than by symbol: the
179
// members that need this satisfy a framework interface no ghūl
180
// declaration mentions.
181
nullary_instance_signature(return_type: EntityHandle) -> ubyte[] is
182
let builder = BlobBuilder(16)
183
184
let return_encoder: ReturnTypeEncoder mut
185
let parameters: ParametersEncoder mut
186
187
BlobEncoder(builder)
188
.method_signature(SignatureCallingConvention.DEFAULT, 0, true)
189
.parameters(0, return_encoder ref, parameters ref)
190
191
return_encoder.`type(false).`type(return_type, false)
192
193
return builder.to_array()
194
si
195
196
// The same, returning `object`, which has its own element-type
197
// code rather than a reference.
198
nullary_instance_signature_returning_object() -> ubyte[] is
199
let builder = BlobBuilder(16)
200
201
let return_encoder: ReturnTypeEncoder mut
202
let parameters: ParametersEncoder mut
203
204
BlobEncoder(builder)
205
.method_signature(SignatureCallingConvention.DEFAULT, 0, true)
206
.parameters(0, return_encoder ref, parameters ref)
207
208
return_encoder.`type(false).object()
209
210
return builder.to_array()
211
si
212
213
// A constructor's signature: an instance method returning void,
214
// taking the given argument types. Built from types rather than
215
// from a Function because the constructors that need this are
216
// the ones with no symbol to read a signature off.
217
constructor_signature(argument_types: Collections.List[Semantic.Types.Type]) -> ubyte[] is
218
let builder = BlobBuilder(16)
219
220
let return_type: ReturnTypeEncoder mut
221
let parameters: ParametersEncoder mut
222
223
BlobEncoder(builder)
224
.method_signature(SignatureCallingConvention.DEFAULT, 0, true)
225
.parameters(argument_types.count, return_type ref, parameters ref)
226
227
return_type.void()
228
229
for argument in argument_types do
230
let referenced = pointee(argument)
231
232
encode_type(parameters.add_parameter().`type(referenced?), referenced ?? argument)
233
od
234
235
return builder.to_array()
236
si
237
238
// A static method's signature, built from types rather than read
239
// off a Function, for the framework members the back end reaches
240
// for by name.
241
static_signature(
242
return_type_of: Semantic.Types.Type,
243
argument_types: Collections.List[Semantic.Types.Type]
244
) -> ubyte[] =>
245
call_signature(return_type_of, argument_types, false)
246
247
// The same, for a method reached through a receiver. Built from
248
// types rather than from a Function because the members that
249
// need it are ones no symbol in the compilation declares.
250
call_signature(
251
return_type_of: Semantic.Types.Type,
252
argument_types: Collections.List[Semantic.Types.Type],
253
is_instance: bool
254
) -> ubyte[] is
255
let builder = BlobBuilder(16)
256
257
let return_type: ReturnTypeEncoder mut
258
let parameters: ParametersEncoder mut
259
260
BlobEncoder(builder)
261
.method_signature(SignatureCallingConvention.DEFAULT, 0, is_instance)
262
.parameters(argument_types.count, return_type ref, parameters ref)
263
264
encode_return(return_type, return_type_of)
265
266
for argument in argument_types do
267
let referenced = pointee(argument)
268
269
encode_type(parameters.add_parameter().`type(referenced?), referenced ?? argument)
270
od
271
272
return builder.to_array()
273
si
274
275
// A constructor on a constructed generic, taking one argument
276
// per type parameter. The arguments are written as indexes for
277
// the same reason Invoke's are: the reference is to the open
278
// type's constructor, and the instantiation lives in the owning
279
// TypeSpec.
280
generic_constructor_signature(argument_count: int) -> ubyte[] is
281
let builder = BlobBuilder(16)
282
283
let return_type: ReturnTypeEncoder mut
284
let parameters: ParametersEncoder mut
285
286
BlobEncoder(builder)
287
.method_signature(SignatureCallingConvention.DEFAULT, 0, true)
288
.parameters(argument_count, return_type ref, parameters ref)
289
290
return_type.void()
291
292
for i in 0..argument_count do
293
parameters.add_parameter().`type(false).generic_type_parameter(i)
294
od
295
296
return builder.to_array()
297
si
298
299
// A delegate's Invoke, whose parameters and return are the
300
// delegate type's own type parameters. They are written as
301
// indexes rather than as the arguments the delegate was
302
// instantiated with: the reference is to Func`N's Invoke, and
303
// the instantiation lives in the owning TypeSpec.
304
delegate_invoke_signature(argument_count: int, is_action: bool) -> ubyte[] is
305
let builder = BlobBuilder(16)
306
307
let return_type: ReturnTypeEncoder mut
308
let parameters: ParametersEncoder mut
309
310
BlobEncoder(builder)
311
.method_signature(SignatureCallingConvention.DEFAULT, 0, true)
312
.parameters(argument_count, return_type ref, parameters ref)
313
314
if is_action then
315
return_type.void()
316
else
317
return_type.`type(false).generic_type_parameter(argument_count)
318
fi
319
320
for i in 0..argument_count do
321
parameters.add_parameter().`type(false).generic_type_parameter(i)
322
od
323
324
return builder.to_array()
325
si
326
327
// The blob a TypeSpec row holds: the constructed type encoded
328
// structurally, exactly as it would appear inside any other
329
// signature.
330
type_specification_signature(type: Semantic.Types.Type) -> ubyte[] is
331
let builder = BlobBuilder(16)
332
333
encode_type(BlobEncoder(builder).type_specification_signature(), type)
334
335
return builder.to_array()
336
si
337
338
method_signature(function: Semantic.Symbols.Function) -> ubyte[] is
339
let closure = frame_mapping(function)
340
341
if closure? then
342
closure.map_type_arguments()
343
fi
344
345
try
346
return _method_signature(function)
347
finally
348
if closure? then
349
closure.unmap_type_arguments()
350
fi
351
yrt
352
si
353
354
// A signature in the member's own specialised types, for a
355
// definition this assembly writes over a member it takes from an
356
// instantiated ancestor. A reference prefers the open form; a
357
// definition has no instantiation to resolve the indexes against.
358
instantiated_method_signature(function: Semantic.Symbols.Function) -> ubyte[] =>
359
_method_signature(function, false)
360
361
_method_signature(function: Semantic.Symbols.Function) -> ubyte[] =>
362
_method_signature(function, true)
363
364
_method_signature(function: Semantic.Symbols.Function, prefer_open: bool) -> ubyte[] is
365
let builder = BlobBuilder(32)
366
367
// A method inherited from a constructed-generic base in
368
// another assembly surfaces via reflection with its type-
369
// parameter references substituted to concrete types, which
370
// the CLR will not match at the call site. The open-generic
371
// form is recovered at import time (symbol_factory) and
372
// carried as unspecialized_*; prefer it when present.
373
let arguments =
374
if prefer_open then function.unspecialized_arguments ?? function.arguments else function.arguments fi
375
376
let return_type: ReturnTypeEncoder mut
377
let parameters: ParametersEncoder mut
378
379
// A generic method's signature declares its arity, and its
380
// own type parameters appear in it as indexes rather than
381
// as the arguments some instantiation supplied.
382
BlobEncoder(builder)
383
.method_signature(
384
SignatureCallingConvention.DEFAULT,
385
function.generic_arguments.count,
386
function.is_emitted_with_receiver)
387
.parameters(arguments.count, return_type ref, parameters ref)
388
389
encode_return(
390
return_type,
391
if prefer_open then function.unspecialized_return_type ?? function.return_type else function.return_type fi)
392
393
for argument in arguments do
394
let referenced = pointee(argument)
395
396
encode_type(parameters.add_parameter().`type(referenced?), referenced ?? argument)
397
od
398
399
return builder.to_array()
400
si
401
402
// What a `T ref` points at, or null when the type is not a
403
// reference.
404
//
405
// The format carries byref as a flag on the slot rather than as
406
// part of the type, so a `T ref` marks its parameter, return or
407
// local and encodes the pointee in its own place. Encoding the
408
// reference type itself instead names a value type the runtime
409
// will not accept where a managed pointer belongs.
410
pointee(type: Semantic.Types.Type?) -> Semantic.Types.Type? static =>
411
if let reference: Semantic.Types.REFERENCE = type then
412
reference.arguments[0]
413
else
414
null
415
fi
416
417
encode_return(encoder: ReturnTypeEncoder, type: Semantic.Types.Type?) is
418
if !type? \/ is_void(type) then
419
encoder.void()
420
421
return
422
fi
423
424
let referenced = pointee(type)
425
426
encode_type(encoder.`type(referenced?), referenced ?? type)
427
si
428
429
is_void(type: Semantic.Types.Type) -> bool static =>
430
type.symbol.il_name_override =~ "void"
431
432
encode_type(encoder: SignatureTypeEncoder, type: Semantic.Types.Type) is
433
if let array: Semantic.Types.ARRAY = type then
434
encode_type(encoder.szarray(), array.arguments[0])
435
436
return
437
fi
438
439
// A pointer is its own element type in the format, not a
440
// generic named `POINTER` over one. Encoding it as the
441
// latter names a class the runtime has never heard of, and
442
// the slot then holds an object reference where the code
443
// reading it expects a native integer.
444
if let pointer: Semantic.Types.POINTER = type then
445
encode_type(encoder.pointer(), pointer.arguments[0])
446
447
return
448
fi
449
450
let symbol = type.symbol
451
452
if let generic_argument: Semantic.Symbols.GenericArgument = symbol then
453
// A body emitted somewhere other than where its type
454
// parameters were declared — a state machine's MoveNext,
455
// a closure's frame — reaches them at a position the
456
// lowering assigns, rather than at their own index.
457
if let position = generic_argument.emitted_position then
458
if position.is_class_level then
459
encoder.generic_type_parameter(position.index)
460
else
461
encoder.generic_method_type_parameter(position.index)
462
fi
463
elif isa Semantic.Symbols.FUNCTION_GENERIC_ARGUMENT(generic_argument) then
464
encoder.generic_method_type_parameter(generic_argument.index)
465
else
466
encoder.generic_type_parameter(generic_argument.index)
467
fi
468
469
return
470
fi
471
472
if symbol.il_is_primitive_type then
473
encode_primitive(encoder, symbol.il_name_override ?? "")
474
475
return
476
fi
477
478
// A constructed generic's symbol is a Symbols.GENERIC, which
479
// holds the open definition rather than being one. The
480
// format wants that definition as the TypeDefOrRef, with
481
// the arguments encoded after it, so the specialization is
482
// unwrapped here rather than referenced in its own right.
483
let classy =
484
if let generic: Semantic.Symbols.GENERIC = symbol then
485
generic.symbol
486
else
487
cast Semantic.Symbols.Classy?(symbol)
488
fi
489
490
if !classy? then
491
throw System.NotImplementedException(
492
"the binary back end cannot encode the type '{type}' yet")
493
fi
494
495
// string and object have dedicated element-type codes rather
496
// than a typeref, and il_is_primitive_type does not cover
497
// them — neither is a CLR primitive. A signature spelling
498
// them as a class reference is well-formed, but no other
499
// assembly declares them that way, so a methodref built from
500
// one binds to nothing.
501
if classy == string_symbol then
502
encoder.string()
503
504
return
505
elif classy == object_symbol then
506
encoder.object()
507
508
return
509
fi
510
511
let handle = _type_handle_for(classy)
512
513
let arguments = type.arguments
514
515
if arguments.count > 0 then
516
let generic_arguments =
517
encoder.generic_instantiation(handle, arguments.count, classy.is_value_type)
518
519
for argument in arguments do
520
encode_type(generic_arguments.add_argument(), argument)
521
od
522
523
return
524
fi
525
526
encoder.`type(handle, classy.is_value_type)
527
si
528
529
encode_primitive(encoder: SignatureTypeEncoder, il_name: string) static is
530
if il_name =~ "bool" then
531
encoder.boolean()
532
elif il_name =~ "char" then
533
encoder.char()
534
elif il_name =~ "int8" then
535
encoder.sbyte()
536
elif il_name =~ "unsigned int8" then
537
encoder.byte()
538
elif il_name =~ "int16" then
539
encoder.int16()
540
elif il_name =~ "unsigned int16" then
541
encoder.uint16()
542
elif il_name =~ "int32" then
543
encoder.int32()
544
elif il_name =~ "unsigned int32" then
545
encoder.uint32()
546
elif il_name =~ "int64" then
547
encoder.int64()
548
elif il_name =~ "unsigned int64" then
549
encoder.uint64()
550
elif il_name =~ "native int" then
551
encoder.int_ptr()
552
elif il_name =~ "native unsigned int" then
553
encoder.uint_ptr()
554
elif il_name =~ "float32" then
555
encoder.single()
556
elif il_name =~ "float64" then
557
encoder.double()
558
else
559
throw System.NotImplementedException(
560
"the binary back end cannot encode the primitive type '{il_name}' yet")
561
fi
562
si
563
564
// A primitive's il_name_override is the assembler keyword, which
565
// is what a signature needs and what a reference must never
566
// carry: there is no type called `int32`. Named here so that a
567
// member reached through a primitive owner - `to_string` on an
568
// `int` - names the type the runtime knows.
569
//
570
// Written as a chain rather than a `case` because `case` over a
571
// string compares by reference, so only interned literals match
572
// and a computed name silently falls through.
573
clr_name_for_primitive(il_name: string) -> string? static =>
574
if il_name =~ "bool" then "System.Boolean"
575
elif il_name =~ "char" then "System.Char"
576
elif il_name =~ "int8" then "System.SByte"
577
elif il_name =~ "unsigned int8" then "System.Byte"
578
elif il_name =~ "int16" then "System.Int16"
579
elif il_name =~ "unsigned int16" then "System.UInt16"
580
elif il_name =~ "int32" then "System.Int32"
581
elif il_name =~ "unsigned int32" then "System.UInt32"
582
elif il_name =~ "int64" then "System.Int64"
583
elif il_name =~ "unsigned int64" then "System.UInt64"
584
elif il_name =~ "native int" then "System.IntPtr"
585
elif il_name =~ "native unsigned int" then "System.UIntPtr"
586
elif il_name =~ "float32" then "System.Single"
587
elif il_name =~ "float64" then "System.Double"
588
elif il_name =~ "void" then "System.Void"
589
else null
590
fi
591
592
// A type this assembly defines is named by its own TypeDef row,
593
// not by a reference; every other type resolves to a TypeRef.
594
// The TypeDef row need not exist yet — the numbering pass has
595
// already fixed which row it will be.
596
_type_handle_for(symbol: Semantic.Symbols.Classy) -> EntityHandle is
597
if let definition = _assembly.handles.type_definition(symbol) then
598
return cast EntityHandle(definition)
599
fi
600
601
return cast EntityHandle(type_reference_for(symbol))
602
si
603
604
type_reference_for(symbol: Semantic.Symbols.Classy) -> System.Reflection.Metadata.TypeReferenceHandle is
605
let assembly_name = symbol.il_assembly_name
606
607
// An empty name is as absent as a null one, and worse to
608
// act on: it yields a reference row the runtime resolves to
609
// the assembly being built, so the type appears to be one
610
// this assembly defines and fails to load.
611
if !assembly_name? \/ assembly_name.length == 0 then
612
throw System.NotImplementedException(
613
"the binary back end cannot yet reference '{symbol.qualified_name}', "
614
"a type defined in the assembly being built")
615
fi
616
617
_assembly.add_assembly_reference(assembly_name)
618
619
return reference_for_clr_name(
620
symbol,
621
assembly_name,
622
symbol.il_name_override ?? symbol.qualified_name)
623
si
624
625
// One CLR type name to one reference row, for both places that
626
// need it: a type named in a signature, and the owner a member
627
// reference hangs off. Each used to split the name itself, and
628
// only one of the two would ever have learned about nesting.
629
//
630
// A nested type is written `Outer/Inner`, and each step is a
631
// reference of its own scoped to the one before it, with only
632
// the outermost carrying a namespace. Flattened into a single
633
// row the name resolves to nothing, and the failure surfaces at
634
// load rather than at emission.
635
reference_for_clr_name(
636
symbol: Semantic.Symbols.Symbol,
637
assembly_name: string,
638
clr_type_name: string
639
) -> System.Reflection.Metadata.TypeReferenceHandle is
640
let nested = clr_type_name.index_of('/')
641
642
if nested >= 0 then
643
let outer_name = clr_type_name.substring(0, nested)
644
let outer_dot = outer_name.last_index_of('.')
645
646
let enclosing mut =
647
_assembly.add_type_reference_by_name(
648
assembly_name,
649
if outer_dot >= 0 then outer_name.substring(0, outer_dot) else "" fi,
650
if outer_dot >= 0 then outer_name.substring(outer_dot + 1) else outer_name fi)
651
652
for step in clr_type_name.substring(nested + 1).split(['/']) do
653
enclosing =
654
_assembly.add_nested_type_reference(assembly_name, enclosing, step)
655
od
656
657
return enclosing
658
fi
659
660
let dot = clr_type_name.last_index_of('.')
661
662
return _assembly.add_type_reference(
663
symbol,
664
assembly_name,
665
if dot >= 0 then clr_type_name.substring(0, dot) else "" fi,
666
if dot >= 0 then clr_type_name.substring(dot + 1) else clr_type_name fi)
667
si
668
si
669
si