Skip to content
← Back

src/semantic/dotnet/interface_nullability.ghul

1
namespace Semantic.DotNet is
2
use Collections.MAP
3
use Collections.LIST
4
5
use TYPE = System.Type
6
use ASSEMBLY = System.Reflection.Assembly
7
8
use MDR = System.Reflection.Metadata.MetadataReader
9
use System.Reflection.Metadata.HandleKind
10
use System.Reflection.Metadata.TypeReferenceHandle
11
use System.Reflection.Metadata.TypeDefinitionHandle
12
use System.Reflection.Metadata.TypeSpecificationHandle
13
use System.Reflection.Metadata.MemberReferenceHandle
14
use System.Reflection.Metadata.BlobHandle
15
use System.Reflection.Metadata.InterfaceImplementation
16
use System.Reflection.Metadata.CustomAttribute
17
use System.Reflection.Metadata.Ecma335.MetadataTokens
18
19
// Reads the `NullableAttribute` an interface implementation carries
20
// on its own `InterfaceImpl` metadata row — the one reference-`?`
21
// carrier `NULLABILITY.resolve` can't reach cross-assembly, because
22
// .NET reflection has no `MemberInfo` for "this type's
23
// implementation of interface X" to hang `CustomAttributeData` off.
24
// A class's field, property, parameter and return slots all round-
25
// trip a `T?` type argument fine on import — this is specifically
26
// about a type argument in the class's own ancestor list, e.g.
27
// `class THING: MutableMap[string, string?]`.
28
//
29
// Reads it the way the emitter writes it (`SRM_STRUCTURE_WALK`'s
30
// `_nullable_slot_attributes` on the `InterfaceImplementationHandle`
31
// `SRM_ASSEMBLY_EMITTER.add_interface_implementation` returns): via
32
// `System.Reflection.Metadata` directly against the assembly's raw
33
// bytes, since ordinary reflection (`System.Type`,
34
// `CustomAttributeData`) has no way to address this row at all.
35
//
36
// The result is keyed by the interface's own closed `System.Type`.
37
// `INTERFACE_TYPE_PROVIDER` decodes the full instantiation to that
38
// `Type`, via the same already-loaded assemblies the rest of the
39
// compiler reflects through, so the caller compares actual type
40
// identity rather than a name rendered from either side.
41
class INTERFACE_NULLABILITY is
42
// One entry per interface `declaring_type`'s own `TypeDef`
43
// implements directly (not one it only inherits via a base
44
// class), keyed by the interface's own closed `Type` — see
45
// `key_for`, which reflects the same interface to the same
46
// `Type` for comparison at the call site. The byte lists follow
47
// the same pre-order convention `NULLABILITY.apply_bytes`
48
// already reads: whatever the row's own head byte says, only a
49
// `2` anywhere in the tree ever turns a position optional, so
50
// this is safe to feed straight in.
51
//
52
// Empty when `reader` can't resolve `declaring_type`'s own
53
// `TypeDef`, or that `TypeDef` declares no interfaces with a
54
// `NullableAttribute` on their row — the caller's fallback,
55
// leaving every position at its non-optional default, is
56
// correct either way.
57
read_declared(
58
reader: MDR,
59
declaring_type: TYPE,
60
assembly_names: MAP[string,ASSEMBLY]
61
) -> MAP[TYPE, LIST[int]] static is
62
let result = MAP[TYPE, LIST[int]]()
63
64
let type_def = _type_definition(reader, declaring_type)
65
66
if !type_def? then
67
// `reader` can't resolve `declaring_type`'s own `TypeDef`
68
// — every interface argument stays at its non-optional
69
// default, the same outcome as an assembly with no
70
// nullability metadata at all.
71
return result
72
fi
73
74
let provider = INTERFACE_TYPE_PROVIDER(declaring_type.assembly, assembly_names)
75
76
for impl_handle in type_def.get_interface_implementations() do
77
// One interface implementation this reader can't resolve
78
// or decode leaves that one interface's arguments at
79
// their non-optional default — never the others in the
80
// same list.
81
try
82
let implementation = reader.get_interface_implementation(impl_handle)
83
let interface_type = _resolve(provider, reader, declaring_type, implementation.`interface)
84
85
if let bytes = _nullable_bytes(reader, implementation) then
86
result[interface_type] = bytes
87
fi
88
catch ex: System.Exception
89
yrt
90
od
91
92
return result
93
si
94
95
_type_definition(reader: MDR, declaring_type: TYPE) -> System.Reflection.Metadata.TypeDefinition? static is
96
try
97
return reader.get_type_definition(MetadataTokens.type_definition_handle(declaring_type.metadata_token))
98
catch ex: System.Exception
99
return null
100
yrt
101
si
102
103
// The matching key for a reflected interface `Type` from
104
// `type.get_interfaces()` — identity, unreduced: it already is
105
// the closed instantiation `read_declared` resolves through
106
// `INTERFACE_TYPE_PROVIDER`, so the two sides compare equal for
107
// the same interface directly.
108
key_for(interface_type: TYPE) -> TYPE static => interface_type
109
110
// `handle` is an `InterfaceImplementation`'s own `Interface`
111
// field: a bare `TypeReference`/`TypeDefinition` for a
112
// non-generic interface, or a `TypeSpecification` — a
113
// signature, not a handle a provider method resolves directly —
114
// for a closed generic one, decoded via `TypeSpecification`'s
115
// own `decode_signature`, which is what makes `provider`'s
116
// recursive calls back into itself well-typed for nested
117
// generics.
118
_resolve(
119
provider: INTERFACE_TYPE_PROVIDER,
120
reader: MDR,
121
declaring_type: TYPE,
122
handle: System.Reflection.Metadata.EntityHandle
123
) -> TYPE static is
124
case handle.kind
125
when HandleKind.TYPE_SPECIFICATION then
126
return reader.get_type_specification(cast TypeSpecificationHandle(handle))
127
.decode_signature[TYPE, TYPE](provider, declaring_type)
128
when HandleKind.TYPE_DEFINITION then
129
return provider.get_type_from_definition(reader, cast TypeDefinitionHandle(handle), 0ub)
130
when HandleKind.TYPE_REFERENCE then
131
return provider.get_type_from_reference(reader, cast TypeReferenceHandle(handle), 0ub)
132
else
133
throw System.NotSupportedException("unsupported interface handle kind {handle.kind}")
134
esac
135
si
136
137
// The pre-order nullability bytes off `implementation`'s own
138
// `NullableAttribute`, or absent when the row carries none, or
139
// carries some other attribute only.
140
_nullable_bytes(reader: MDR, implementation: InterfaceImplementation) -> LIST[int]? static is
141
for attribute_handle in implementation.get_custom_attributes() do
142
let attribute = reader.get_custom_attribute(attribute_handle)
143
144
if _is_nullable_attribute(reader, attribute) then
145
return _decode_value(reader, attribute.value)
146
fi
147
od
148
149
return null
150
si
151
152
_is_nullable_attribute(reader: MDR, attribute: CustomAttribute) -> bool static is
153
let constructor = attribute.constructor
154
155
if constructor.kind != HandleKind.MEMBER_REFERENCE then
156
return false
157
fi
158
159
let member_ref = reader.get_member_reference(cast MemberReferenceHandle(constructor))
160
let parent = member_ref.parent
161
162
if parent.kind != HandleKind.TYPE_REFERENCE then
163
return false
164
fi
165
166
let type_ref = reader.get_type_reference(cast TypeReferenceHandle(parent))
167
168
return reader.get_string(type_ref.`namespace) =~ "System.Runtime.CompilerServices" /\
169
reader.get_string(type_ref.name) =~ "NullableAttribute"
170
si
171
172
// `SRM_ATTRIBUTE_BLOB_ENCODER.encode_byte_argument` /
173
// `encode_byte_array_argument` write exactly two shapes, and
174
// their total lengths never overlap: prolog(2) + one byte +
175
// num-named(2) = 5 for a single-position attribute, or
176
// prolog(2) + count(4) + `count` bytes + num-named(2) = 8 +
177
// count otherwise. That is enough to tell the two apart without
178
// decoding the constructor's own signature.
179
_decode_value(reader: MDR, value_handle: BlobHandle) -> LIST[int]? static is
180
let blob mut = reader.get_blob_reader(value_handle)
181
let total = blob.length
182
183
if total < 5 \/ blob.read_u_int16() != 1us then
184
return null
185
fi
186
187
let result = LIST[int]()
188
189
if total == 5 then
190
result.add(cast int(blob.read_byte()))
191
elif total >= 8 then
192
let count = cast int(blob.read_u_int32())
193
194
if total != 8 + count then
195
return null
196
fi
197
198
for i in 0..count do
199
result.add(cast int(blob.read_byte()))
200
od
201
else
202
return null
203
fi
204
205
return result
206
si
207
si
208
si