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