Appearance
| 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 |