Appearance
| 1 | namespace Semantic.DotNet is | |
| 2 | use Collections | |
| 3 | ||
| 4 | use TYPE = System.Type | |
| 5 | ||
| 6 | // Attributes applied to the assembly itself, each requested as the | |
| 7 | // attribute's full name and a string value. A request is matched to a | |
| 8 | // public constructor of the attribute taking only strings before | |
| 9 | // anything is emitted: one taking a single string is given the whole | |
| 10 | // value, and failing that one taking two is given the value split at | |
| 11 | // its first comma, which is how an attribute such as | |
| 12 | // AssemblyMetadataAttribute is given a key and a value. | |
| 13 | class ASSEMBLY_INFO(_context: IR.CONTEXT) is | |
| 14 | _requested: LIST[(name: string, value: string)] | |
| 15 | _resolved: LIST[(name: string, assembly_name: string, arguments: List[string])] | |
| 16 | ||
| 17 | UNMATCHED: string static => "assembly-attribute-unmatched" | |
| 18 | ||
| 19 | init(..) is | |
| 20 | _requested = LIST[(name: string, value: string)]() | |
| 21 | _resolved = LIST[(name: string, assembly_name: string, arguments: List[string])]() | |
| 22 | si | |
| 23 | ||
| 24 | add_attribute(name: string, value: string) is | |
| 25 | _requested.add((name = name, value = value)) | |
| 26 | si | |
| 27 | ||
| 28 | // Matches every requested attribute to a constructor, returning a | |
| 29 | // message for each that cannot be. Only the matched ones are | |
| 30 | // emitted, so nothing refers to a constructor that does not exist, | |
| 31 | // and an unmatched one is left out of the assembly. | |
| 32 | resolve(find_type: (string) -> TYPE?) -> List[string] is | |
| 33 | let problems = LIST[string]() | |
| 34 | ||
| 35 | _resolved.clear() | |
| 36 | ||
| 37 | // Found the same way as the attributes, so that it is the string | |
| 38 | // type their constructors are declared with: a type loaded for | |
| 39 | // inspection is not the running process's own. | |
| 40 | let string_type = find_type("System.String") | |
| 41 | ||
| 42 | for request in _requested do | |
| 43 | let type = find_type(request.name) | |
| 44 | ||
| 45 | if !type? then | |
| 46 | problems.add("assembly attribute {request.name} not found") | |
| 47 | ||
| 48 | continue | |
| 49 | fi | |
| 50 | ||
| 51 | let arguments = | |
| 52 | if let text = string_type then | |
| 53 | arguments_for(string_constructor_arities(type, text), request.value) | |
| 54 | else | |
| 55 | null | |
| 56 | fi | |
| 57 | ||
| 58 | if let matched = arguments then | |
| 59 | _resolved.add(( | |
| 60 | name = request.name, | |
| 61 | assembly_name = type.assembly.get_name().name ?? "System.Runtime", | |
| 62 | arguments = matched | |
| 63 | )) | |
| 64 | else | |
| 65 | problems.add( | |
| 66 | "assembly attribute {request.name} has no constructor taking " | |
| 67 | "{if request.value.contains(',') then "one or two strings" else "one string" fi}" | |
| 68 | ) | |
| 69 | fi | |
| 70 | od | |
| 71 | ||
| 72 | return problems | |
| 73 | si | |
| 74 | ||
| 75 | // How many strings each public constructor taking nothing but | |
| 76 | // strings takes. | |
| 77 | string_constructor_arities(type: TYPE, string_type: TYPE) -> LIST[int] static is | |
| 78 | let result = LIST[int]() | |
| 79 | ||
| 80 | for constructor in type.get_constructors() do | |
| 81 | let parameters = constructor.get_parameters() | |
| 82 | let all_strings mut = true | |
| 83 | ||
| 84 | for parameter in parameters do | |
| 85 | if parameter.parameter_type != string_type then | |
| 86 | all_strings = false | |
| 87 | fi | |
| 88 | od | |
| 89 | ||
| 90 | if all_strings then | |
| 91 | result.add(parameters.count) | |
| 92 | fi | |
| 93 | od | |
| 94 | ||
| 95 | return result | |
| 96 | si | |
| 97 | ||
| 98 | // The constructor arguments a value supplies, given the arities of | |
| 99 | // the string-only constructors on offer, or null if none fits. | |
| 100 | arguments_for(arities: LIST[int], value: string) -> List[string]? static is | |
| 101 | if arities.contains(1) then | |
| 102 | let whole = LIST[string]() | |
| 103 | ||
| 104 | whole.add(value) | |
| 105 | ||
| 106 | return whole | |
| 107 | fi | |
| 108 | ||
| 109 | let comma = value.index_of(',') | |
| 110 | ||
| 111 | if arities.contains(2) /\ comma >= 0 then | |
| 112 | let pair = LIST[string]() | |
| 113 | ||
| 114 | pair.add(value.substring(0, comma).trim()) | |
| 115 | pair.add(value.substring(comma + 1).trim()) | |
| 116 | ||
| 117 | return pair | |
| 118 | fi | |
| 119 | ||
| 120 | return null | |
| 121 | si | |
| 122 | ||
| 123 | gen(emitter: IR.Emitter.SRM_ASSEMBLY_EMITTER) is | |
| 124 | for attribute in _resolved do | |
| 125 | let argument_types = LIST[Types.Type]() | |
| 126 | ||
| 127 | for _ in attribute.arguments do | |
| 128 | argument_types.add(IoC.CONTAINER.instance.innate_symbol_lookup.get_string_type()) | |
| 129 | od | |
| 130 | ||
| 131 | emitter.add_named_attribute( | |
| 132 | emitter.assembly_handle, | |
| 133 | attribute.assembly_name, | |
| 134 | attribute.name, | |
| 135 | argument_types, | |
| 136 | IR.Emitter.SRM_ATTRIBUTE_BLOB_ENCODER.encode_string_arguments(attribute.arguments)) | |
| 137 | od | |
| 138 | si | |
| 139 | si | |
| 140 | si |