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
class SYMBOL_FACTORY(
30
_namespaces: NAMESPACES,
31
_type_name_map: TYPE_NAME_MAP,
32
_type_mapper: TYPE_MAPPER,
33
_referenced_assemblies: REFERENCED_ASSEMBLIES,
34
_type_source: TypeSource,
35
_type_details_lookup: TYPE_DETAILS_LOOKUP,
36
logger: Logging.Logger init
37
) is
38
_assembly_names: MAP[Assembly,string];
39
40
_symbol_table: SYMBOL_TABLE;
41
42
_non_generic_get_enumerator: MethodInfo;
43
44
_void_type: TYPE;
45
_object_type: TYPE;
46
_value_type: TYPE;
47
_tuple_types: List[TYPE];
48
49
_ambiguous_method_checker: AMBIGUOUS_METHOD_CHECKER;
50
51
init(..) is
52
_assembly_names = MAP[Assembly,string]();
53
_ambiguous_method_checker = AMBIGUOUS_METHOD_CHECKER(logger);
54
55
_type_source.on_start(() -> void is start(); si);
56
si
57
58
start() is
59
let mnt = _type_source.get_type("System.Collections.IEnumerator");
60
_non_generic_get_enumerator = mnt.get_method("GetEnumerator")!;
61
62
_void_type = _type_source.get_type("System.Void");
63
_object_type = _type_source.get_type("System.Object");
64
_value_type = _type_source.get_type("System.ValueType");
65
66
_tuple_types = _type_source.get_types([
67
"System.ValueTuple`1",
68
"System.ValueTuple`2",
69
"System.ValueTuple`3",
70
"System.ValueTuple`4",
71
"System.ValueTuple`5",
72
"System.ValueTuple`6",
73
"System.ValueTuple`7"
74
]);
75
76
// TODO handle more than 7-tuple types by
77
// nesting them
78
si
79
80
set_symbol_table(symbol_table: SYMBOL_TABLE) is
81
_symbol_table = symbol_table;
82
si
83
84
create_symbol(type_details: TYPE_DETAILS) -> Symbols.Scoped? is
85
try
86
let dotnet_type = type_details.dotnet_type;
87
88
if dotnet_type.is_class \/ dotnet_type.is_value_type \/ dotnet_type.is_interface \/ dotnet_type.is_enum then
89
return create_class(type_details);
90
fi
91
catch ex: System.Exception
92
debug_always("create symbol failed: {type_details} exception: {ex}");
93
94
throw ex;
95
yrt
96
97
return null;
98
si
99
100
create_class(type_details: TYPE_DETAILS) -> Symbols.Scoped is
101
let dotnet_type = type_details.dotnet_type;
102
let ghul_type_name = type_details.ghul_type_name;
103
let assembly_name = type_details.assembly_name!;
104
let il_name mut = type_details.il_name;
105
106
let owner = EMPTY_SCOPE(type_details.ghul_namespace);
107
108
let arguments = LIST();
109
let argument_variances = LIST();
110
let argument_constraint_kinds = LIST();
111
let argument_has_constructor_constraint = LIST();
112
let argument_type_bounds = Collections.LIST[Semantic.Types.Type?]();
113
114
if dotnet_type.is_generic_type /\ dotnet_type.contains_generic_parameters then
115
let argument_types = dotnet_type.get_generic_arguments();
116
117
for a in argument_types do
118
if a.is_generic_type_parameter then
119
arguments.add(a.name);
120
argument_variances.add(_type_mapper.map_type_argument_variance(a));
121
argument_constraint_kinds.add(_type_mapper.generic_parameter_constraint_kind(a));
122
argument_has_constructor_constraint.add(_type_mapper.generic_parameter_has_constructor_constraint(a));
123
argument_type_bounds.add(first_type_bound(a));
124
fi
125
od
126
fi
127
128
let result: Symbols.Classy mut;
129
130
if dotnet_type.is_enum then
131
result = Symbols.ENUM_STRUCT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, owner);
132
result.mark_overrides_resolved();
133
elif dotnet_type.is_class /\ has_union_attribute(dotnet_type) then
134
result = Symbols.UNION(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner);
135
result.mark_overrides_resolved();
136
elif dotnet_type.is_class /\ has_variant_attribute(dotnet_type) then
137
result = Symbols.VARIANT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner);
138
result.mark_overrides_resolved();
139
elif dotnet_type.is_class then
140
let result_class = Symbols.CLASS(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner);
141
142
if dotnet_type == _object_type then
143
result_class.mark_is_object();
144
elif dotnet_type == _value_type then
145
result_class.mark_is_root_value_type();
146
fi
147
148
if has_closed_attribute(dotnet_type) then
149
result_class.mark_closed();
150
fi
151
152
if dotnet_type.is_abstract then
153
result_class.mark_abstract();
154
fi
155
156
result = result_class;
157
result.mark_overrides_resolved();
158
elif dotnet_type.is_value_type then
159
if dotnet_type == _void_type then
160
result = Symbols.VOID_STRUCT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner);
161
result.il_is_primitive_type = true;
162
else
163
result = Symbols.STRUCT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner);
164
result.il_is_primitive_type = dotnet_type.is_primitive \/ dotnet_type == _void_type;
165
fi
166
167
result.mark_overrides_resolved();
168
elif dotnet_type.is_interface then
169
result = Symbols.TRAIT(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, ghul_type_name, arguments, owner);
170
else
171
throw System.Exception("unhandled reflected kind for {dotnet_type}");
172
fi
173
174
_referenced_assemblies.add(assembly_name, type_details.version_number);
175
176
if argument_variances.count > 0 then
177
result.argument_variances = argument_variances;
178
fi
179
180
if argument_constraint_kinds.count > 0 then
181
result.argument_constraint_kinds = argument_constraint_kinds;
182
fi
183
184
if argument_has_constructor_constraint.count > 0 then
185
result.argument_has_constructor_constraint = argument_has_constructor_constraint;
186
fi
187
188
if argument_type_bounds.count > 0 /\ argument_type_bounds |> any(b => b?) then
189
result.argument_type_bounds = argument_type_bounds;
190
fi
191
192
result.il_assembly_name = assembly_name;
193
194
if !il_name? then
195
il_name = get_il_name(dotnet_type);
196
fi
197
198
result.il_name_override = il_name;
199
200
return result;
201
si
202
203
add_ancestors(symbol: Symbols.Scoped, type: TYPE) is
204
if !isa Symbols.Classy(symbol) then
205
return;
206
fi
207
208
let result = symbol;
209
210
if type.base_type? then
211
result.add_ancestor(_type_mapper.get_type(type.base_type!));
212
elif type.is_interface then
213
result.add_ancestor(_type_mapper.get_type(_type_source.get_type("System.Object")));
214
fi
215
216
for i in type.get_interfaces() do
217
result.add_ancestor(_type_mapper.get_type(i));
218
od
219
220
check_constraints_for_type(result, type);
221
si
222
223
// Reflected interfaces are the one kind that isn't marked
224
// overrides-resolved on creation, so this is where their
225
// inherited members get pulled down. It has to run after
226
// `add_members`: a .NET interface that redeclares an inherited
227
// member covariantly (`new IDerived? BaseType { get; }`) only
228
// has its own declaration to override with once that
229
// declaration is in scope. Pull down first and the inherited
230
// declaration wins, hiding the derived one and turning a
231
// resolved redeclaration into an inheritance clash.
232
resolve_overrides(symbol: Symbols.Scoped) is
233
if !isa Symbols.Classy(symbol) then
234
return;
235
fi
236
237
let result = symbol;
238
239
IoC.CONTAINER.instance.symbol_table.enter_scope(result);
240
result.pull_down_super_symbols();
241
IoC.CONTAINER.instance.symbol_table.leave_scope(result);
242
si
243
244
check_constraints_for_type(symbol: Symbols.Scoped, type: TYPE) is
245
if
246
type.is_generic_type /\
247
type.contains_generic_parameters /\
248
type.get_generic_arguments() |>
249
any(t =>
250
t.is_generic_type_parameter /\
251
!are_generic_parameter_constraints_safe(t)
252
)
253
then
254
symbol.is_unsafe_constraints = true;
255
fi
256
si
257
258
check_constraints_for_method(owner: Symbols.Symbol, symbol: Symbols.Scoped, generic_arguments: Collections.Iterable[TYPE]) is
259
if generic_arguments |> any(t => !are_generic_parameter_constraints_safe(t)) then
260
symbol.is_unsafe_constraints = true;
261
fi
262
si
263
264
mask_gpa(a: GenericParameterAttributes, b: GenericParameterAttributes) -> GenericParameterAttributes =>
265
cast GenericParameterAttributes(cast int(a) & cast int(b));
266
267
are_generic_parameter_constraints_safe(t: TYPE) -> bool is
268
// `class`, `struct` and `new()` (parameterless-constructor)
269
// attribute flags are modelled and enforced. Type bounds
270
// (`where T : SomeBase`) are now also read and enforced —
271
// the first one per parameter, captured into the symbol's
272
// `argument_type_bounds` list. A parameter declaring more
273
// than one type bound stays unsafe (we only have one slot)
274
// and triggers the unchecked-constraints warning so the
275
// caller knows the second bound isn't checked.
276
return count_type_bounds(t) <= 1;
277
si
278
279
// Number of .NET constraint types declared on this parameter
280
// that ghūl would have to enforce — i.e. excluding the redundant
281
// `System.ValueType` that accompanies a `struct` parameter.
282
count_type_bounds(t: TYPE) -> int is
283
let constraint_attributes = mask_gpa(t.generic_parameter_attributes, GenericParameterAttributes.SPECIAL_CONSTRAINT_MASK);
284
let struct_constrained =
285
constraint_attributes.has_flag(GenericParameterAttributes.NOT_NULLABLE_VALUE_TYPE_CONSTRAINT);
286
287
let count mut = 0;
288
289
for c in t.get_generic_parameter_constraints() do
290
if !(struct_constrained /\ c.full_name? /\ c.full_name =~ "System.ValueType") then
291
count = count + 1;
292
fi
293
od
294
295
return count;
296
si
297
298
// The first ghūl-mapped .NET type-bound on this parameter, or
299
// null when the parameter is unbounded (or only carries the
300
// redundant `System.ValueType` companion of a struct flag).
301
first_type_bound(t: TYPE) -> Semantic.Types.Type? is
302
let constraint_attributes = mask_gpa(t.generic_parameter_attributes, GenericParameterAttributes.SPECIAL_CONSTRAINT_MASK);
303
let struct_constrained =
304
constraint_attributes.has_flag(GenericParameterAttributes.NOT_NULLABLE_VALUE_TYPE_CONSTRAINT);
305
306
for c in t.get_generic_parameter_constraints() do
307
if !(struct_constrained /\ c.full_name? /\ c.full_name =~ "System.ValueType") then
308
return _type_mapper.get_type(c);
309
fi
310
od
311
312
return null;
313
si
314
315
// Static members of the `$globals` synthetic class are global
316
// functions / variables / properties in the source language. Detect
317
// the host so the importer can produce GLOBAL_FUNCTION /
318
// GLOBAL_VARIABLE / GLOBAL_PROPERTY symbols rather than the
319
// static-on-a-class kinds. Their owner becomes the enclosing
320
// NAMESPACE (mirroring source-side `declare_function`) so qualified
321
// names skip the synthetic `$globals` segment and IL emission goes
322
// through the existing `gen_globals_class_reference` path.
323
_globals_owner_namespace(owner: Symbols.Symbol) -> Symbols.NAMESPACE? is
324
if !(owner.name =~ "$globals") \/ !isa Symbols.Classy(owner) then
325
return null;
326
fi
327
328
let classy_owner = owner.owner;
329
330
if !classy_owner? then
331
return null;
332
fi
333
334
return _namespaces.try_find_namespace(".{classy_owner.name}");
335
si
336
337
add_members(result: Symbols.Scoped, type: TYPE) is
338
let properties = MAP();
339
let indexers = SET();
340
let methods = LIST();
341
342
for p in type.get_properties() do
343
add_property(result, type, properties, indexers, p);
344
od
345
346
for t in type.get_members(cast BindingFlags(64 + 4 + 32 + 16 + 8)) do
347
add_member(result, type, properties, indexers, methods, t);
348
od
349
350
if methods.count > 0 then
351
_ambiguous_method_checker.check(result, methods);
352
353
for m in methods do
354
result.add_member(m.function);
355
od
356
fi
357
358
inherit_default_trait_members(result, type);
359
si
360
361
// Copy any concrete (`is_default_trait_method`) method from
362
// a trait the class implements into the class itself, unless
363
// the class already has a member by that name.
364
//
365
// Why this is its own pass: `mark_overrides_resolved()` runs
366
// on reflected concrete classes in `create_class` so the
367
// full `SYMBOL_INHERITANCE_RESOLVER` skips them — its
368
// override-clash and abstract-method checks would otherwise
369
// fire on .NET-side diamonds (`IList` × `IReadOnlyList`,
370
// `MemberInfo.Module`, …) that the assembly's IL has already
371
// resolved via explicit interface implementation but that
372
// ghul's reflection layer can't see. Pulling down just the
373
// trait defaults keeps the assumption-of-trust for
374
// overrides while still making `find_member("filter")` work
375
// on a reflected class that inherits `filter` from `Pipe[T]`.
376
inherit_default_trait_members(result: Symbols.Scoped, type: TYPE) is
377
if !isa Symbols.Classy(result) then
378
return;
379
fi
380
381
let classy = result;
382
383
for i in 0..classy.ancestors.count do
384
let scope = classy.get_ancestor(i)?.scope;
385
386
if !scope? then
387
continue;
388
fi
389
390
for member in scope.symbols do
391
inherit_default_trait_member(classy, member);
392
od
393
od
394
si
395
396
inherit_default_trait_member(into: Symbols.Classy, member: Symbols.Symbol) is
397
if isa Symbols.FUNCTION_GROUP(member) then
398
let group = member;
399
400
for function in group.functions do
401
if function.is_default_trait_method then
402
into.add_member(function);
403
fi
404
od
405
elif isa Symbols.Function(member) then
406
let function = member;
407
408
if function.is_default_trait_method then
409
into.add_member(function);
410
fi
411
fi
412
si
413
414
add_member(
415
owner: Symbols.Scoped,
416
type: TYPE,
417
properties: MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS],
418
indexers: SET[System.Reflection.MethodInfo],
419
methods: LIST[(function: Symbols.Function, method_info: MethodInfo)],
420
member: MemberInfo
421
) -> Symbols.Symbol?
422
is
423
let member_type = member.member_type;
424
425
if member_type == MemberTypes.CONSTRUCTOR then
426
add_constructor(owner, cast ConstructorInfo?(member)!);
427
elif member_type == MemberTypes.FIELD then
428
add_field(owner, type, cast FieldInfo?(member)!);
429
elif member_type == MemberTypes.METHOD then
430
add_method(owner, type, properties, indexers, methods, cast MethodInfo?(member)!);
431
fi
432
return null;
433
si
434
435
add_constructor(owner: Symbols.Scoped, method: ConstructorInfo) is
436
let result: Symbols.Function mut;
437
let location = Source.LOCATION.reflected;
438
439
if !method.is_public then
440
return;
441
fi
442
443
if method.is_static then
444
result = Symbols.STATIC_METHOD(location, location, owner, "init", owner);
445
elif owner.is_value_type then
446
result = Symbols.STRUCT_METHOD(location, location, owner, "init", owner);
447
elif method.is_abstract then
448
result = Symbols.ABSTRACT_METHOD(location, location, owner, "init", owner);
449
else
450
result = Symbols.INSTANCE_METHOD(location, location, owner, "init", owner);
451
fi
452
453
result.il_name_override = method.name;
454
455
let argument_names = LIST();
456
let argument_types = LIST();
457
let argument_defaults = Collections.LIST[string?]();
458
let argument_reads = Collections.LIST[bool]();
459
let argument_writes = Collections.LIST[bool]();
460
let has_direction_override mut = false;
461
462
for a in method.get_parameters() do
463
argument_names.add(a.name ?? "");
464
let arg_attributes = a.get_custom_attributes_data();
465
argument_types.add(
466
NULLABILITY.resolve(
467
_with_tuple_element_names(_type_mapper.get_type(a.parameter_type), arg_attributes),
468
arg_attributes,
469
method,
470
a.parameter_type
471
)!
472
);
473
argument_defaults.add(_default_for_parameter(a));
474
argument_reads.add(!(a.is_out /\ !a.is_in));
475
argument_writes.add(!(a.is_in /\ !a.is_out));
476
if a.is_out \/ a.is_in then
477
has_direction_override = true;
478
fi
479
od
480
481
result.argument_names = argument_names;
482
result.arguments = argument_types;
483
result.argument_defaults = argument_defaults;
484
485
if has_direction_override then
486
result.set_argument_directions(argument_reads, argument_writes);
487
fi
488
489
result.return_type = _type_mapper.get_type(_void_type);
490
491
// A public parameterless constructor — what a `new()`
492
// type-parameter constraint requires of a type argument.
493
if argument_types.count == 0 /\ isa Symbols.Classy(owner) then
494
(cast Symbols.Classy(owner)).has_parameterless_constructor = true;
495
fi
496
497
owner.add_member(result);
498
si
499
500
add_field(owner: Symbols.Scoped, type: TYPE, `field: FieldInfo) is
501
if !`field.is_public then
502
return;
503
fi
504
505
if type.is_enum /\ !`field.is_special_name then
506
let raw_value = `field.get_raw_constant_value();
507
let enum_member =
508
Symbols.ENUM_STRUCT_MEMBER(
509
Source.LOCATION.reflected,
510
cast Symbols.ENUM_STRUCT?(owner)!,
511
_type_name_map.get_constant_name(owner.qualified_name, `field.name, false),
512
if raw_value? then raw_value.to_string() ?? "" else "" fi
513
);
514
515
owner.add_member(enum_member);
516
517
return;
518
fi
519
520
let result: Symbols.Field mut;
521
let location = Source.LOCATION.reflected;
522
523
let field_name = `field.name;
524
525
let name: string? mut = null;
526
527
if field_name.length == 5 /\ `field.name.starts_with("Item") /\ type.is_generic_type then
528
let unspecialized_type = type.get_generic_type_definition();
529
530
let is_tuple mut = false;
531
532
for t in _tuple_types do
533
if unspecialized_type == t then
534
is_tuple = true;
535
break;
536
fi
537
od
538
539
if is_tuple then
540
name = "{(cast int(field_name.get_chars(field_name.length-1)) - 49)}";
541
fi
542
fi
543
544
if !name? then
545
name = _type_name_map.get_member_name(owner.qualified_name, `field.name, false, false);
546
fi
547
548
// For inherited fields, the declaring symbol must point at the base
549
// class, not the derived class — otherwise the IL fieldref says
550
// "Foo::SubjectField" instead of "FooBase`2<...>::SubjectField" and
551
// the runtime can't find the field.
552
let declaring_symbol: Symbols.Symbol mut = owner;
553
let dt = `field.declaring_type;
554
if dt? /\ dt != type then
555
declaring_symbol = _type_mapper.get_type(dt).symbol;
556
fi
557
558
if `field.is_static then
559
let globals_namespace = _globals_owner_namespace(owner);
560
if globals_namespace? then
561
let global = Symbols.GLOBAL_VARIABLE(location, globals_namespace, name);
562
global.il_assembly_name = cast Symbols.Classy?(owner)!.il_assembly_name;
563
result = global;
564
else
565
result = Symbols.STATIC_FIELD(location, declaring_symbol, name);
566
fi
567
elif type.is_value_type then
568
result = Symbols.STRUCT_FIELD(location, declaring_symbol, name);
569
else
570
result = Symbols.INSTANCE_FIELD(location, declaring_symbol, name);
571
fi
572
573
result.il_name_override = `field.name;
574
575
let field_attributes = `field.get_custom_attributes_data();
576
result.set_type(
577
NULLABILITY.resolve(
578
_with_tuple_element_names(_type_mapper.get_type(`field.field_type), field_attributes),
579
field_attributes,
580
`field,
581
`field.field_type
582
)!
583
);
584
585
// Same problem as add_method: inherited fields on a constructed
586
// generic base have their declared type returned by reflection in
587
// *substituted* form. Recover the open form via the open base type
588
// and stash it as unspecialized_type; gen_reference prefers it.
589
if dt? /\ dt != type /\ dt.is_generic_type /\ !dt.is_generic_type_definition then
590
let open_type = dt.get_generic_type_definition();
591
let open_field: FieldInfo? mut = null;
592
593
for m in open_type.get_members(cast BindingFlags(2 + 4 + 8 + 16 + 32)) do
594
if m.member_type == MemberTypes.FIELD then
595
let candidate = cast FieldInfo?(m)!;
596
if candidate.metadata_token == `field.metadata_token then
597
open_field = candidate;
598
break;
599
fi
600
fi
601
od
602
603
if open_field? then
604
result.unspecialized_type = _type_mapper.get_type(open_field.field_type);
605
fi
606
fi
607
608
owner.add_member(result);
609
si
610
611
// `family` and `famorassem` are deliberately absent: a subclass in
612
// another assembly is entitled to a protected member.
613
_is_reachable_from_other_assembly(method: MethodInfo) -> bool =>
614
!(method.is_private \/ method.is_assembly \/ method.is_family_and_assembly);
615
616
add_property(
617
owner: Symbols.Scoped,
618
type: TYPE,
619
properties: MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS],
620
indexers: SET[System.Reflection.MethodInfo],
621
property: PropertyInfo
622
) is
623
let index_parameters = property.get_index_parameters();
624
625
if index_parameters.count > 0 then
626
let getter = property.get_get_method();
627
if getter? then
628
indexers.add(getter);
629
fi
630
631
let setter = property.get_set_method();
632
if setter? then
633
indexers.add(setter);
634
fi
635
636
return;
637
fi
638
639
if property.name =~ "Current" then
640
if property.declaring_type == _type_source.get_type("System.Collections.IEnumerator") then
641
return;
642
fi
643
fi
644
645
let result: Symbols.Property mut;
646
let name = _type_name_map.get_member_name(owner.qualified_name, property.name, false, property.is_special_name);
647
648
let getter = property.get_method;
649
let setter = property.set_method;
650
651
if setter? /\ !getter? then
652
return;
653
fi
654
655
let is_static mut = false;
656
657
if getter? /\ getter.is_static then
658
is_static = true;
659
elif setter? /\ setter.is_static then
660
is_static = true;
661
fi
662
663
if is_static then
664
let globals_namespace = _globals_owner_namespace(owner);
665
if globals_namespace? then
666
result = Symbols.GLOBAL_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, globals_namespace, name, setter?);
667
else
668
result = Symbols.STATIC_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, name, setter?, false);
669
fi
670
else
671
result = Symbols.INSTANCE_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, name, setter?, false);
672
fi
673
674
result.set_type(
675
_with_property_tuple_element_names(
676
NULLABILITY.resolve(
677
_type_mapper.get_type(property.property_type),
678
property.get_custom_attributes_data(),
679
property,
680
property.property_type
681
)!,
682
property
683
)
684
);
685
result.il_name_override = property.name;
686
687
owner.add_member(result);
688
689
let property_details = PROPERTY_DETAILS(result, property.get_get_method(), property.get_set_method());
690
691
if getter? then
692
properties[getter] = property_details;
693
fi
694
695
if setter? then
696
properties[setter] = property_details;
697
fi
698
si
699
700
add_method(
701
owner: Symbols.Scoped,
702
type: TYPE,
703
properties: MAP[System.Reflection.MethodInfo,PROPERTY_DETAILS],
704
indexers: SET[System.Reflection.MethodInfo],
705
methods: LIST[(function: Symbols.Function, method_info: MethodInfo)],
706
method: MethodInfo
707
) is
708
if !_is_reachable_from_other_assembly(method) then
709
return;
710
fi
711
712
let result: Symbols.Function mut;
713
let location = Source.LOCATION.reflected;
714
715
let name: string mut;
716
717
let property_details: PROPERTY_DETAILS mut;
718
719
if properties.try_get_value(method, property_details ref) then
720
name = _type_name_map.get_member_name(owner.qualified_name, method.name, method.is_private, true);
721
elif method.name =~ "get_Item" \/ method.name =~ "set_Item" then
722
name = method.name;
723
elif method.name =~ "get_Current" /\ method.declaring_type == _type_source.get_type("System.Collections.IEnumerator") then
724
return;
725
elif method.name =~ "GetEnumerator" then
726
if method.declaring_type == _type_source.get_type("System.Collections.IEnumerable") then
727
return;
728
fi
729
730
name = "$get_iterator";
731
732
let iterator_property = Symbols.INSTANCE_PROPERTY(Source.LOCATION.reflected, Source.LOCATION.reflected, owner, "iterator", false, false);
733
iterator_property.set_type(_type_mapper.get_type(method.return_type));
734
735
owner.add_member(iterator_property);
736
737
property_details = PROPERTY_DETAILS(iterator_property, method, null);
738
else
739
name = _type_name_map.get_member_name(owner.qualified_name, method.name, method.is_private, method.is_special_name /\ !indexers.contains(method));
740
fi
741
742
let declaring_symbol: Symbols.Symbol mut = owner;
743
744
let mdt = method.declaring_type;
745
if mdt? /\ mdt != type then
746
declaring_symbol = _type_mapper.get_type(mdt).symbol;
747
fi
748
749
let intrinsic_operation = _intrinsic_operation(method.get_custom_attributes_data());
750
751
if intrinsic_operation? then
752
let globals_namespace = if method.is_static then _globals_owner_namespace(owner) else null fi;
753
754
if globals_namespace? then
755
// Innate calls lower to opcodes inline and never emit a
756
// methodref, so an innate function needs no il_assembly_name.
757
result = Symbols.INNATE_FUNCTION(location, globals_namespace, name, globals_namespace, intrinsic_operation);
758
else
759
result = Symbols.INNATE_METHOD(location, declaring_symbol, name, owner, intrinsic_operation);
760
fi
761
elif method.is_static then
762
let globals_namespace = _globals_owner_namespace(owner);
763
if globals_namespace? then
764
let global = Symbols.GLOBAL_FUNCTION(location, location, globals_namespace, name, globals_namespace);
765
global.il_assembly_name = cast Symbols.Classy?(owner)!.il_assembly_name;
766
result = global;
767
else
768
let static_method = Symbols.STATIC_METHOD(location, location, declaring_symbol, name, owner);
769
770
// A static virtual/abstract interface member -
771
// .NET's generic-math feature. Reachable through a
772
// bound type parameter, which needs the
773
// `constrained.` call shape; an ordinary reflected
774
// static method never is.
775
static_method.is_static_interface_virtual = method.is_virtual /\ mdt? /\ mdt.is_interface;
776
777
result = static_method;
778
fi
779
elif mdt? /\ mdt.is_value_type then
780
result = Symbols.STRUCT_METHOD(location, location, declaring_symbol, name, owner);
781
elif method.is_abstract then
782
result = Symbols.ABSTRACT_METHOD(location, location, declaring_symbol, name, owner);
783
elif mdt? /\ mdt.is_interface then
784
result = Symbols.DEFAULT_TRAIT_METHOD(location, location, declaring_symbol, name, owner);
785
else
786
result = Symbols.INSTANCE_METHOD(location, location, declaring_symbol, name, owner);
787
fi
788
789
if method.is_generic_method then
790
result.is_generic = true;
791
792
let generic_argument_names = LIST();
793
let generic_arguments = LIST();
794
let generic_argument_constraint_kinds = Collections.LIST[Symbols.TypeParameterConstraintKind]();
795
let generic_argument_has_constructor_constraint = Collections.LIST[bool]();
796
let generic_argument_type_bounds = Collections.LIST[Semantic.Types.Type?]();
797
798
let ga = method.get_generic_arguments();
799
800
for p in ga do
801
generic_argument_names.add(p.name);
802
generic_arguments.add(_type_mapper.get_type(p));
803
generic_argument_constraint_kinds.add(_type_mapper.generic_parameter_constraint_kind(p));
804
generic_argument_has_constructor_constraint.add(_type_mapper.generic_parameter_has_constructor_constraint(p));
805
generic_argument_type_bounds.add(first_type_bound(p));
806
od
807
808
check_constraints_for_method(owner, result, ga);
809
810
result.generic_argument_names = generic_argument_names;
811
result.generic_arguments = generic_arguments;
812
result.generic_argument_constraint_kinds = generic_argument_constraint_kinds;
813
result.generic_argument_has_constructor_constraint = generic_argument_has_constructor_constraint;
814
815
if generic_argument_type_bounds |> any(b => b?) then
816
result.generic_argument_type_bounds = generic_argument_type_bounds;
817
fi
818
fi
819
820
result.il_name_override = method.name;
821
822
if _has_pure_attribute(method.get_custom_attributes_data()) then
823
// the marker carries the declared-pure contract: the
824
// function is trusted store-free, and local overrides
825
// are held to the pure-override contract here in the
826
// overriding assembly
827
result.mark_declared_pure();
828
fi
829
830
let argument_names = LIST();
831
let argument_types = LIST();
832
let argument_defaults = Collections.LIST[string?]();
833
let argument_reads = Collections.LIST[bool]();
834
let argument_writes = Collections.LIST[bool]();
835
let has_direction_override mut = false;
836
837
for a in method.get_parameters() do
838
argument_names.add(a.name ?? "");
839
let arg_attributes = a.get_custom_attributes_data();
840
argument_types.add(
841
_with_pure_function(
842
NULLABILITY.resolve(
843
_with_tuple_element_names(_type_mapper.get_type(a.parameter_type), arg_attributes),
844
arg_attributes,
845
method,
846
a.parameter_type
847
)!,
848
arg_attributes
849
)
850
);
851
argument_defaults.add(_default_for_parameter(a));
852
argument_reads.add(!(a.is_out /\ !a.is_in));
853
argument_writes.add(!(a.is_in /\ !a.is_out));
854
if a.is_out \/ a.is_in then
855
has_direction_override = true;
856
fi
857
od
858
859
result.argument_names = argument_names;
860
result.arguments = argument_types;
861
result.argument_defaults = argument_defaults;
862
863
if has_direction_override then
864
result.set_argument_directions(argument_reads, argument_writes);
865
fi
866
867
let return_attributes = method.return_parameter.get_custom_attributes_data();
868
result.return_type =
869
_with_pure_function(
870
NULLABILITY.resolve(
871
_with_tuple_element_names(
872
_type_mapper.get_type(method.return_type),
873
return_attributes
874
),
875
return_attributes,
876
method,
877
method.return_type
878
)!,
879
return_attributes
880
);
881
882
// When inheriting a method from a constructed-generic base in another
883
// assembly, reflection returns parameter and return types in their
884
// *substituted* form (e.g. !0 surfaces as System.Object when the base
885
// is FooBase<object,...>). The CLR requires the methodref signature
886
// to use the open-generic form (!0, !!0). Recover the open form via
887
// the open base type and stash it as unspecialized_*; the IL emitter
888
// already prefers unspecialized_* when present.
889
let dt = method.declaring_type;
890
if dt? /\ dt != type /\ dt.is_generic_type /\ !dt.is_generic_type_definition then
891
let open_type = dt.get_generic_type_definition();
892
let open_method: MethodInfo? mut = null;
893
894
for m in open_type.get_members(cast BindingFlags(2 + 4 + 8 + 16 + 32)) do
895
if m.member_type == MemberTypes.METHOD then
896
let candidate = cast MethodInfo?(m)!;
897
if candidate.metadata_token == method.metadata_token then
898
open_method = candidate;
899
break;
900
fi
901
fi
902
od
903
904
if open_method? then
905
let unspec_arg_types = LIST();
906
for a in open_method.get_parameters() do
907
unspec_arg_types.add(_type_mapper.get_type(a.parameter_type));
908
od
909
result.unspecialized_arguments = unspec_arg_types;
910
result.unspecialized_return_type = _type_mapper.get_type(open_method.return_type);
911
fi
912
fi
913
914
// property_details is only set for a property accessor method
915
@suppress("presence-test-non-optional")
916
if property_details? then
917
let accessor_method_name = method.name;
918
919
if method.equals(property_details.dotnet_get_method) then
920
property_details.ghul_property.read_function = result;
921
property_details.ghul_property.read_function_il_name_override = method.name;
922
elif method.equals(property_details.dotnet_set_method) then
923
property_details.ghul_property.assign_function = result;
924
property_details.ghul_property.assign_function_il_name_override = method.name;
925
fi
926
fi
927
928
methods.add((result, method));
929
si
930
931
get_assembly_name(type: TYPE) -> string is
932
let assembly = type.assembly;
933
934
let result: string mut;
935
936
if _assembly_names.try_get_value(assembly, result ref) then
937
return result;
938
fi
939
940
result = assembly.get_name().name ?? "";
941
942
_assembly_names.add(assembly, result);
943
944
return result;
945
si
946
947
// C# records TupleElementNamesAttribute on the property itself;
948
// a ghūl assembly built before the property carried it holds
949
// the names only on the getter's `.param [0]`, which reflects
950
// as the return parameter rather than as a method attribute.
951
// Every reflected property reaches here, so neither carrier is
952
// read until the type is known to be a tuple.
953
_with_property_tuple_element_names(
954
type: Types.Type,
955
property: PropertyInfo
956
) -> Types.Type is
957
if !type.is_value_tuple then
958
return type;
959
fi
960
961
let result = _with_tuple_element_names(type, property.get_custom_attributes_data());
962
963
if result.tuple_element_names? then
964
return result;
965
fi
966
967
let getter = property.get_get_method();
968
969
if !getter? then
970
return result;
971
fi
972
973
return _with_tuple_element_names(result, getter.return_parameter.get_custom_attributes_data());
974
si
975
976
// If `type` is a value tuple and `attributes` carry a
977
// TupleElementNamesAttribute, return the tuple rebuilt with the
978
// recovered element names; otherwise return `type` unchanged.
979
// The reflected ValueTuple type itself has no names.
980
_with_tuple_element_names(
981
type: Types.Type,
982
attributes: Collections.Iterable[System.Reflection.CustomAttributeData]
983
) -> Types.Type is
984
if !type.is_value_tuple then
985
return type;
986
fi
987
988
let names = TUPLE_ELEMENT_NAMES.read(attributes);
989
990
if !names? then
991
return type;
992
fi
993
994
return type.apply_tuple_element_names(names);
995
si
996
997
has_union_attribute(type: TYPE) -> bool is
998
try
999
for attr in type.get_custom_attributes_data() do
1000
let attr_type = attr.attribute_type;
1001
1002
if attr_type.full_name =~ "Ghul.Internal.UNION_ATTRIBUTE" then
1003
return true;
1004
fi
1005
od
1006
catch ex: System.Exception
1007
yrt
1008
1009
return false;
1010
si
1011
1012
has_variant_attribute(type: TYPE) -> bool is
1013
try
1014
for attr in type.get_custom_attributes_data() do
1015
let attr_type = attr.attribute_type;
1016
1017
if attr_type.full_name =~ "Ghul.Internal.VARIANT_ATTRIBUTE" then
1018
return true;
1019
fi
1020
od
1021
catch ex: System.Exception
1022
yrt
1023
1024
return false;
1025
si
1026
1027
has_default_variant_attribute(type: TYPE) -> bool is
1028
try
1029
for attr in type.get_custom_attributes_data() do
1030
let attr_type = attr.attribute_type;
1031
1032
if attr_type.full_name =~ "Ghul.Internal.DEFAULT_VARIANT_ATTRIBUTE" then
1033
return true;
1034
fi
1035
od
1036
catch ex: System.Exception
1037
yrt
1038
1039
return false;
1040
si
1041
1042
_has_pure_attribute(attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> bool is
1043
try
1044
for attr in attributes do
1045
let attr_type = attr.attribute_type;
1046
1047
if attr_type.full_name =~ "Ghul.Internal.PURE_ATTRIBUTE" then
1048
return true;
1049
fi
1050
od
1051
catch ex: System.Exception
1052
yrt
1053
1054
return false;
1055
si
1056
1057
// The intrinsic operation name (e.g. "arithmetic.add") carried by
1058
// a method's INTRINSIC_ATTRIBUTE, or null if it carries none. An
1059
// imported method bearing the marker is reconstructed as an innate
1060
// function whose calls lower to IL opcodes, matching a source-
1061
// declared intrinsic.
1062
_intrinsic_operation(attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> string? is
1063
try
1064
for attr in attributes do
1065
if attr.attribute_type.full_name =~ "Ghul.Internal.INTRINSIC_ATTRIBUTE" then
1066
for argument in attr.constructor_arguments do
1067
return cast string?(argument.value);
1068
od
1069
fi
1070
od
1071
catch ex: System.Exception
1072
yrt
1073
1074
return null;
1075
si
1076
1077
// A parameter or return slot carrying the purity marker has a
1078
// pure top-level function type: rebuild the mapped type in
1079
// its pure shape. Slots whose mapped type is not a function
1080
// type (or is the zero-argument void form, which has no pure
1081
// shape) keep the plain type — conservative on both sides.
1082
_with_pure_function(type: Types.Type, attributes: Collections.Iterable[System.Reflection.CustomAttributeData]) -> Types.Type is
1083
if !type.is_function \/ !isa Types.GENERIC(type) \/ !_has_pure_attribute(attributes) then
1084
return type;
1085
fi
1086
1087
let lookup = IoC.CONTAINER.instance.innate_symbol_lookup;
1088
1089
let types = Collections.LIST[Types.Type]();
1090
1091
for a in (cast Types.GENERIC(type)).arguments do
1092
types.add(a);
1093
od
1094
1095
if type.is_action then
1096
types.add(lookup.get_void_type());
1097
fi
1098
1099
return lookup.get_function_type(types, true);
1100
si
1101
1102
has_closed_attribute(type: TYPE) -> bool is
1103
try
1104
for attr in type.get_custom_attributes_data() do
1105
let attr_type = attr.attribute_type;
1106
1107
if attr_type.full_name =~ "Ghul.Internal.CLOSED_ATTRIBUTE" then
1108
return true;
1109
fi
1110
od
1111
catch ex: System.Exception
1112
yrt
1113
1114
return false;
1115
si
1116
1117
// Cross-assembly variant materialization: when reflecting a
1118
// union from another assembly, the variant classes are
1119
// sibling top-level types whose `Namespace` equals the
1120
// union's full name (assemblies.ghul: import_type queues
1121
// them into TYPE_DETAILS_LOOKUP by union full name instead
1122
// of registering them as top-level). When the union's
1123
// symbol is created and its members populated, call this
1124
// to also materialize the variants as members of the union.
1125
materialize_variants(union_symbol: Symbols.UNION, union_type: TYPE) is
1126
let union_full_name = union_type.full_name;
1127
1128
if !union_full_name? then
1129
return;
1130
fi
1131
1132
let variants = _type_details_lookup.get_variants_for_union(union_full_name);
1133
1134
if !variants? then
1135
return;
1136
fi
1137
1138
for variant_type in variants do
1139
materialize_variant(union_symbol, variant_type);
1140
od
1141
si
1142
1143
materialize_variant(union_symbol: Symbols.UNION, variant_type: TYPE) is
1144
let variant_name = variant_type.name;
1145
let assembly_name = get_assembly_name(variant_type);
1146
let assembly_version =
1147
variant_type.assembly.get_name().version!.to_string().replace('.', ':');
1148
1149
let arguments = LIST();
1150
let argument_variances = LIST();
1151
1152
if variant_type.is_generic_type /\ variant_type.contains_generic_parameters then
1153
for a in variant_type.get_generic_arguments() do
1154
if a.is_generic_type_parameter then
1155
arguments.add(a.name);
1156
argument_variances.add(_type_mapper.map_type_argument_variance(a));
1157
fi
1158
od
1159
fi
1160
1161
let variant_symbol =
1162
Symbols.VARIANT(
1163
Source.LOCATION.reflected,
1164
Source.LOCATION.reflected,
1165
union_symbol,
1166
variant_name,
1167
arguments,
1168
union_symbol
1169
);
1170
1171
variant_symbol.mark_overrides_resolved();
1172
1173
if argument_variances.count > 0 then
1174
variant_symbol.argument_variances = argument_variances;
1175
fi
1176
1177
variant_symbol.il_assembly_name = assembly_name;
1178
variant_symbol.il_name_override = get_il_name(variant_type);
1179
1180
_referenced_assemblies.add(assembly_name, assembly_version);
1181
1182
// Register the variant in the lookup so subsequent
1183
// dotnet-type-keyed lookups (`get_symbol(type)`) and
1184
// resolution paths can find it.
1185
let variant_td =
1186
TYPE_DETAILS(
1187
variant_type,
1188
union_symbol.qualified_name,
1189
variant_name,
1190
variant_symbol.il_name_override,
1191
assembly_name,
1192
assembly_version
1193
);
1194
1195
union_symbol.add_member(variant_symbol);
1196
1197
add_ancestors(variant_symbol, variant_type);
1198
add_members(variant_symbol, variant_type);
1199
resolve_overrides(variant_symbol);
1200
1201
// Populate the variant's `_field_names` list from the
1202
// .NET instance-field declaration order so positional
1203
// destructuring (`let (v, r) = step`) works the same
1204
// way it does for an intra-assembly variant. Source-
1205
// defined variants get this filled by
1206
// `declare_variable`; reflected variants need to do it
1207
// explicitly here.
1208
for `field in variant_type.get_fields() do
1209
if !`field.is_static /\ `field.is_public then
1210
variant_symbol.register_reflected_field_name(`field.name);
1211
fi
1212
od
1213
1214
// Wire the parent union's `default_variant` pointer to
1215
// the marked variant so cross-asm `u?` / `u!` lowers to
1216
// isa/cast against this variant — matching what source-
1217
// defined unions get from declare_members.
1218
if has_default_variant_attribute(variant_type) then
1219
variant_symbol.is_default = true;
1220
union_symbol.default_variant = variant_symbol;
1221
fi
1222
si
1223
1224
get_il_name(type: TYPE) -> string is
1225
let buffer = System.Text.StringBuilder();
1226
1227
let full_name: string? = type.full_name;
1228
assert full_name? else "get il name type has no full name: {type}";
1229
1230
let seen_any mut = false;
1231
for name in full_name.split(['.']) do
1232
if seen_any then
1233
buffer.append('.');
1234
fi
1235
1236
if name.contains('+') then
1237
1238
let inner_seen_any mut = false;
1239
for inner_name in name.split(['+']) do
1240
if inner_seen_any then
1241
buffer.append('/');
1242
fi
1243
1244
buffer
1245
.append('\'')
1246
.append(inner_name)
1247
.append('\'');
1248
1249
inner_seen_any = true;
1250
od
1251
1252
else
1253
buffer
1254
.append('\'')
1255
.append(name)
1256
.append('\'');
1257
fi
1258
1259
seen_any = true;
1260
od
1261
1262
return buffer.to_string();
1263
si
1264
1265
// Reflect the .NET-declared default value of a parameter into
1266
// the string form that `Function.argument_defaults` expects.
1267
// - null ⇒ no default ⇒ parameter is not omittable.
1268
// - "default" ⇒ omittable, fill with `IR.Values.DEFAULT` (the
1269
// .NET parameter is a reference type with `= null`, or a
1270
// `Nullable<T>` with no constant value).
1271
// - any other string ⇒ a constant literal, formatted with
1272
// invariant culture so the emission side can read it back
1273
// independent of host locale.
1274
//
1275
// Conservative gate: only primitive numeric types, bool, char,
1276
// string, and enums are stored. Anything else (decimal,
1277
// struct, …) returns null so the parameter is treated as not
1278
// omittable rather than silently filled with a zero default.
1279
_default_for_parameter(a: ParameterInfo) -> string? is
1280
if !a.has_default_value then
1281
return null;
1282
fi
1283
1284
let raw = a.raw_default_value;
1285
1286
if !raw? then
1287
return "default";
1288
fi
1289
1290
let pt = a.parameter_type;
1291
1292
if !pt.is_primitive /\ !pt.is_enum /\ pt.full_name !~ "System.String" then
1293
return null;
1294
fi
1295
1296
let convertible = cast System.IConvertible?(raw);
1297
if !convertible? then
1298
return null;
1299
fi
1300
1301
return convertible.to_string(
1302
System.Globalization.CultureInfo.invariant_culture
1303
);
1304
si
1305
si
1306
si