Appearance
| 1 | namespace Syntax.Process is | |
| 2 | use System.Reflection.Metadata.ILOpCode | |
| 3 | use IO.Std | |
| 4 | ||
| 5 | use System.Text.StringBuilder | |
| 6 | ||
| 7 | use Logging | |
| 8 | use Trees | |
| 9 | use Source | |
| 10 | ||
| 11 | use IR | |
| 12 | use IR.Values | |
| 13 | ||
| 14 | use Ghul.Pipes | |
| 15 | ||
| 16 | ||
| 17 | // Type-level walks: namespace, class, partial and impl blocks, trait, struct, union, | |
| 18 | // variant, enum, plus bodies, properties, pragmas and test attributes. | |
| 19 | partial GENERATE_IL is | |
| 20 | enter_class(symbol: Semantic.Symbols.Classy) is | |
| 21 | ADDRESS.reset_id() | |
| 22 | TEMP.reset_id() | |
| 23 | LABEL.reset_id() | |
| 24 | ||
| 25 | // A body that fails to emit unwinds to `Classy.walk`, which | |
| 26 | // restores the symbol table and the diagnostics but knows | |
| 27 | // nothing about emission state. Without this the abandoned | |
| 28 | // body emitter is still installed when the next class opens, | |
| 29 | // and everything that class emits is reported against it. | |
| 30 | _context.current_srm_body_emitter = null | |
| 31 | si | |
| 32 | ||
| 33 | leave_class(symbol: Semantic.Symbols.Classy) is | |
| 34 | si | |
| 35 | ||
| 36 | pre(`namespace: Definitions.NAMESPACE) -> bool => super.pre(`namespace) | |
| 37 | ||
| 38 | visit(`namespace: Definitions.NAMESPACE) is | |
| 39 | let context = _symbol_table.current_closure_context | |
| 40 | ||
| 41 | gen_closures(context.get_closures()) | |
| 42 | ||
| 43 | emit_pending_state_machines() | |
| 44 | ||
| 45 | super.visit(`namespace) | |
| 46 | si | |
| 47 | ||
| 48 | // A `reset` the type never declared has no body walked from a | |
| 49 | // tree, so its throw is deposited here. | |
| 50 | _gen_synthesized_reset(symbol: Semantic.Symbols.Classy) is | |
| 51 | let found = symbol.find_direct("reset") | |
| 52 | ||
| 53 | let reset = | |
| 54 | if let group: Semantic.Symbols.FUNCTION_GROUP = found then | |
| 55 | group.functions | |
| 56 | |> Ghul.Pipes.find(f => f.throws_not_supported) | |
| 57 | elif let single: Semantic.Symbols.Function = found /\ single.throws_not_supported then | |
| 58 | single | |
| 59 | else | |
| 60 | null | |
| 61 | fi | |
| 62 | ||
| 63 | if let reset0 = reset then | |
| 64 | ||
| 65 | let assembly_emitter = _context.srm_assembly_emitter | |
| 66 | let enclosing = _context.current_srm_body_emitter | |
| 67 | let body = IR.Emitter.SRM_METHOD_BODY_EMITTER() | |
| 68 | ||
| 69 | _context.current_srm_body_emitter = body | |
| 70 | ||
| 71 | try | |
| 72 | IR.Values.NEW_NOT_SUPPORTED_EXCEPTION().gen(_context) | |
| 73 | ||
| 74 | body.op(System.Reflection.Metadata.ILOpCode.THROW) | |
| 75 | ||
| 76 | assembly_emitter.handles.set_body_offset( | |
| 77 | reset0, body.flush(assembly_emitter)) | |
| 78 | finally | |
| 79 | _context.current_srm_body_emitter = enclosing | |
| 80 | yrt | |
| 81 | fi | |
| 82 | si | |
| 83 | ||
| 84 | pre(`class: Definitions.CLASS) -> bool is | |
| 85 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`class))! | |
| 86 | ||
| 87 | enter_class(symbol) | |
| 88 | ||
| 89 | let owner = cast Semantic.Symbols.Symbol?(symbol.owner)! | |
| 90 | ||
| 91 | ||
| 92 | enter_scope(`class) | |
| 93 | ||
| 94 | ||
| 95 | return false | |
| 96 | si | |
| 97 | ||
| 98 | visit(`class: Definitions.CLASS) is | |
| 99 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`class))! | |
| 100 | ||
| 101 | let closures = symbol.get_closures() | |
| 102 | ||
| 103 | ||
| 104 | _gen_synthesized_reset(symbol) | |
| 105 | ||
| 106 | leave_class(symbol) | |
| 107 | ||
| 108 | leave_scope(`class) | |
| 109 | ||
| 110 | gen_closures(closures) | |
| 111 | ||
| 112 | emit_pending_state_machines() | |
| 113 | si | |
| 114 | ||
| 115 | // A `partial` block contributes members to a type declared | |
| 116 | // elsewhere; the member order collects them onto that type's one | |
| 117 | // row. The class header (extends/implements/attributes) is the primary | |
| 118 | // definition's job, so this only walks the block's members; closures | |
| 119 | // declared here are emitted by visit(partial), after their bodies are | |
| 120 | // walked. | |
| 121 | pre(`partial: Definitions.PARTIAL) -> bool is | |
| 122 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`partial))! | |
| 123 | ||
| 124 | enter_class(symbol) | |
| 125 | ||
| 126 | ||
| 127 | enter_scope(`partial) | |
| 128 | ||
| 129 | ||
| 130 | return false | |
| 131 | si | |
| 132 | ||
| 133 | visit(`partial: Definitions.PARTIAL) is | |
| 134 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`partial))! | |
| 135 | ||
| 136 | // Closures declared in this block are tracked on its injection | |
| 137 | // scope and emitted here, after the member walk has generated | |
| 138 | // their bodies - the target's primary definition runs before | |
| 139 | // this block is walked and would emit them bodyless. | |
| 140 | let injection = cast Semantic.INJECTION_SCOPE?(scope_for(`partial)) | |
| 141 | let closures = injection?.get_closures() | |
| 142 | ||
| 143 | ||
| 144 | ||
| 145 | leave_class(symbol) | |
| 146 | ||
| 147 | leave_scope(`partial) | |
| 148 | ||
| 149 | gen_closures(closures) | |
| 150 | ||
| 151 | emit_pending_state_machines() | |
| 152 | si | |
| 153 | ||
| 154 | // An `impl` block contributes members to its target exactly as | |
| 155 | // `partial` does. The interface the target now implements is carried on | |
| 156 | // the target symbol, so the InterfaceImpl row is written from the | |
| 157 | // primary definition. | |
| 158 | pre(`impl: Definitions.IMPL) -> bool is | |
| 159 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`impl))! | |
| 160 | ||
| 161 | enter_class(symbol) | |
| 162 | ||
| 163 | ||
| 164 | enter_scope(`impl) | |
| 165 | ||
| 166 | ||
| 167 | return false | |
| 168 | si | |
| 169 | ||
| 170 | visit(`impl: Definitions.IMPL) is | |
| 171 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`impl))! | |
| 172 | ||
| 173 | // As visit(partial): closures declared in this block are tracked | |
| 174 | // on its injection scope and emitted here, after the member walk | |
| 175 | // has generated their bodies. | |
| 176 | let injection = cast Semantic.INJECTION_SCOPE?(scope_for(`impl)) | |
| 177 | let closures = injection?.get_closures() | |
| 178 | ||
| 179 | ||
| 180 | ||
| 181 | leave_class(symbol) | |
| 182 | ||
| 183 | leave_scope(`impl) | |
| 184 | ||
| 185 | gen_closures(closures) | |
| 186 | ||
| 187 | emit_pending_state_machines() | |
| 188 | si | |
| 189 | ||
| 190 | pre(`trait: Definitions.TRAIT) -> bool is | |
| 191 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`trait))! | |
| 192 | let owner = cast Semantic.Symbols.Symbol?(symbol.owner)! | |
| 193 | ||
| 194 | enter_class(symbol) | |
| 195 | ||
| 196 | ||
| 197 | enter_scope(`trait) | |
| 198 | ||
| 199 | ||
| 200 | ||
| 201 | return false | |
| 202 | si | |
| 203 | ||
| 204 | visit(`trait: Definitions.TRAIT) is | |
| 205 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`trait))! | |
| 206 | ||
| 207 | let closures = symbol.get_closures() | |
| 208 | ||
| 209 | ||
| 210 | ||
| 211 | leave_class(symbol) | |
| 212 | ||
| 213 | leave_scope(`trait) | |
| 214 | ||
| 215 | gen_closures(closures) | |
| 216 | ||
| 217 | emit_pending_state_machines() | |
| 218 | si | |
| 219 | ||
| 220 | pre(`struct: Definitions.STRUCT) -> bool is | |
| 221 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`struct))! | |
| 222 | let owner = cast Semantic.Symbols.Symbol?(symbol.owner)! | |
| 223 | ||
| 224 | enter_class(symbol) | |
| 225 | ||
| 226 | ||
| 227 | ||
| 228 | enter_scope(`struct) | |
| 229 | ||
| 230 | ||
| 231 | return false | |
| 232 | si | |
| 233 | ||
| 234 | visit(`struct: Definitions.STRUCT) is | |
| 235 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`struct))! | |
| 236 | ||
| 237 | let closures = symbol.get_closures() | |
| 238 | ||
| 239 | ||
| 240 | ||
| 241 | _gen_synthesized_reset(symbol) | |
| 242 | ||
| 243 | leave_class(symbol) | |
| 244 | ||
| 245 | leave_scope(`struct) | |
| 246 | ||
| 247 | gen_closures(closures) | |
| 248 | si | |
| 249 | ||
| 250 | pre(`union: Definitions.UNION) -> bool is | |
| 251 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(`union))! | |
| 252 | ||
| 253 | enter_class(symbol) | |
| 254 | ||
| 255 | let owner = cast Semantic.Symbols.Symbol?(symbol.owner)! | |
| 256 | ||
| 257 | ||
| 258 | enter_scope(`union) | |
| 259 | ||
| 260 | `union.name.walk(self) | |
| 261 | ||
| 262 | if `union.arguments? then | |
| 263 | `union.arguments.walk(self) | |
| 264 | fi | |
| 265 | ||
| 266 | `union.modifiers.walk(self) | |
| 267 | ||
| 268 | for member in `union.body |> filter(m => !isa Definitions.VARIANT(m)) do | |
| 269 | member.walk(self) | |
| 270 | od | |
| 271 | ||
| 272 | ||
| 273 | ||
| 274 | ||
| 275 | ||
| 276 | leave_class(symbol) | |
| 277 | ||
| 278 | leave_scope(`union) | |
| 279 | ||
| 280 | for member in `union.body |> filter(m => isa Definitions.VARIANT(m)) do | |
| 281 | member.walk(self) | |
| 282 | od | |
| 283 | ||
| 284 | ||
| 285 | return true | |
| 286 | si | |
| 287 | ||
| 288 | visit(`union: Definitions.UNION) is | |
| 289 | // all done in pre | |
| 290 | si | |
| 291 | ||
| 292 | pre(variant: Definitions.VARIANT) -> bool is | |
| 293 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(variant))! | |
| 294 | ||
| 295 | enter_class(symbol) | |
| 296 | ||
| 297 | let owner = cast Semantic.Symbols.Symbol?(symbol.owner)! | |
| 298 | ||
| 299 | ||
| 300 | enter_scope(variant) | |
| 301 | ||
| 302 | ||
| 303 | return false | |
| 304 | si | |
| 305 | ||
| 306 | visit(variant: Definitions.VARIANT) is | |
| 307 | let symbol = cast Semantic.Symbols.Classy?(symbol_for(variant))! | |
| 308 | ||
| 309 | let closures = symbol.get_closures() | |
| 310 | ||
| 311 | gen_unit_variant_singleton(symbol) | |
| 312 | ||
| 313 | ||
| 314 | leave_class(symbol) | |
| 315 | ||
| 316 | leave_scope(variant) | |
| 317 | ||
| 318 | gen_closures(closures) | |
| 319 | si | |
| 320 | ||
| 321 | // For a unit variant (no constructor parameters / no | |
| 322 | // instance fields), emit a static `_instance` field plus a | |
| 323 | // `.cctor` that allocates exactly one instance. `IR.Values.NEW` | |
| 324 | // redirects construction sites for unit variants to a `ldsfld` | |
| 325 | // of this field, so `RED()` (or `NONE[int]()`) returns the | |
| 326 | // cached singleton instead of allocating each call. | |
| 327 | gen_unit_variant_singleton(symbol: Semantic.Symbols.Classy) is | |
| 328 | if let variant_symbol: Semantic.Symbols.VARIANT = symbol then | |
| 329 | if !variant_symbol.is_unit_variant then | |
| 330 | return | |
| 331 | fi | |
| 332 | ||
| 333 | _gen_unit_variant_singleton_srm(variant_symbol) | |
| 334 | fi | |
| 335 | si | |
| 336 | ||
| 337 | // The rows for the field and this initializer are written by the | |
| 338 | // structure walk, which cannot encode a body; the body is | |
| 339 | // deposited here, in the pass that encodes every other body. | |
| 340 | _gen_unit_variant_singleton_srm(variant: Semantic.Symbols.VARIANT) is | |
| 341 | let assembly_emitter = _context.srm_assembly_emitter | |
| 342 | let handles = assembly_emitter.handles | |
| 343 | ||
| 344 | let instance = handles.unit_variant_instance(variant) | |
| 345 | ||
| 346 | if !instance? then | |
| 347 | return | |
| 348 | fi | |
| 349 | ||
| 350 | let constructors = cast Semantic.Symbols.FUNCTION_GROUP?(variant.find_direct("init")) | |
| 351 | ||
| 352 | if !constructors? \/ constructors.functions.count == 0 then | |
| 353 | return | |
| 354 | fi | |
| 355 | ||
| 356 | let body = IR.Emitter.SRM_METHOD_BODY_EMITTER() | |
| 357 | ||
| 358 | body.new_object(_context.resolve_call_target(constructors.functions[0])) | |
| 359 | body.stsfld(_context.resolve_unit_variant_instance(variant)) | |
| 360 | body.ret() | |
| 361 | ||
| 362 | handles.set_unit_variant_initializer_body(variant, body.flush(assembly_emitter)) | |
| 363 | si | |
| 364 | ||
| 365 | ||
| 366 | ||
| 367 | gen_closures(closures: Collections.Iterable[Semantic.Symbols.Closure]?) is | |
| 368 | if !closures? then | |
| 369 | return | |
| 370 | fi | |
| 371 | ||
| 372 | for closure in closures |> filter(c => !c.is_anon_func /\ !c.is_delegate) do | |
| 373 | closure.gen_frame(_context, _symbol_loader) | |
| 374 | od | |
| 375 | ||
| 376 | for closure in closures |> filter(c => c.pack_thunk?) do | |
| 377 | _gen_pack_thunk(closure) | |
| 378 | od | |
| 379 | si | |
| 380 | ||
| 381 | pre(`enum: Definitions.ENUM) -> bool is | |
| 382 | let symbol = cast Semantic.Symbols.Symbol?(symbol_for(`enum))! | |
| 383 | ||
| 384 | let owner = cast Semantic.Symbols.Symbol?(symbol.owner)! | |
| 385 | ||
| 386 | ||
| 387 | enter_scope(`enum) | |
| 388 | ||
| 389 | ||
| 390 | return false | |
| 391 | si | |
| 392 | ||
| 393 | visit(`enum: Definitions.ENUM) is | |
| 394 | ||
| 395 | leave_scope(`enum) | |
| 396 | si | |
| 397 | ||
| 398 | pre(enum_member: Definitions.ENUM_MEMBER) -> bool is | |
| 399 | return false | |
| 400 | si | |
| 401 | pre(body: Bodies.BLOCK) -> bool is | |
| 402 | add(Values.MAX_STACK(64)) | |
| 403 | ||
| 404 | return super.pre(body) | |
| 405 | si | |
| 406 | ||
| 407 | pre(expression_body: Bodies.EXPRESSION) -> bool is | |
| 408 | add(Values.MAX_STACK(64)) | |
| 409 | ||
| 410 | return super.pre(expression_body) | |
| 411 | si | |
| 412 | ||
| 413 | visit(expression_body: Bodies.EXPRESSION) is | |
| 414 | // An `=> body` whose function awaits is emitted after this | |
| 415 | // walk, by the state machine's own emitter, which puts its | |
| 416 | // value in the frame's result. Nothing is emitted here: the | |
| 417 | // `ret` below would return out of MoveNext without ever | |
| 418 | // completing the builder. | |
| 419 | if let casm = _current_async_state_machine, function = current_function then | |
| 420 | if function == casm.function then | |
| 421 | super.visit(expression_body) | |
| 422 | ||
| 423 | return | |
| 424 | fi | |
| 425 | fi | |
| 426 | ||
| 427 | let body_value = expression_body.expression.value | |
| 428 | if let value = body_value, value_type = value.type then | |
| 429 | let return_type = current_function!.return_type! | |
| 430 | ||
| 431 | // A void body discards whatever its expression leaves | |
| 432 | // standing, the same as a block body's tail: emit it | |
| 433 | // for its effects, then pop what it pushed. | |
| 434 | if return_type.is_void /\ !value_type.is_void then | |
| 435 | add(value) | |
| 436 | add(Values.INSTRUCTION(ILOpCode.POP)) | |
| 437 | else | |
| 438 | add(_boxer.box_if_needed(value, return_type)) | |
| 439 | fi | |
| 440 | elif let | |
| 441 | self.current_function? /\ | |
| 442 | current_function.return_type? /\ | |
| 443 | !current_function.return_type.is_void | |
| 444 | then | |
| 445 | // Diverging body (`=> throw E`): the body's own IL | |
| 446 | // already terminates control flow. Emit a default-value | |
| 447 | // trailer so the unreachable fall-off is verifiable, | |
| 448 | // matching statement-bodied functions that end in throw. | |
| 449 | add(Values.DEFAULT_RETURN(current_function.return_type!)) | |
| 450 | fi | |
| 451 | ||
| 452 | add(Values.RET()) | |
| 453 | ||
| 454 | super.visit(expression_body) | |
| 455 | si | |
| 456 | ||
| 457 | pre(property: Definitions.PROPERTY) -> bool is | |
| 458 | return true | |
| 459 | si | |
| 460 | ||
| 461 | pre(pragma: Definitions.PRAGMA) -> bool is | |
| 462 | process_pragma(pragma.pragma, true) | |
| 463 | return false | |
| 464 | si | |
| 465 | ||
| 466 | visit(pragma: Definitions.PRAGMA) is | |
| 467 | process_pragma(pragma.pragma, false) | |
| 468 | si | |
| 469 | ||
| 470 | process_pragma(pragma: Pragmas.PRAGMA, is_enter: bool) is | |
| 471 | let name = pragma.name.to_string() | |
| 472 | ||
| 473 | if name =~ "IL.output" then | |
| 474 | if pragma.arguments.expressions.count != 1 then | |
| 475 | _logger.error(pragma.arguments.location, "expected 1 argument") | |
| 476 | return | |
| 477 | fi | |
| 478 | ||
| 479 | let argument = pragma.arguments.expressions[0] | |
| 480 | ||
| 481 | if !isa Expressions.Literals.STRING(argument) then | |
| 482 | _logger.error(pragma.arguments.location, "expected a string literal argument") | |
| 483 | return | |
| 484 | fi | |
| 485 | ||
| 486 | let file_name = argument.value_string | |
| 487 | ||
| 488 | if is_enter then | |
| 489 | _il_output_depth = _il_output_depth + 1 | |
| 490 | ||
| 491 | // Only a statement inside a body has instructions to | |
| 492 | // measure; a pragma on a declaration marks nothing. | |
| 493 | if _block_context.is_in_block then | |
| 494 | current_block.add(Values.ENTER_FILE(file_name)) | |
| 495 | fi | |
| 496 | else | |
| 497 | _il_output_depth = _il_output_depth - 1 | |
| 498 | ||
| 499 | if _block_context.is_in_block then | |
| 500 | current_block.add(Values.LEAVE_FILE(file_name)) | |
| 501 | fi | |
| 502 | fi | |
| 503 | fi | |
| 504 | si | |
| 505 | ||
| 506 | si | |
| 507 | si |