Skip to content
← Back

src/semantic/dotnet/symbol_factory.ghul

1
namespace Semantic.DotNet is
2
use TYPE = System.Type
3
4
use System.Reflection.Assembly
5
use System.Reflection.ConstructorInfo
6
use System.Reflection.MethodInfo
7
use System.Reflection.FieldInfo
8
use System.Reflection.PropertyInfo
9
use System.Reflection.MemberInfo
10
use System.Reflection.ParameterInfo
11
12
use System.Reflection.BindingFlags
13
use System.Reflection.MemberTypes
14
15
use System.Reflection.GenericParameterAttributes
16
17
use Collections.List
18
use Collections.MutableList
19
use Collections.LIST
20
use Collections.SET
21
use Collections.MAP
22
23
use Ghul.Pipes
24
25
use Types.Type
26
27
use Logging
28
29
// The reflected types these hold cannot be looked up until the
30
// type source has started, so start does it and the constructor
31
// only registers the callback.
32
@suppress("field-definite-assignment")
33
class SYMBOL_FACTORY(
34
_namespaces: NAMESPACES,
35
_type_name_map: TYPE_NAME_MAP,
36
_type_mapper: TYPE_MAPPER,
37
_type_source: TypeSource,
38
_type_details_lookup: TYPE_DETAILS_LOOKUP,
39
logger: Logging.Logger private
40
) is
41
_assembly_names: MAP[Assembly,string]
42
43
_symbol_table: SYMBOL_TABLE
44
45
_void_type: TYPE
46
_object_type: TYPE
47
_value_type: TYPE
48
_array_type: TYPE
49
_multicast_delegate_type: TYPE
50
_bool_type: TYPE
51
_int_type: TYPE
52
_equatable_open_type: TYPE
53
_comparable_open_type: TYPE
54
_tuple_types: List[TYPE]
55
56
// The types whose `<>` / `=~` is an intrinsic lowering to
57
// opcodes (`compare.order` and friends in the runtime's
58
// intrinsics). Their reflected interface members are hidden
59
// from binary-operator resolution so `a < b` keeps the opcode
60
// and its semantics — on `single`, `CompareTo` is a total
61
// order that sorts NaN below everything, where the opcode is
62
// the IEEE comparison. All are sealed, so the hidden member
63
// can have no sibling overloads to shadow.
64
//
65
// `string` has an innate `=~` but no innate order, so it is in
66
// the equality set only, and ordering one reaches
67
// `System.String.CompareTo`. That comparison is culture-
68
// sensitive: `"a" < "B"` answers true under a typical current
69
// culture and false under invariant globalization, so a string
70
// ordering is not machine-independent the way a scalar's is.
71
_innate_order_type_names: SET[string]
72
_innate_equality_type_names: SET[string]
73
74
// The scalar types whose arithmetic, bitwise and shift operators
75
// are the intrinsics. Held as the reflected types themselves, so
76
// membership is identity rather than a rendered name.
77
_intrinsic_operator_types: SET[TYPE]
78
79
_ambiguous_method_checker: AMBIGUOUS_METHOD_CHECKER
80
81
init(..) is
82
_assembly_names = MAP[Assembly,string]()
83
_ambiguous_method_checker = AMBIGUOUS_METHOD_CHECKER(logger)
84
85
_type_source.on_start(() -> void is start(); si)
86
si
87
88
start() is
89
_void_type = _type_source.get_type("System.Void")
90
_object_type = _type_source.get_type("System.Object")
91
_value_type = _type_source.get_type("System.ValueType")
92
_array_type = _type_source.get_type("System.Array")
93
_multicast_delegate_type = _type_source.get_type("System.MulticastDelegate")
94
_bool_type = _type_source.get_type("System.Boolean")
95
_int_type = _type_source.get_type("System.Int32")
96
_equatable_open_type = _type_source.get_type("System.IEquatable`1")
97
_comparable_open_type = _type_source.get_type("System.IComparable`1")
98
99
_innate_order_type_names = SET([
100
"System.Char",
101
"System.SByte", "System.Byte",
102
"System.Int16", "System.UInt16",
103
"System.Int32", "System.UInt32",
104
"System.Int64", "System.UInt64",
105
"System.IntPtr", "System.UIntPtr",
106
"System.Single", "System.Double",
107
"System.Decimal"
108
])
109
110
_innate_equality_type_names = SET([
111
"System.String"
112
])
113
114
_intrinsic_operator_types = SET(_type_source.get_types([
115
"System.Char",
116
"System.SByte", "System.Byte",
117
"System.Int16", "System.UInt16",
118
"System.Int32", "System.UInt32",
119
"System.Int64", "System.UInt64",
120
"System.IntPtr", "System.UIntPtr",
121
"System.Single", "System.Double",
122
"System.Decimal"
123
]))
124
125
_tuple_types = _type_source.get_types([
126
"System.ValueTuple`1",
127
"System.ValueTuple`2",
128
"System.ValueTuple`3",
129
"System.ValueTuple`4",
130
"System.ValueTuple`5",
131
"System.ValueTuple`6",
132
"System.ValueTuple`7"
133
])
134
135
// TODO handle more than 7-tuple types by
136
// nesting them
137
si
138
139
set_symbol_table(symbol_table: SYMBOL_TABLE) is
140
_symbol_table = symbol_table
141
si
142
143
// A reference set that cannot supply everything a type's members name
144
// is a fact about the command line, so it is reported rather than
145
// thrown: the caller keeps the partly-populated symbol, and the error
146
// stops the build before generate-il can meet whatever the missing
147
// members left behind.
148
report_materialization_failure(dotnet_type: TYPE, ex: System.Exception) is
149
_logger.environment_error(
150
Source.LOCATION.reflected,
151
"cannot load {dotnet_type}: {without_loader_advice(ex.message)}")
152
si
153
154
// The reflection layer's own remediation - load the assembly with
155
// `LoadFromAssemblyPath`, or supply a resolver - addresses whoever
156
// called it, which here is the compiler rather than the person reading
157
// the diagnostic. The sentence before it names the assembly, which is
158
// what they need. Dropping the advice is cosmetic on both sides: an
159
// unrecognised message is reported whole.
160
without_loader_advice(message: string) -> string is
161
let advice = message.index_of("Either explicitly load")
162
163
if advice < 0 then
164
return message
165
fi
166
167
return message.substring(0, advice).trim_end()
168
si
169
170
create_symbol(type_details: TYPE_DETAILS) -> Symbols.Scoped? is
171
try
172
let dotnet_type = type_details.dotnet_type
173
174
if dotnet_type.is_class \/ dotnet_type.is_value_type \/ dotnet_type.is_interface \/ dotnet_type.is_enum then
175
return create_class(type_details)
176
fi
177
catch ex: System.Exception
178
debug_always("create symbol failed: {type_details} exception: {ex}")
179
180
throw ex
181
yrt
182
183
return null
184
si
185
186
create_class(type_details: TYPE_DETAILS) -> Symbols.Scoped is
187
let dotnet_type = type_details.dotnet_type
188
let ghul_type_name = type_details.ghul_type_name
189
let assembly_name = type_details.assembly_name!
190
let il_name mut = type_details.il_name
191
192
let owner = EMPTY_SCOPE(type_details.ghul_namespace)
193
194
let arguments = LIST()
195
let argument_variances = LIST()
196
let argument_constraint_kinds = LIST()
197
let argument_has_constructor_constraint = LIST()
198
let argument_type_bounds = Collections.LIST[Collections.List[Semantic.Types.Type]]()
199
200
if dotnet_type.is_generic_type /\ dotnet_type.contains_generic_parameters then
201
let argument_types = dotnet_type.get_generic_arguments()
202
203
for a in argument_types do
204
if a.is_generic_type_parameter then
205
arguments.add(a.name)
206
argument_variances.add(_type_mapper.map_type_argument_variance(a))
207
argument_constraint_kinds.add(_type_mapper.generic_parameter_constraint_kind(a))
208
argument_has_constructor_constraint.add(_type_mapper.generic_parameter_has_constructor_constraint(a))
209
argument_type_bounds.add(type_bounds(a))
210
fi
211
od
212
fi
213
214
let result: Symbols.Classy mut
215
216
if dotnet_type.is_enum then
217
let enum_struct =
218
Symbols.ENUM_STRUCT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, owner)
219
220
enum_struct.mark_overrides_resolved()
221
222
enum_struct.underlying_type = _type_mapper.get_type(dotnet_type.get_enum_underlying_type())
223
224
Symbols.ENUM_ORDER_OPERATOR.register(enum_struct)
225
Symbols.ENUM_EQUALITY_OPERATOR.register(enum_struct)
226
227
if type_details.has_flags_attribute then
228
Symbols.ENUM_AND_OPERATOR.register(enum_struct)
229
Symbols.ENUM_OR_OPERATOR.register(enum_struct)
230
Symbols.ENUM_XOR_OPERATOR.register(enum_struct)
231
Symbols.ENUM_NOT_OPERATOR.register(enum_struct)
232
fi
233
234
result = enum_struct
235
elif dotnet_type.is_class /\ has_union_attribute(dotnet_type) then
236
result = Symbols.UNION(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner)
237
result.mark_overrides_resolved()
238
elif dotnet_type.is_class /\ has_variant_attribute(dotnet_type) then
239
result = Symbols.VARIANT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner)
240
result.mark_overrides_resolved()
241
elif dotnet_type.is_class then
242
let result_class = Symbols.CLASS(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner)
243
244
if dotnet_type == _object_type then
245
result_class.mark_is_object()
246
elif dotnet_type == _value_type then
247
result_class.mark_is_root_value_type()
248
elif dotnet_type == _array_type then
249
result_class.mark_is_root_array_type()
250
fi
251
252
if has_closed_attribute(dotnet_type) then
253
result_class.mark_closed()
254
fi
255
256
if _inherits_multicast_delegate(dotnet_type) then
257
result_class.mark_delegate()
258
fi
259
260
if dotnet_type.is_abstract then
261
result_class.mark_abstract()
262
fi
263
264
result = result_class
265
result.mark_overrides_resolved()
266
elif dotnet_type.is_value_type then
267
if dotnet_type == _void_type then
268
result = Symbols.VOID_STRUCT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner)
269
result.il_is_primitive_type = true
270
else
271
result = Symbols.STRUCT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner)
272
result.il_is_primitive_type = dotnet_type.is_primitive \/ dotnet_type == _void_type
273
fi
274
275
result.mark_overrides_resolved()
276
elif dotnet_type.is_interface then
277
let result_trait = Symbols.TRAIT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner)
278
279
if has_closed_attribute(dotnet_type) then
280
result_trait.mark_closed()
281
fi
282
283
result = result_trait
284
else
285
throw System.Exception("unhandled reflected kind for {dotnet_type}")
286
fi
287
288
// The attribute check alone misses a type imported from an
289
// assembly built before this change - ghul-runtime itself
290
// included. A type name is never operator syntax, so a
291
// leading `$` on a reflected type is an unambiguous legacy
292
// signal, the same reasoning add_field's fallback uses.
293
if
294
type_details.is_globals_carrier \/
295
_has_compiler_generated_attribute(dotnet_type.get_custom_attributes_data()) \/
296
ghul_type_name.starts_with('$')
297
then
298
result.mark_synthesized()
299
fi
300
301
if argument_variances.count > 0 then
302
result.argument_variances = argument_variances
303
fi
304
305
if argument_constraint_kinds.count > 0 then
306
result.argument_constraint_kinds = argument_constraint_kinds
307
fi
308
309
if argument_has_constructor_constraint.count > 0 then
310
result.argument_has_constructor_constraint = argument_has_constructor_constraint
311
fi
312
313
if argument_type_bounds.count > 0 /\ argument_type_bounds |> any(l => l.count > 0) then
314
result.argument_type_bounds = argument_type_bounds
315
fi
316
317
result.il_assembly_name = assembly_name
318
319
if let builder = type_details.async_builder_type then
320
if let classy_result: Symbols.Classy = result then
321
classy_result.async_builder = _type_mapper.get_type(builder)
322
fi
323
fi
324
325
if !il_name? then
326
il_name = get_il_name(dotnet_type)
327
fi
328
329
result.il_name_override = il_name
330
result.dotnet_full_name = dotnet_type.full_name
331
332
return result
333
si
334
335
add_ancestors(symbol: Symbols.Scoped, type: TYPE) is
336
if !isa Symbols.Classy(symbol) then
337
return
338
fi
339
340
let result = symbol
341
342
if type.base_type? then
343
result.add_ancestor(_type_mapper.get_type(type.base_type!))
344
elif type.is_interface then
345
result.add_ancestor(_type_mapper.get_type(_type_source.get_type("System.Object")))
346
fi
347
348
// A trait `type` implements directly can carry a reference-`?`
349
// type argument that ordinary reflection has no way to
350
// surface (see INTERFACE_NULLABILITY) — recover it from the
351
// assembly's raw metadata and apply it to the mapped type
352
// before it becomes an ancestor. Empty for a trait `type`
353
// only inherits via a base class, or one with nothing
354
// optional in its arguments; either way the mapped type is
355
// used as-is.
356
let nullable_by_key = _type_source.interface_nullability_bytes(type)
357
358
for i in type.get_interfaces() do
359
let mapped mut = _type_mapper.get_type(i)
360
361
let bytes: LIST[int] mut
362
363
if nullable_by_key.try_get_value(Semantic.DotNet.INTERFACE_NULLABILITY.key_for(i), bytes ref) then
364
mapped = Semantic.DotNet.NULLABILITY.apply_bytes(mapped, bytes) ?? mapped
365
fi
366
367
result.add_ancestor(mapped)
368
od
369
si
370
371
// Reflected interfaces are the one kind that isn't marked
372
// overrides-resolved on creation, so this is where their
373
// inherited members get pulled down. It has to run after
374
// `add_members`: a .NET interface that redeclares an inherited
375
// member covariantly (`new IDerived? BaseType { get; }`) only
376
// has its own declaration to override with once that
377
// declaration is in scope. Pull down first and the inherited
378
// declaration wins, hiding the derived one and turning a
379
// resolved redeclaration into an inheritance clash.
380
resolve_overrides(symbol: Symbols.Scoped) is
381
if !isa Symbols.Classy(symbol) then
382
return
383
fi
384
385
let result = symbol
386
387
IoC.CONTAINER.instance.symbol_table.enter_scope(result)
388
result.pull_down_super_symbols()
389
IoC.CONTAINER.instance.symbol_table.leave_scope(result)
390
si
391
392
mask_gpa(a: GenericParameterAttributes, b: GenericParameterAttributes) -> GenericParameterAttributes =>
393
cast GenericParameterAttributes(cast int(a) & cast int(b))
394
395
// Every ghūl-mapped .NET type bound on this parameter (`where T :
396
// IA, IB`), empty when the parameter is unbounded or only carries
397
// the redundant `System.ValueType` companion of a `struct` flag.
398
type_bounds(t: TYPE) -> Collections.List[Semantic.Types.Type] is
399
let result = Collections.LIST[Semantic.Types.Type]()
400
let constraint_attributes = mask_gpa(t.generic_parameter_attributes, GenericParameterAttributes.SPECIAL_CONSTRAINT_MASK)
401
let struct_constrained =
402
constraint_attributes.has_flag(GenericParameterAttributes.NOT_NULLABLE_VALUE_TYPE_CONSTRAINT)
403
404
for c in t.get_generic_parameter_constraints() do
405
if !(struct_constrained /\ c.full_name? /\ c.full_name =~ "System.ValueType") then
406
result.add(_type_mapper.get_type(c))
407
fi
408
od
409
410
return result
411
si
412
413
// Static members of the `$globals` synthetic class are global
414
// functions / variables / properties in the source language. Detect
415
// the host so the importer can produce GLOBAL_FUNCTION /
416
// GLOBAL_VARIABLE / GLOBAL_PROPERTY symbols rather than the
417
// static-on-a-class kinds. Their owner becomes the enclosing
418
// NAMESPACE (mirroring source-side `declare_function`) so qualified
419
// names skip the synthetic `$globals` segment and IL emission goes
420
// through the existing `gen_globals_class_reference` path.
421
_globals_owner_namespace(owner: Symbols.Symbol) -> Symbols.NAMESPACE? is
422
if !(owner.name =~ "$globals") \/ !isa Symbols.Classy(owner) then
423
return null
424
fi
425
426
let classy_owner = owner.owner
427
428
if !classy_owner? then
429
return null
430
fi
431
432
return _namespaces.try_find_namespace(".{classy_owner.name}")
433
si
434
435
add_members(result: Symbols.Scoped, type: TYPE) is
436
let properties = MAP()
437
let indexers = SET()
438
let indexer_accessors = SET()
439
let methods = LIST()
440
441
for p in type.get_properties() do
442
add_property(result, type, properties, indexers, indexer_accessors, p)
443
od
444
445
for t in type.get_members(cast BindingFlags(64 + 4 + 32 + 16 + 8)) do
446
add_member(result, type, properties, indexers, indexer_accessors, methods, t)
447
od
448
449
if methods.count > 0 then
450
_ambiguous_method_checker.check(result, methods)
451
452
for m in methods do
453
// An intrinsic operator is registered on the types of
454
// its operands rather than on the carrier that declared
455
// it.
456
if let home: Symbols.Classy = m.function.owner /\ isa Symbols.INNATE_FUNCTION(m.function) then
457
home.add_member(m.function)
458
459
for other in Symbols.TYPE_HOMED_OPERATORS.homes(m.function) do
460
if other != home then
461
other.add_member(m.function)
462
fi
463
od
464
else
465
result.add_member(m.function)
466
fi
467
od
468
fi
469
470
inherit_default_trait_members(result, type)
471
si
472
473
// Copy any concrete (`is_default_trait_method`) method from
474
// a trait the class implements into the class itself, unless
475
// the class declares a method with the same signature.
476
//
477
// Why this is its own pass: `mark_overrides_resolved()` runs
478
// on reflected concrete classes in `create_class` so the
479
// full `SYMBOL_INHERITANCE_RESOLVER` skips them — its
480
// override-clash and abstract-method checks would otherwise
481
// fire on .NET-side diamonds (`IList` × `IReadOnlyList`,
482
// `MemberInfo.Module`, …) that the assembly's IL has already
483
// resolved via explicit interface implementation but that
484
// ghul's reflection layer can't see. Pulling down just the
485
// trait defaults keeps the assumption-of-trust for
486
// overrides while still making `find_member("filter")` work
487
// on a reflected class that inherits `filter` from `Pipe[T]`.
488
inherit_default_trait_members(result: Symbols.Scoped, type: TYPE) is
489
if !isa Symbols.Classy(result) then
490
return
491
fi
492
493
let classy = result
494
495
for i in 0..classy.ancestors.count do
496
let scope = classy.get_ancestor(i)?.scope
497
498
if !scope? then
499
continue
500
fi
501
502
for member in scope.symbols do
503
inherit_default_trait_member(classy, member)
504
od
505
od
506
si
507
508
inherit_default_trait_member(into: Symbols.Classy, member: Symbols.Symbol) is
509
if isa Symbols.FUNCTION_GROUP(member) then
510
let group = member
511
512
for function in group.functions do
513
if function.is_default_trait_method /\ !declares_override_of(into, function) then
514
into.add_member(function)
515
fi
516
od
517
elif isa Symbols.Function(member) then
518
let function = member
519
520
if function.is_default_trait_method /\ !declares_override_of(into, function) then
521
into.add_member(function)
522
fi
523
elif let property: Symbols.Property = member then
524
// A property with a default body is inherited the same way,
525
// unless the class declares one of that name itself.
526
if let getter = property.read_function /\ getter.is_default_trait_method /\ !into.find_direct(property.name)? then
527
into.add_member(property)
528
fi
529
fi
530
si
531
532
// Whether the class itself declares a method with the default
533
// method's signature: that method is the class's implementation
534
// of it, so the default is not inherited beside it as a second
535
// candidate for the same call.
536
declares_override_of(into: Symbols.Classy, default_method: Symbols.Function) -> bool static is
537
let existing = into.find_direct(default_method.name)
538
539
if let group: Symbols.FUNCTION_GROUP = existing then
540
return group.functions |> any(f => _overrides(into, f, default_method))
541
elif let single: Symbols.Function = existing then
542
return _overrides(into, single, default_method)
543
fi
544
545
return false
546
si
547
548
_overrides(into: Symbols.Classy, candidate: Symbols.Function, default_method: Symbols.Function) -> bool static =>
549
candidate != default_method /\
550
candidate.owner == into /\
551
candidate.override_class =~ default_method.override_class
552
553
add_member(
554
owner: Symbols.Scoped,
555
type: TYPE,
556
properties: MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS],
557
indexers: SET[System.Reflection.MethodInfo],
558
indexer_accessors: SET[System.Reflection.MethodInfo],
559
methods: LIST[(function: Symbols.Function, method_info: MethodInfo)],
560
member: MemberInfo
561
) -> Symbols.Symbol?
562
is
563
let member_type = member.member_type
564
565
if member_type == MemberTypes.CONSTRUCTOR then
566
add_constructor(owner, cast ConstructorInfo?(member)!)
567
elif member_type == MemberTypes.FIELD then
568
add_field(owner, type, cast FieldInfo?(member)!)
569
elif member_type == MemberTypes.METHOD then
570
add_method(owner, type, properties, indexers, indexer_accessors, methods, cast MethodInfo?(member)!)
571
fi
572
return null
573
si
574
575
add_constructor(owner: Symbols.Scoped, method: ConstructorInfo) is
576
let result: Symbols.Function mut
577
let location = Source.LOCATION.reflected
578
579
if !method.is_public /\ !_is_session_member(method) then
580
return
581
fi
582
583
if method.is_static then
584
result = Symbols.STATIC_METHOD(location, location, owner, "init", owner)
585
elif owner.is_value_type then
586
result = Symbols.STRUCT_METHOD(location, location, owner, "init", owner)
587
elif method.is_abstract then
588
result = Symbols.ABSTRACT_METHOD(location, location, owner, "init", owner)
589
else
590
result = Symbols.INSTANCE_METHOD(location, location, owner, "init", owner)
591
fi
592
593
result.il_name_override = method.name
594
595
let argument_names = LIST()
596
let argument_types = LIST()
597
let argument_defaults = Collections.LIST[string?]()
598
let argument_reads = Collections.LIST[bool]()
599
let argument_writes = Collections.LIST[bool]()
600
let argument_is_pack = Collections.LIST[bool]()
601
let argument_pack_depth = Collections.LIST[int]()
602
let spread_argument_index mut = -1
603
let has_direction_override mut = false
604
605
for a in method.get_parameters() do
606
argument_names.add(a.name ?? "")
607
let arg_attributes = a.get_custom_attributes_data()
608
609
let pack_depth = _argument_pack_depth(arg_attributes)
610
611
argument_is_pack.add(pack_depth >= 0)
612
argument_pack_depth.add(if pack_depth >= 0 then pack_depth else 0 fi)
613
614
if _has_attribute_named(arg_attributes, ARGUMENT_SPREAD_ATTRIBUTE_NAME) then
615
spread_argument_index = argument_names.count - 1
616
fi
617
618
argument_types.add(
619
NULLABILITY.resolve(
620
_with_tuple_element_names(_type_mapper.get_type(a.parameter_type), arg_attributes),
621
arg_attributes,
622
method,
623
a.parameter_type
624
)!
625
)
626
argument_defaults.add(_default_for_parameter(a))
627
argument_reads.add(!(a.is_out /\ !a.is_in))
628
argument_writes.add(!(a.is_in /\ !a.is_out))
629
if a.is_out \/ a.is_in then
630
has_direction_override = true
631
fi
632
od
633
634
result.argument_is_pack = argument_is_pack
635
result.argument_pack_depth = argument_pack_depth
636
result.spread_argument_index = spread_argument_index
637
638
let return_pack_depth = _pack_depth(method.get_custom_attributes_data(), RETURN_PACK_ATTRIBUTE_NAME)
639
640
if return_pack_depth >= 0 then
641
result.return_pack_depth = return_pack_depth
642
fi
643
result.argument_names = argument_names
644
result.arguments = argument_types
645
result.argument_defaults = argument_defaults
646
647
if has_direction_override then
648
result.set_argument_directions(argument_reads, argument_writes)
649
fi
650
651
result.return_type = _type_mapper.get_type(_void_type)
652
result.mark_pack_type_parameters()
653
654
// A public parameterless constructor — what a `new()`
655
// type-parameter constraint requires of a type argument.
656
if argument_types.count == 0 /\ isa Symbols.Classy(owner) then
657
(cast Symbols.Classy(owner)).has_parameterless_constructor = true
658
fi
659
660
owner.add_member(result)
661
si
662
663
add_field(owner: Symbols.Scoped, type: TYPE, `field: FieldInfo) is
664
if !`field.is_public /\ !_is_session_member(`field) then
665
return
666
fi
667
668
if type.is_enum /\ !`field.is_special_name then
669
let raw_value = `field.get_raw_constant_value()
670
let enum_member =
671
Symbols.ENUM_STRUCT_MEMBER(
672
Source.LOCATION.reflected,
673
cast Symbols.ENUM_STRUCT?(owner)!,
674
_type_name_map.get_constant_name(owner.qualified_name, `field.name, false, `field.declaring_type ?? type),
675
if raw_value? then raw_value.to_string() ?? "" else "" fi
676
)
677
678
owner.add_member(enum_member)
679
680
return
681
fi
682
683
let result: Symbols.Field mut
684
let location = Source.LOCATION.reflected
685
686
let field_name = `field.name
687
688
let name: string? mut = null
689
690
if field_name.length == 5 /\ `field.name.starts_with("Item") /\ type.is_generic_type then
691
let unspecialized_type = type.get_generic_type_definition()
692
693
let is_tuple mut = false
694
695
for t in _tuple_types do
696
if unspecialized_type == t then
697
is_tuple = true
698
break
699
fi
700
od
701
702
if is_tuple then
703
name = "{(cast int(field_name[field_name.length-1]) - 49)}"
704
fi
705
fi
706
707
if !name? then
708
name = _type_name_map.get_member_name(owner.qualified_name, `field.name, false, false, `field.declaring_type ?? type)
709
fi
710
711
// For inherited fields, the declaring symbol must point at the base
712
// class, not the derived class — otherwise the IL fieldref says
713
// "Foo::SubjectField" instead of "FooBase`2<...>::SubjectField" and
714
// the runtime can't find the field.
715
let declaring_symbol: Symbols.Symbol mut = owner
716
let dt = `field.declaring_type
717
if dt? /\ dt != type then
718
declaring_symbol = _type_mapper.get_type(dt).symbol
719
fi
720
721
if `field.is_static then
722
let globals_namespace = _globals_owner_namespace(owner)
723
let constant = REFLECTED_CONSTANTS.value_of(`field)
724
725
if globals_namespace? then
726
let global = Symbols.GLOBAL_VARIABLE(location, globals_namespace, name)
727
global.il_carrier = cast Symbols.Classy?(owner)!
728
result = global
729
elif isa ReflectedConstant.ABSENT(constant) then
730
result = Symbols.CONSTANT_FIELD(location, declaring_symbol, name, null)
731
elif let value: ReflectedConstant.VALUE = constant then
732
result = Symbols.CONSTANT_FIELD(location, declaring_symbol, name, value.text)
733
else
734
result = Symbols.STATIC_FIELD(location, declaring_symbol, name)
735
fi
736
elif type.is_value_type then
737
result = Symbols.STRUCT_FIELD(location, declaring_symbol, name)
738
else
739
result = Symbols.INSTANCE_FIELD(location, declaring_symbol, name)
740
fi
741
742
result.il_name_override = `field.name
743
744
let field_attributes = `field.get_custom_attributes_data()
745
746
// The attribute check alone misses a field imported from an
747
// assembly built by a compiler that predates it. A field name
748
// is never operator syntax - only a function can be named with
749
// one - so a leading `$` on a field carries no ambiguity and is
750
// a safe legacy fallback, the same shape as
751
// GLOBALS_CARRIER.is_carrier's attribute-or-name check.
752
if _has_compiler_generated_attribute(field_attributes) \/ name.starts_with('$') then
753
result.mark_synthesized()
754
fi
755
756
result.set_type(
757
NULLABILITY.resolve(
758
_with_tuple_element_names(_type_mapper.get_type(`field.field_type), field_attributes),
759
field_attributes,
760
`field,
761
`field.field_type
762
)!
763
)
764
765
// Same problem as add_method: inherited fields on a constructed
766
// generic base have their declared type returned by reflection in
767
// *substituted* form. Recover the open form via the open base type
768
// and stash it as unspecialized_type; gen_reference prefers it.
769
if dt? /\ dt != type /\ dt.is_generic_type /\ !dt.is_generic_type_definition then
770
let open_type = dt.get_generic_type_definition()
771
let open_field: FieldInfo? mut = null
772
773
for m in open_type.get_members(cast BindingFlags(2 + 4 + 8 + 16 + 32)) do
774
if m.member_type == MemberTypes.FIELD then
775
let candidate = cast FieldInfo?(m)!
776
if candidate.metadata_token == `field.metadata_token then
777
open_field = candidate
778
break
779
fi
780
fi
781
od
782
783
if open_field? then
784
result.unspecialized_type = _type_mapper.get_type(open_field.field_type)
785
fi
786
fi
787
788
owner.add_member(result)
789
si
790
791
// `family` and `famorassem` are deliberately absent: a subclass in
792
// another assembly is entitled to a protected member.
793
_is_reachable_from_other_assembly(method: MethodInfo) -> bool =>
794
!(method.is_private \/ method.is_assembly \/ method.is_family_and_assembly) \/
795
_is_session_member(method)
796
797
// A member an earlier cell of the session declared, other than one
798
// the compiler generated for it: the session is one assembly, so
799
// its assembly-private members are as visible as they would be in
800
// one file, and the ordinary access rules decide who may use them.
801
_is_session_member(member: System.Reflection.MemberInfo) -> bool is
802
let declaring = member.declaring_type
803
804
return
805
declaring? /\ !member.name.contains('$') /\
806
IoC.CONTAINER.instance.assemblies.can_reach_privates_of(declaring.assembly)
807
si
808
809
// The name of the type's indexer property, as named by
810
// `DefaultMemberAttribute`. A type with no marker cannot have
811
// an indexer, but the name an indexer would take is still the
812
// right answer for the accessor-naming comparison.
813
_default_member_name(type: TYPE) -> string is
814
for attr in type.get_custom_attributes_data() do
815
if attr.attribute_type.full_name =~ "System.Reflection.DefaultMemberAttribute" then
816
for argument in attr.constructor_arguments do
817
let name = cast string?(argument.value)
818
819
if name? then
820
return name
821
fi
822
od
823
fi
824
od
825
826
return Symbols.INDEXER_NAMES.property_name
827
si
828
829
add_property(
830
owner: Symbols.Scoped,
831
type: TYPE,
832
properties: MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS],
833
indexers: SET[System.Reflection.MethodInfo],
834
indexer_accessors: SET[System.Reflection.MethodInfo],
835
property: PropertyInfo
836
) is
837
let index_parameters = property.get_index_parameters()
838
839
if index_parameters.count > 0 then
840
// An indexed property is *the* indexer when the declaring
841
// type nominates it. A type can carry others that indexing
842
// must not reach — but one already named `Item` was
843
// reachable before the nomination was consulted, so it
844
// stays reachable whether or not it is the nominated one.
845
let is_indexer =
846
property.name =~ _default_member_name(property.declaring_type ?? type) \/
847
property.name =~ Symbols.INDEXER_NAMES.property_name
848
849
let getter = property.get_get_method()
850
if getter? then
851
indexers.add(getter)
852
853
if is_indexer then
854
indexer_accessors.add(getter)
855
fi
856
fi
857
858
let setter = property.get_set_method()
859
if setter? then
860
indexers.add(setter)
861
862
if is_indexer then
863
indexer_accessors.add(setter)
864
fi
865
fi
866
867
return
868
fi
869
870
if property.name =~ "Current" then
871
if property.declaring_type == _type_source.get_type("System.Collections.IEnumerator") then
872
return
873
fi
874
fi
875
876
let result: Symbols.Property mut
877
let name =
878
_ghul_name_override(property) ??
879
_type_name_map.get_member_name(owner.qualified_name, property.name, false, property.is_special_name, property.declaring_type ?? type)
880
881
let getter = property.get_method
882
let setter = property.set_method
883
884
if setter? /\ !getter? then
885
return
886
fi
887
888
let is_static mut = false
889
890
if getter? /\ getter.is_static then
891
is_static = true
892
elif setter? /\ setter.is_static then
893
is_static = true
894
fi
895
896
if is_static then
897
let globals_namespace = _globals_owner_namespace(owner)
898
if globals_namespace? then
899
result = Symbols.GLOBAL_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, globals_namespace, name, setter?)
900
else
901
result = Symbols.STATIC_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, name, setter?, false)
902
fi
903
else
904
result = Symbols.INSTANCE_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, name, setter?, false)
905
fi
906
907
result.set_type(
908
_with_property_tuple_element_names(
909
NULLABILITY.resolve(
910
_type_mapper.get_type(property.property_type),
911
property.get_custom_attributes_data(),
912
property,
913
property.property_type
914
)!,
915
property
916
)
917
)
918
result.il_name_override = property.name
919
920
// Not CompilerGeneratedAttribute here: the C# compiler puts it
921
// on a record's public surface too (EqualityContract is
922
// protected, but a member reachable through inheritance still
923
// has to stay visible where it's reachable at all), so it
924
// does not distinguish hidden from ordinary API the way it
925
// does for a field or a type. is_special_name is the CLR's
926
// own narrower signal for a property that is not ordinary
927
// API.
928
if property.is_special_name then
929
result.mark_synthesized()
930
fi
931
932
owner.add_member(result)
933
934
let property_details = PROPERTY_DETAILS(result, property.get_get_method(), property.get_set_method())
935
936
if getter? then
937
properties[getter] = property_details
938
fi
939
940
if setter? then
941
properties[setter] = property_details
942
fi
943
si
944
945
add_method(
946
owner: Symbols.Scoped,
947
type: TYPE,
948
properties: MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS],
949
indexers: SET[System.Reflection.MethodInfo],
950
indexer_accessors: SET[System.Reflection.MethodInfo],
951
methods: LIST[(function: Symbols.Function, method_info: MethodInfo)],
952
method: MethodInfo
953
) is
954
add_method(owner, type, properties, indexers, indexer_accessors, methods, method, null)
955
si
956
957
// `force_name`, when non-null, supplies the ghūl name instead of
958
// deriving one from the method. Used to surface a single
959
// reflected method under a second name: the derivation is
960
// skipped so the alias cannot re-trigger the side effects some
961
// of its branches carry.
962
//
963
// An alias of an abstract interface member is built as a
964
// default trait method rather than an abstract one. Only the
965
// primary name carries the obligation to implement, so a type
966
// satisfies the interface by supplying that one member, while
967
// both names remain callable.
968
add_method(
969
owner: Symbols.Scoped,
970
type: TYPE,
971
properties: MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS],
972
indexers: SET[System.Reflection.MethodInfo],
973
indexer_accessors: SET[System.Reflection.MethodInfo],
974
methods: LIST[(function: Symbols.Function, method_info: MethodInfo)],
975
method: MethodInfo,
976
force_name: string?
977
) is
978
if !_is_reachable_from_other_assembly(method) then
979
return
980
fi
981
982
let result: Symbols.Function mut
983
let location = Source.LOCATION.reflected
984
985
let name: string mut
986
let hide_from_operator_resolution mut = false
987
988
// Set when this method is declared under a name the compiler
989
// itself picked ($get_iterator below) rather than one the
990
// reflected metadata names it, so it never appears as a
991
// second, ordinarily-callable member of the reflected type.
992
let is_synthesized_name mut = false
993
994
// Set when the method is also to be reached under its
995
// ordinary de-camelled name, alongside the operator
996
// spelling picked below.
997
let alias_name: string? mut = null
998
999
let property_details: PROPERTY_DETAILS mut
1000
1001
if force_name? then
1002
name = force_name
1003
elif properties.try_get_value(method, property_details ref) then
1004
name =
1005
_ghul_name_override(method) ??
1006
_type_name_map.get_member_name(owner.qualified_name, method.name, method.is_private, true, method.declaring_type ?? type)
1007
is_synthesized_name = true
1008
elif indexer_accessors.contains(method) then
1009
// Declared only under the indexer names, whatever
1010
// metadata calls the accessor. An indexer is reached by
1011
// indexing and by nothing else, so the metadata spelling
1012
// is deliberately not surfaced as a second member.
1013
name = Symbols.INDEXER_NAMES.accessor(method.name)
1014
elif method.name =~ "Equals" /\ _is_self_relational_interface_target(type, method, _equatable_open_type, _bool_type) then
1015
name = "=~"
1016
alias_name = _plain_member_name(owner, type, indexers, method)
1017
hide_from_operator_resolution = type.full_name? /\ _innate_equality_type_names.contains(type.full_name)
1018
elif method.name =~ "CompareTo" /\ _is_self_relational_interface_target(type, method, _comparable_open_type, _int_type) then
1019
name = "<>"
1020
alias_name = _plain_member_name(owner, type, indexers, method)
1021
hide_from_operator_resolution = type.full_name? /\ _innate_order_type_names.contains(type.full_name)
1022
elif method.name =~ "get_Current" /\ method.declaring_type == _type_source.get_type("System.Collections.IEnumerator") then
1023
return
1024
elif method.name =~ "GetEnumerator" then
1025
if method.declaring_type == _type_source.get_type("System.Collections.IEnumerable") then
1026
return
1027
fi
1028
1029
name = "$get_iterator"
1030
is_synthesized_name = true
1031
1032
let iterator_property = Symbols.INSTANCE_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, "iterator", false, false)
1033
iterator_property.set_type(_type_mapper.get_type(method.return_type))
1034
1035
owner.add_member(iterator_property)
1036
1037
property_details = PROPERTY_DETAILS(iterator_property, method, null)
1038
elif
1039
method.is_static /\
1040
let mdt0 = method.declaring_type in mdt0? /\
1041
_generic_math_operator_name(method.name)? /\
1042
(
1043
(method.is_virtual /\ mdt0.is_interface) \/
1044
_declares_own_operators(mdt0)
1045
)
1046
then
1047
name = _generic_math_operator_name(method.name)!
1048
else
1049
name = _plain_member_name(owner, type, indexers, method)
1050
1051
// A .NET special name - an operator, an event accessor -
1052
// is reached through its dedicated syntax, never under
1053
// any name, whatever assembly declared it. The flag on
1054
// the row is the upstream fact; the $ spelling
1055
// get_member_name derives from it is presentation.
1056
//
1057
// The test repeats the two carve-outs _plain_member_name
1058
// applies, so the verdict and the name stay in step: an
1059
// indexer accessor is named under its indexer names, and
1060
// a ghūl name override replaces the derived spelling
1061
// outright - either way there is no $ to hide.
1062
if
1063
method.is_special_name /\
1064
!indexers.contains(method) /\
1065
!_ghul_name_override(method)?
1066
then
1067
is_synthesized_name = true
1068
fi
1069
fi
1070
1071
let declaring_symbol: Symbols.Symbol mut = owner
1072
1073
let mdt = method.declaring_type
1074
if mdt? /\ mdt != type then
1075
declaring_symbol = _type_mapper.get_type(mdt).symbol
1076
fi
1077
1078
let intrinsic_operation = _intrinsic_operation(method.get_custom_attributes_data())
1079
1080
if intrinsic_operation? then
1081
let globals_namespace = if method.is_static then _globals_owner_namespace(owner) else null fi
1082
1083
if globals_namespace? then
1084
// Innate calls lower to opcodes inline and never emit a
1085
// methodref, so an innate function needs no il_assembly_name.
1086
let home = _intrinsic_home(name, method)
1087
1088
if home? then
1089
result = Symbols.INNATE_FUNCTION(location, home, name, home, intrinsic_operation)
1090
else
1091
result = Symbols.INNATE_FUNCTION(location, globals_namespace, name, globals_namespace, intrinsic_operation)
1092
fi
1093
else
1094
result = Symbols.INNATE_METHOD(location, declaring_symbol, name, owner, intrinsic_operation)
1095
fi
1096
elif method.is_static then
1097
let globals_namespace = _globals_owner_namespace(owner)
1098
if globals_namespace? then
1099
let global = Symbols.GLOBAL_FUNCTION(location, location, globals_namespace, name, globals_namespace)
1100
global.il_carrier = cast Symbols.Classy?(owner)!
1101
result = global
1102
else
1103
let static_method = Symbols.STATIC_METHOD(location, location, declaring_symbol, name, owner)
1104
1105
// A static virtual/abstract interface member -
1106
// .NET's generic-math feature. Reachable through a
1107
// bound type parameter, which needs the
1108
// `constrained.` call shape; an ordinary reflected
1109
// static method never is.
1110
static_method.is_static_interface_virtual = method.is_virtual /\ mdt? /\ mdt.is_interface
1111
1112
static_method.cannot_be_overridden = !static_method.is_static_interface_virtual
1113
1114
result = static_method
1115
fi
1116
elif mdt? /\ mdt.is_value_type then
1117
result = Symbols.STRUCT_METHOD(location, location, declaring_symbol, name, owner)
1118
1119
result.cannot_be_overridden = true
1120
elif method.is_abstract /\ !force_name? then
1121
result = Symbols.ABSTRACT_METHOD(location, location, declaring_symbol, name, owner)
1122
elif mdt? /\ mdt.is_interface then
1123
result = Symbols.DEFAULT_TRAIT_METHOD(location, location, declaring_symbol, name, owner)
1124
else
1125
result = Symbols.INSTANCE_METHOD(location, location, declaring_symbol, name, owner)
1126
1127
// A non-virtual or final CLR method binds to exactly
1128
// one body, and so does any method of a sealed owner:
1129
// no subclass exists to override it in.
1130
result.cannot_be_overridden =
1131
!method.is_virtual \/ method.is_final \/ (mdt? /\ mdt.is_sealed)
1132
fi
1133
1134
if method.is_generic_method then
1135
result.is_generic = true
1136
1137
let generic_argument_names = LIST()
1138
let generic_arguments = LIST()
1139
let generic_argument_constraint_kinds = Collections.LIST[Symbols.TypeParameterConstraintKind]()
1140
let generic_argument_has_constructor_constraint = Collections.LIST[bool]()
1141
let generic_argument_type_bounds = Collections.LIST[Collections.List[Semantic.Types.Type]]()
1142
1143
let ga = method.get_generic_arguments()
1144
1145
for p in ga do
1146
generic_argument_names.add(p.name)
1147
generic_arguments.add(_type_mapper.get_type(p))
1148
generic_argument_constraint_kinds.add(_type_mapper.generic_parameter_constraint_kind(p))
1149
generic_argument_has_constructor_constraint.add(_type_mapper.generic_parameter_has_constructor_constraint(p))
1150
generic_argument_type_bounds.add(type_bounds(p))
1151
od
1152
1153
result.generic_argument_names = generic_argument_names
1154
result.generic_arguments = generic_arguments
1155
result.generic_argument_constraint_kinds = generic_argument_constraint_kinds
1156
result.generic_argument_has_constructor_constraint = generic_argument_has_constructor_constraint
1157
1158
if generic_argument_type_bounds.count > 0 /\ generic_argument_type_bounds |> any(l => l.count > 0) then
1159
result.generic_argument_type_bounds = generic_argument_type_bounds
1160
fi
1161
fi
1162
1163
result.il_name_override = method.name
1164
result.is_hidden_from_operator_resolution = hide_from_operator_resolution
1165
1166
// Not CompilerGeneratedAttribute on a foreign assembly: the C#
1167
// compiler puts it on a record's ToString, Equals, GetHashCode,
1168
// Deconstruct and equality operators too - ordinary callable
1169
// API, not hidden members - so unlike a field or a type it does
1170
// not distinguish the two there. On a ghūl-compiled assembly it
1171
// is authoritative: the emitter marks what it synthesises -
1172
// property accessors, lambda bodies compiled onto their
1173
// enclosing type as $anon members, state-machine frame members,
1174
// the Object.Equals bridge - and marks nothing a user wrote,
1175
// since an authored member is either public and unmarked or
1176
// invisible past the assembly boundary. Reading it recognises
1177
// those shapes whatever name they travel under, and leaves an
1178
// author's own `$` spelling alone.
1179
if
1180
is_synthesized_name \/
1181
(
1182
_type_name_map.is_ghul_compiled(method.declaring_type ?? type) /\
1183
_has_compiler_generated_attribute(method.get_custom_attributes_data())
1184
)
1185
then
1186
result.mark_synthesized()
1187
fi
1188
1189
if method.is_family \/ method.is_family_or_assembly then
1190
result.mark_imported_protected()
1191
fi
1192
1193
if _has_pure_attribute(method.get_custom_attributes_data()) then
1194
// the marker carries the declared-pure contract: the
1195
// function is trusted store-free, and local overrides
1196
// are held to the pure-override contract here in the
1197
// overriding assembly
1198
result.mark_declared_pure()
1199
fi
1200
1201
if _has_stable_attribute(method.get_custom_attributes_data()) then
1202
// the marker carries the declared-stable contract of
1203
// the property this accessor reads: facts narrowed
1204
// through it count as backed, and local overrides are
1205
// held to the stable-override contract here in the
1206
// overriding assembly
1207
result.mark_declared_stable()
1208
fi
1209
1210
let argument_names = LIST()
1211
let argument_types = LIST()
1212
let argument_defaults = Collections.LIST[string?]()
1213
let argument_reads = Collections.LIST[bool]()
1214
let argument_writes = Collections.LIST[bool]()
1215
let argument_is_pack = Collections.LIST[bool]()
1216
let argument_pack_depth = Collections.LIST[int]()
1217
let spread_argument_index mut = -1
1218
let has_direction_override mut = false
1219
1220
for a in method.get_parameters() do
1221
argument_names.add(a.name ?? "")
1222
let arg_attributes = a.get_custom_attributes_data()
1223
1224
let pack_depth = _argument_pack_depth(arg_attributes)
1225
1226
argument_is_pack.add(pack_depth >= 0)
1227
argument_pack_depth.add(if pack_depth >= 0 then pack_depth else 0 fi)
1228
1229
if _has_attribute_named(arg_attributes, ARGUMENT_SPREAD_ATTRIBUTE_NAME) then
1230
spread_argument_index = argument_names.count - 1
1231
fi
1232
1233
argument_types.add(
1234
_with_pure_function(
1235
NULLABILITY.resolve(
1236
_with_tuple_element_names(_type_mapper.get_type(a.parameter_type), arg_attributes),
1237
arg_attributes,
1238
method,
1239
a.parameter_type
1240
)!,
1241
arg_attributes
1242
)
1243
)
1244
argument_defaults.add(_default_for_parameter(a))
1245
argument_reads.add(!(a.is_out /\ !a.is_in))
1246
argument_writes.add(!(a.is_in /\ !a.is_out))
1247
if a.is_out \/ a.is_in then
1248
has_direction_override = true
1249
fi
1250
od
1251
1252
result.argument_is_pack = argument_is_pack
1253
result.argument_pack_depth = argument_pack_depth
1254
result.spread_argument_index = spread_argument_index
1255
1256
let return_pack_depth = _pack_depth(method.get_custom_attributes_data(), RETURN_PACK_ATTRIBUTE_NAME)
1257
1258
if return_pack_depth >= 0 then
1259
result.return_pack_depth = return_pack_depth
1260
fi
1261
result.argument_names = argument_names
1262
result.arguments = argument_types
1263
result.argument_defaults = argument_defaults
1264
1265
if has_direction_override then
1266
result.set_argument_directions(argument_reads, argument_writes)
1267
fi
1268
1269
let return_attributes = method.return_parameter.get_custom_attributes_data()
1270
result.return_type =
1271
_with_pure_function(
1272
NULLABILITY.resolve(
1273
_with_tuple_element_names(
1274
_type_mapper.get_type(method.return_type),
1275
return_attributes
1276
),
1277
return_attributes,
1278
method,
1279
method.return_type
1280
)!,
1281
return_attributes
1282
)
1283
1284
result.mark_pack_type_parameters()
1285
1286
// When inheriting a method from a constructed-generic base in another
1287
// assembly, reflection returns parameter and return types in their
1288
// *substituted* form (e.g. !0 surfaces as System.Object when the base
1289
// is FooBase<object,...>). The CLR requires the methodref signature
1290
// to use the open-generic form (!0, !!0). Recover the open form via
1291
// the open base type and stash it as unspecialized_*; the IL emitter
1292
// already prefers unspecialized_* when present.
1293
let dt = method.declaring_type
1294
if dt? /\ dt != type /\ dt.is_generic_type /\ !dt.is_generic_type_definition then
1295
let open_type = dt.get_generic_type_definition()
1296
let open_method: MethodInfo? mut = null
1297
1298
for m in open_type.get_members(cast BindingFlags(2 + 4 + 8 + 16 + 32)) do
1299
if m.member_type == MemberTypes.METHOD then
1300
let candidate = cast MethodInfo?(m)!
1301
if candidate.metadata_token == method.metadata_token then
1302
open_method = candidate
1303
break
1304
fi
1305
fi
1306
od
1307
1308
if open_method? then
1309
let unspec_arg_types = LIST()
1310
for a in open_method.get_parameters() do
1311
unspec_arg_types.add(_type_mapper.get_type(a.parameter_type))
1312
od
1313
result.unspecialized_arguments = unspec_arg_types
1314
result.unspecialized_return_type = _type_mapper.get_type(open_method.return_type)
1315
fi
1316
fi
1317
1318
// property_details is only set for a property accessor method:
1319
// most of the branches above never touch it, so it reaches here
1320
// unassigned on every other path.
1321
@suppress("presence-test-non-optional", "definite-assignment")
1322
if property_details? then
1323
let accessor_method_name = method.name
1324
1325
if method.equals(property_details.dotnet_get_method) then
1326
property_details.ghul_property.read_function = result
1327
property_details.ghul_property.read_function_il_name_override = method.name
1328
elif method.equals(property_details.dotnet_set_method) then
1329
property_details.ghul_property.assign_function = result
1330
property_details.ghul_property.assign_function_il_name_override = method.name
1331
fi
1332
fi
1333
1334
methods.add((result, method))
1335
1336
// `IEquatable[T].Equals` and `IComparable[T].CompareTo` are
1337
// reached as the operators `=~` and `<>`, and also under the
1338
// ordinary de-camelled name, so a call spelled `equals` or
1339
// `compare_to` resolves as well. Both symbols carry the same
1340
// `il_name_override`, so either spelling emits a call to the
1341
// one underlying method.
1342
if alias_name? then
1343
add_method(owner, type, properties, indexers, indexer_accessors, methods, method, alias_name)
1344
fi
1345
si
1346
1347
// The type an intrinsic operator is registered on: the type of its
1348
// first operand, for the operators a type declares for itself.
1349
// Null for every other intrinsic, which stays global.
1350
_intrinsic_home(name: string, method: MethodInfo) -> Symbols.Classy? is
1351
if !Symbols.TYPE_HOMED_OPERATORS.contains(name) then
1352
return null
1353
fi
1354
1355
let parameters = method.get_parameters()
1356
1357
if parameters.count == 0 then
1358
return null
1359
fi
1360
1361
if let first: Semantic.Types.NAMED = _type_mapper.get_type(parameters[0].parameter_type) then
1362
return cast Symbols.Classy?(first.symbol)
1363
fi
1364
1365
return null
1366
si
1367
1368
// Whether a type's public static operator methods import under
1369
// their operator spelling. The scalar types do not: their
1370
// operators are the intrinsics, and a reflected `op_Addition` on
1371
// `System.Decimal` or `System.IntPtr` would be a second candidate
1372
// for the same operands.
1373
_declares_own_operators(type: TYPE) -> bool =>
1374
!type.is_interface /\ !_intrinsic_operator_types.contains(type)
1375
1376
// The ghūl spelling of a CLR operator-method name, or null for a
1377
// method that is not one. Consulted for a static virtual interface
1378
// member and for a public static operator on an ordinary type; a
1379
// scalar type's reflected operator keeps its mangled name, since
1380
// the intrinsic is that type's operator.
1381
//
1382
// The arithmetic, bitwise and shift operators. The comparison and
1383
// equality operators are deliberately absent: ghūl derives those
1384
// from `<>` and `=~`, and a bound that supplies these operators
1385
// supplies those too, so importing `op_LessThan` as `<` would
1386
// give the same expression a second, unrelated route to a
1387
// different member.
1388
_generic_math_operator_name(dotnet_name: string) -> string? =>
1389
case dotnet_name
1390
when "op_Addition" then "+"
1391
when "op_Subtraction" then "-"
1392
when "op_Multiply" then "*"
1393
when "op_Division" then "/"
1394
when "op_Modulus" then "%"
1395
when "op_UnaryNegation" then "-"
1396
when "op_BitwiseAnd" then "&"
1397
when "op_BitwiseOr" then "|"
1398
when "op_ExclusiveOr" then "^"
1399
when "op_OnesComplement" then "\\"
1400
when "op_LeftShift" then "<<"
1401
when "op_RightShift" then ">>"
1402
when "op_UnsignedRightShift" then ">>>"
1403
else null
1404
esac
1405
1406
_plain_member_name(
1407
owner: Symbols.Scoped,
1408
type: TYPE,
1409
indexers: SET[System.Reflection.MethodInfo],
1410
method: MethodInfo
1411
) -> string =>
1412
_ghul_name_override(method) ??
1413
_type_name_map.get_member_name(
1414
owner.qualified_name,
1415
method.name,
1416
method.is_private,
1417
method.is_special_name /\ !indexers.contains(method),
1418
method.declaring_type ?? type
1419
)
1420
1421
get_assembly_name(type: TYPE) -> string is
1422
let assembly = type.assembly
1423
1424
let result: string mut
1425
1426
if _assembly_names.try_get_value(assembly, result ref) then
1427
return result
1428
fi
1429
1430
result = assembly.get_name().name ?? ""
1431
1432
_assembly_names.add(assembly, result)
1433
1434
return result
1435
si
1436
1437
// C# records TupleElementNamesAttribute on the property itself;
1438
// a ghūl assembly built before the property carried it holds
1439
// the names only on the getter's `.param [0]`, which reflects
1440
// as the return parameter rather than as a method attribute.
1441
// Every reflected property reaches here, so neither carrier is
1442
// read until the type is known to be a tuple.
1443
_with_property_tuple_element_names(
1444
type: Types.Type,
1445
property: PropertyInfo
1446
) -> Types.Type is
1447
let names =
1448
TUPLE_ELEMENT_NAMES.read(property.get_custom_attributes_data()) ??
1449
(if let getter = property.get_get_method() then
1450
TUPLE_ELEMENT_NAMES.read(getter.return_parameter.get_custom_attributes_data())
1451
else
1452
null
1453
fi)
1454
1455
if let found = names then
1456
return TUPLE_ELEMENT_NAMES.apply(type, found)
1457
fi
1458
1459
return type
1460
si
1461
1462
// If `attributes` carry a TupleElementNamesAttribute, return
1463
// `type` with the recovered names put back on every tuple in it,
1464
// however deep; otherwise return `type` unchanged. The reflected
1465
// ValueTuple type itself has no names.
1466
_with_tuple_element_names(
1467
type: Types.Type,
1468
attributes: Collections.Iterable[System.Reflection.CustomAttributeData]
1469
) -> Types.Type is
1470
let names = TUPLE_ELEMENT_NAMES.read(attributes)
1471
1472
if !names? then
1473
return type
1474
fi
1475
1476
return TUPLE_ELEMENT_NAMES.apply(type, names)
1477
si
1478
1479
// The name `GHUL_NAME_ATTRIBUTE` carries, when a member is
1480
// marked with one — an override or interface implementation
1481
// whose IL name the base or interface fixed, so the IL name
1482
// itself cannot be de-camelled back to the ghūl name the way
1483
// an ordinary member's can. Read in preference to any name
1484
// computed from the IL name.
1485
_ghul_name_override(member: MemberInfo) -> string? is
1486
try
1487
for attr in member.get_custom_attributes_data() do
1488
if attr.attribute_type.full_name =~ "Ghul.Internal.GHUL_NAME_ATTRIBUTE" then
1489
return cast string?(attr.constructor_arguments[0].value)
1490
fi
1491
od
1492
catch ex: System.Exception
1493
yrt
1494
1495
return null
1496
si
1497
1498
has_union_attribute(type: TYPE) -> bool is
1499
try
1500
for attr in type.get_custom_attributes_data() do
1501
let attr_type = attr.attribute_type
1502
1503
if attr_type.full_name =~ "Ghul.Internal.UNION_ATTRIBUTE" then
1504
return true
1505
fi
1506
od
1507
catch ex: System.Exception
1508
yrt
1509
1510
return false
1511
si
1512
1513
has_variant_attribute(type: TYPE) -> bool is
1514
try
1515
for attr in type.get_custom_attributes_data() do
1516
let attr_type = attr.attribute_type
1517
1518
if attr_type.full_name =~ "Ghul.Internal.VARIANT_ATTRIBUTE" then
1519
return true
1520
fi
1521
od
1522
catch ex: System.Exception
1523
yrt
1524
1525
return false
1526
si
1527
1528
has_default_variant_attribute(type: TYPE) -> bool is
1529
try
1530
for attr in type.get_custom_attributes_data() do
1531
let attr_type = attr.attribute_type
1532
1533
if attr_type.full_name =~ "Ghul.Internal.DEFAULT_VARIANT_ATTRIBUTE" then
1534
return true
1535
fi
1536
od
1537
catch ex: System.Exception
1538
yrt
1539
1540
return false
1541
si
1542
1543
// Resolving an attribute's type reaches for the assembly that
1544
// declares it, so a marker this compiler emits against a runtime
1545
// too old to declare it throws rather than answering. Treated as
1546
// absent: the slot then reads the way it did before the marker
1547
// existed.
1548
ARGUMENT_PACK_ATTRIBUTE_NAME: string static =>
1549
"Ghul.Internal.ARGUMENT_PACK_ATTRIBUTE"
1550
1551
RETURN_PACK_ATTRIBUTE_NAME: string static =>
1552
"Ghul.Internal.RETURN_PACK_ATTRIBUTE"
1553
1554
ARGUMENT_SPREAD_ATTRIBUTE_NAME: string static =>
1555
"Ghul.Internal.ARGUMENT_SPREAD_ATTRIBUTE"
1556
1557
// How deep in the parameter's own type the argument-pack marker
1558
// sits, and negative when the parameter carries no marker at
1559
// all. The argumentless form of the attribute is depth zero -
1560
// the parameter's own function type - which is what an assembly
1561
// built before the marker could be written deeper records.
1562
_argument_pack_depth(attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> int =>
1563
_pack_depth(attributes, ARGUMENT_PACK_ATTRIBUTE_NAME)
1564
1565
_pack_depth(
1566
attributes: Collections.Iterable[System.Reflection.CustomAttributeData],
1567
attribute_name: string
1568
) -> int is
1569
try
1570
for attr in attributes do
1571
if attr.attribute_type.full_name =~ attribute_name then
1572
for argument in attr.constructor_arguments do
1573
if let depth = cast int?(argument.value) then
1574
return depth
1575
fi
1576
od
1577
1578
return 0
1579
fi
1580
od
1581
catch ex: System.Exception
1582
yrt
1583
1584
return -1
1585
si
1586
1587
_has_attribute_named(attributes: Collections.Iterable[System.Reflection.CustomAttributeData], name: string) -> bool is
1588
try
1589
for attr in attributes do
1590
if attr.attribute_type.full_name =~ name then
1591
return true
1592
fi
1593
od
1594
catch ex: System.Exception
1595
yrt
1596
1597
return false
1598
si
1599
1600
_has_pure_attribute(attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> bool is
1601
try
1602
for attr in attributes do
1603
let attr_type = attr.attribute_type
1604
1605
if attr_type.full_name =~ "Ghul.Internal.PURE_ATTRIBUTE" then
1606
return true
1607
fi
1608
od
1609
catch ex: System.Exception
1610
yrt
1611
1612
return false
1613
si
1614
1615
_has_stable_attribute(attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> bool is
1616
try
1617
for attr in attributes do
1618
let attr_type = attr.attribute_type
1619
1620
if attr_type.full_name =~ "Ghul.Internal.STABLE_ATTRIBUTE" then
1621
return true
1622
fi
1623
od
1624
catch ex: System.Exception
1625
yrt
1626
1627
return false
1628
si
1629
1630
// The BCL's own marker for a synthesised member or type — see
1631
// SRM_STRUCTURE_WALK.COMPILER_GENERATED_ATTRIBUTE_NAME for the
1632
// emission side. Reading it back means an importing compilation
1633
// recognises a synthesised symbol without testing its name,
1634
// whether the assembly it came from is ghūl-compiled or not —
1635
// a C#-compiled record's synthesised members carry it too.
1636
_has_compiler_generated_attribute(attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> bool is
1637
try
1638
for attr in attributes do
1639
let attr_type = attr.attribute_type
1640
1641
if attr_type.full_name =~ "System.Runtime.CompilerServices.CompilerGeneratedAttribute" then
1642
return true
1643
fi
1644
od
1645
catch ex: System.Exception
1646
yrt
1647
1648
return false
1649
si
1650
1651
// True when `method` is `type`'s implementation of the
1652
// self-instantiated generic interface named by `open_interface_name`
1653
// (`System.IEquatable\`1` or `System.IComparable\`1`) — i.e. `type`
1654
// implements `IEquatable[type]`/`IComparable[type]`, not some other
1655
// instantiation, and `method` is that interface member rather than
1656
// an unrelated same-named overload (`object.Equals`, the
1657
// non-generic `IComparable.CompareTo(object)`).
1658
//
1659
// `Type.GetInterfaceMap` would answer this directly but throws
1660
// `NotSupportedException` under the `MetadataLoadContext` this
1661
// reflection runs in (import assemblies are loaded for metadata
1662
// only, never executed), so this matches by signature instead:
1663
// `IEquatable[T].Equals`/`IComparable[T].CompareTo` are always
1664
// exactly `(T) -> bool`/`(T) -> int`, and no other member can
1665
// satisfy that interface with a different parameter type. The one
1666
// gap this leaves is a type that implements the interface
1667
// *explicitly* (`bool IEquatable<Box>.Equals(Box other)`) — that
1668
// member doesn't surface under its simple name in `GetMethods()`
1669
// at all, with or without `GetInterfaceMap`, so it's an existing
1670
// reflection-layer limitation rather than one this check adds.
1671
_is_self_relational_interface_target(type: TYPE, method: MethodInfo, open_interface: TYPE, expected_return_type: TYPE) -> bool is
1672
if method.return_type != expected_return_type then
1673
return false
1674
fi
1675
1676
let parameters = method.get_parameters()
1677
1678
if parameters.count != 1 then
1679
return false
1680
fi
1681
1682
// `IEquatable[T].=~`/`IComparable[T].<>` on the trait's own
1683
// declaration: `type` is the open interface itself (reflecting
1684
// `Ghul.Equatable[T]`'s member list, not a concrete
1685
// implementer), and the parameter is its own type parameter.
1686
if type == open_interface then
1687
return parameters[0].parameter_type == open_interface.get_generic_arguments()[0]
1688
fi
1689
1690
// An implementer: `type` closed-implements `IEquatable[type]`
1691
// and the parameter is `type` itself.
1692
let closed_interface = open_interface.make_generic_type([type])
1693
1694
return type.get_interfaces() |> any(i => i == closed_interface) /\
1695
parameters[0].parameter_type == type
1696
si
1697
1698
// The intrinsic operation name (e.g. "arithmetic.add") carried by
1699
// a method's INTRINSIC_ATTRIBUTE, or null if it carries none. An
1700
// imported method bearing the marker is reconstructed as an innate
1701
// function whose calls lower to IL opcodes, matching a source-
1702
// declared intrinsic.
1703
_intrinsic_operation(attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> string? is
1704
try
1705
for attr in attributes do
1706
if attr.attribute_type.full_name =~ "Ghul.Internal.INTRINSIC_ATTRIBUTE" then
1707
for argument in attr.constructor_arguments do
1708
return cast string?(argument.value)
1709
od
1710
fi
1711
od
1712
catch ex: System.Exception
1713
yrt
1714
1715
return null
1716
si
1717
1718
// A parameter or return slot carrying the purity marker has a
1719
// pure top-level function type: rebuild the mapped type in
1720
// its pure shape. Slots whose mapped type is not a function
1721
// type (or is the zero-argument void form, which has no pure
1722
// shape) keep the plain type — conservative on both sides.
1723
_with_pure_function(type: Types.Type, attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> Types.Type is
1724
if !type.is_function \/ !isa Types.GENERIC(type) \/ !_has_pure_attribute(attributes) then
1725
return type
1726
fi
1727
1728
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup
1729
1730
let types = Collections.LIST[Types.Type]()
1731
1732
for a in (cast Types.GENERIC(type)).arguments do
1733
types.add(a)
1734
od
1735
1736
if type.is_action then
1737
types.add(lookup.get_void_type())
1738
fi
1739
1740
return lookup.get_function_type(types, true)
1741
si
1742
1743
// A delegate type, found by walking the base chain to
1744
// System.MulticastDelegate and comparing reflected types by
1745
// identity. What a delegate member does is whatever its target
1746
// does, so nothing about its shape can vouch for it.
1747
_inherits_multicast_delegate(type: TYPE) -> bool is
1748
let current mut = type.base_type
1749
1750
while current? do
1751
if current == _multicast_delegate_type then
1752
return true
1753
fi
1754
1755
current = current.base_type
1756
od
1757
1758
return false
1759
si
1760
1761
has_closed_attribute(type: TYPE) -> bool is
1762
try
1763
for attr in type.get_custom_attributes_data() do
1764
let attr_type = attr.attribute_type
1765
1766
if attr_type.full_name =~ "Ghul.Internal.CLOSED_ATTRIBUTE" then
1767
return true
1768
fi
1769
od
1770
catch ex: System.Exception
1771
yrt
1772
1773
return false
1774
si
1775
1776
// Cross-assembly variant materialization: when reflecting a
1777
// union from another assembly, the variant classes are
1778
// sibling top-level types whose `Namespace` equals the
1779
// union's full name (assemblies.ghul: import_type queues
1780
// them into TYPE_DETAILS_LOOKUP by union full name instead
1781
// of registering them as top-level). When the union's
1782
// symbol is created and its members populated, call this
1783
// to also materialize the variants as members of the union.
1784
materialize_variants(union_symbol: Symbols.UNION, union_type: TYPE) is
1785
let union_full_name = union_type.full_name
1786
1787
if !union_full_name? then
1788
return
1789
fi
1790
1791
let variants = _type_details_lookup.get_variants_for_union(union_full_name)
1792
1793
if !variants? then
1794
return
1795
fi
1796
1797
for variant_type in variants do
1798
materialize_variant(union_symbol, variant_type)
1799
od
1800
si
1801
1802
materialize_variant(union_symbol: Symbols.UNION, variant_type: TYPE) is
1803
let variant_name = variant_type.name
1804
let assembly_name = get_assembly_name(variant_type)
1805
1806
let arguments = LIST()
1807
let argument_variances = LIST()
1808
1809
if variant_type.is_generic_type /\ variant_type.contains_generic_parameters then
1810
for a in variant_type.get_generic_arguments() do
1811
if a.is_generic_type_parameter then
1812
arguments.add(a.name)
1813
argument_variances.add(_type_mapper.map_type_argument_variance(a))
1814
fi
1815
od
1816
fi
1817
1818
let variant_symbol =
1819
Symbols.VARIANT(
1820
Source.LOCATION.reflected,
1821
Source.LOCATION.reflected,
1822
union_symbol,
1823
variant_name,
1824
arguments,
1825
union_symbol
1826
)
1827
1828
variant_symbol.mark_overrides_resolved()
1829
1830
if argument_variances.count > 0 then
1831
variant_symbol.argument_variances = argument_variances
1832
fi
1833
1834
variant_symbol.il_assembly_name = assembly_name
1835
variant_symbol.il_name_override = get_il_name(variant_type)
1836
variant_symbol.dotnet_full_name = variant_type.full_name
1837
1838
// Register the variant in the lookup so subsequent
1839
// dotnet-type-keyed lookups (`get_symbol(type)`) and
1840
// resolution paths can find it.
1841
let variant_td =
1842
TYPE_DETAILS(
1843
variant_type,
1844
union_symbol.qualified_name,
1845
variant_name,
1846
variant_symbol.il_name_override,
1847
assembly_name
1848
)
1849
1850
union_symbol.add_member(variant_symbol)
1851
1852
add_ancestors(variant_symbol, variant_type)
1853
add_members(variant_symbol, variant_type)
1854
resolve_overrides(variant_symbol)
1855
1856
// Populate the variant's `_field_names` list from the
1857
// .NET instance-field declaration order so positional
1858
// destructuring (`let (v, r) = step`) works the same
1859
// way it does for an intra-assembly variant. Source-
1860
// defined variants get this filled by
1861
// `declare_variable`; reflected variants need to do it
1862
// explicitly here.
1863
for `field in variant_type.get_fields() do
1864
if !`field.is_static /\ `field.is_public then
1865
variant_symbol.register_reflected_field_name(`field.name)
1866
fi
1867
od
1868
1869
// Wire the parent union's `default_variant` pointer to
1870
// the marked variant so cross-asm `u?` / `u!` lowers to
1871
// isa/cast against this variant — matching what source-
1872
// defined unions get from declare_members.
1873
if has_default_variant_attribute(variant_type) then
1874
variant_symbol.is_default = true
1875
union_symbol.default_variant = variant_symbol
1876
fi
1877
si
1878
1879
// The CLR-side full name, with the nested-type separator the
1880
// metadata reader expects: reflection spells a nested type
1881
// `Outer+Inner`, and a type reference names it `Outer/Inner`.
1882
get_il_name(type: TYPE) -> string is
1883
let full_name: string? = type.full_name
1884
1885
assert full_name? else "get il name type has no full name: {type}"
1886
1887
return full_name.replace("+", "/")
1888
si
1889
1890
// Reflect the .NET-declared default value of a parameter into
1891
// the string form that `Function.argument_defaults` expects.
1892
// - null ⇒ no default ⇒ parameter is not omittable.
1893
// - "default" ⇒ omittable, fill with `IR.Values.DEFAULT` (the
1894
// .NET parameter is a reference type with `= null`, or a
1895
// `Nullable<T>` with no constant value).
1896
// - any other string ⇒ a constant literal, formatted with
1897
// invariant culture so the emission side can read it back
1898
// independent of host locale.
1899
//
1900
// Conservative gate: only primitive numeric types, bool, char,
1901
// string, and enums are stored. Anything else (decimal,
1902
// struct, …) returns null so the parameter is treated as not
1903
// omittable rather than silently filled with a zero default.
1904
_default_for_parameter(a: ParameterInfo) -> string? is
1905
if !a.has_default_value then
1906
return null
1907
fi
1908
1909
let raw = a.raw_default_value
1910
1911
// This channel does spell an absent value as a word, because
1912
// ghūl's own `= _` reaches it that way and a parameter
1913
// default is read back by the same conversion.
1914
if !raw? then
1915
return "default"
1916
fi
1917
1918
return REFLECTED_CONSTANTS.text_of(raw, a.parameter_type)
1919
si
1920
si
1921
si