Skip to content
← Back

src/syntax/process/generate-il/generate_il_async.ghul

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
// Async machinery: the async outer method, dispatch holders, frame construction and the
18
// async state-machine class emission.
19
partial GENERATE_IL is
20
// The outer method of an async function: everything
21
// ASYNC_METHOD_LAUNCH emits, plus the return that hands back the
22
// builder's task.
23
_emit_async_outer_method_srm(
24
symbol: Semantic.Symbols.Function,
25
launch: IR.Values.ASYNC_METHOD_LAUNCH
26
) is
27
let assembly_emitter = _context.srm_assembly_emitter
28
let enclosing = _context.current_srm_body_emitter
29
let body = IR.Emitter.SRM_METHOD_BODY_EMITTER()
30
31
_context.current_srm_body_emitter = body
32
33
try
34
launch.gen(_context)
35
36
body.ret()
37
38
assembly_emitter.handles.set_body_offset(symbol, body.flush(assembly_emitter))
39
assembly_emitter.handles.set_il_outputs(symbol, body.il_outputs)
40
finally
41
_context.current_srm_body_emitter = enclosing
42
yrt
43
si
44
45
// The frame type as it must be named in IL: constructed over the
46
// type arguments visible inside the owning function, so a frame
47
// declared in a generic owner is never referred to by its open
48
// form. Null when the frame is not generic.
49
_specialized_frame_type(
50
frame: Semantic.Symbols.Classy,
51
construction_type_args: Collections.List[Semantic.Types.Type]?,
52
location: Source.LOCATION
53
) -> Semantic.Types.Type? is
54
if !construction_type_args? then
55
return null
56
fi
57
58
return Semantic.Types.GENERIC(location, frame, construction_type_args)
59
si
60
61
// A frame field as it must be named from the outer method: on
62
// the frame's own instantiation, so the reference and the
63
// field's type are both in the outer method's terms rather than
64
// the frame's own.
65
_specialized_field(
66
frame: Semantic.Symbols.Classy,
67
`field: Semantic.Symbols.Field,
68
construction_type_args: Collections.List[Semantic.Types.Type]?,
69
location: Source.LOCATION
70
) -> Semantic.Symbols.Field is
71
if !construction_type_args? \/ construction_type_args.count == 0 then
72
return `field
73
fi
74
75
if let specialized =
76
_specialized_frame_type(frame, construction_type_args, location)
77
then
78
if let found: Semantic.Symbols.Field =
79
specialized.find_member(`field.name)
80
then
81
return found
82
fi
83
fi
84
85
return `field
86
si
87
88
// Async-function IL emission. Parallels
89
// `_pre_generator_function`: emits the outer method (newobj
90
// SM, builder.Start, return Task) and stashes a MoveNext
91
// BLOCK to be flushed when the enclosing class closes.
92
// MoveNext wraps the body in a try/catch routing exceptions
93
// through `builder.SetException` and success through
94
// `builder.SetResult`.
95
_pre_async_function(function: Definitions.FUNCTION, symbol: Semantic.Symbols.Function, async_state_machine: Semantic.Symbols.ASYNC_STATE_MACHINE) -> bool is
96
let frame = async_state_machine.frame
97
98
if !frame? then
99
_logger.error(function.location, "async function must return a task-like type — one carrying AsyncMethodBuilderAttribute, such as Tasks.TASK or Tasks.ValueTask")
100
return true
101
fi
102
103
frame.declare()
104
105
let construction_type_args = async_state_machine.get_construction_type_arguments()
106
107
let constructor =
108
_specialized_constructor(
109
frame, frame.constructor, construction_type_args, symbol.location)
110
111
// The local holding the state machine, and the type argument
112
// to builder.Start, both name the frame type. A frame in a
113
// generic owner is generic too, so both need the constructed
114
// form — the open form does not resolve at run time.
115
let state_machine_type =
116
_specialized_frame_type(frame, construction_type_args, symbol.location) ?? frame.type!
117
118
// ctor args: instance methods prepend `ldarg.0` for
119
// _outer_self, then each user-declared parameter via
120
// `ldarg '<name>'`. Build them as a sequence Value so
121
// ASYNC_METHOD_LAUNCH composes them in the right order.
122
let ctor_args_block = Values.BLOCK()
123
124
// The enclosing method's own arguments, forwarded
125
// positionally: an instance method's receiver occupies slot
126
// 0, so its declared arguments start at 1.
127
let slot mut = if symbol.is_emitted_with_receiver then 1 else 0 fi
128
129
if frame.outer_self_field? then
130
ctor_args_block.add(Values.Load.ARGUMENT_SLOT(0, "ldarg.0"))
131
fi
132
for arg_name in symbol.argument_names do
133
ctor_args_block.add(Values.Load.ARGUMENT_SLOT(slot, "ldarg '{arg_name}'"))
134
slot = slot + 1
135
od
136
ctor_args_block.close()
137
138
let task_type = symbol.return_type!
139
140
let launch = IR.Values.ASYNC_METHOD_LAUNCH(
141
task_type,
142
state_machine_type,
143
constructor,
144
ctor_args_block,
145
_specialized_field(
146
frame, frame.state_field, construction_type_args, symbol.location),
147
_specialized_field(
148
frame, frame.builder_field!, construction_type_args, symbol.location),
149
".sm"
150
)
151
152
_emit_async_outer_method_srm(symbol, launch)
153
154
// --- Walk the body into MoveNext's block ---
155
async_state_machine.install_body_emission_overrides()
156
157
enter_scope(function)
158
159
let move_next_block = Values.BLOCK()
160
161
enter_block(move_next_block)
162
163
add(Values.MAX_STACK(64))
164
165
// Cache the state field to a CLR local at MoveNext entry.
166
// Every AWAIT_SUSPEND keeps this local in sync via
167
// `dup; stloc; stfld` so the per-region dispatch (below)
168
// and the finally-body state guards read a stable value.
169
//
170
// Finally bodies (let-use auto-dispose and user-written
171
// `finally`) state-guard their work on the same local:
172
// when V_state >= 0 the function is mid-suspend (the
173
// `leave` from the AwaitUnsafeOnCompleted path fires the
174
// finally before exiting), and side effects must be
175
// skipped.
176
let outer_dispatch_holder =
177
_open_state_machine_entry(frame, frame.state_field, ".async_state")
178
179
// Labels used by the trailer: success fall-through and
180
// the end of the wrapping try/catch. The entire MoveNext
181
// body lives inside a `.try` whose handler routes
182
// exceptions through `builder.SetException(e)`.
183
let success_label = IR.LABEL()
184
let end_label = IR.LABEL()
185
186
// Stash the emission context so visit(LET-await),
187
// visit(AWAIT), and visit(RETURN) inside the body know
188
// they're inside this state machine and can reach the
189
// frame / labels they need to emit. Cleared after the
190
// body walk so anything else emitted at this nesting
191
// level (other functions, nested classes' visits)
192
// goes through the standard paths.
193
let prev_async_sm = _current_async_state_machine
194
let prev_success = _current_async_success_label
195
let prev_end = _current_async_end_label
196
_current_async_state_machine = async_state_machine
197
_current_async_success_label = success_label
198
_current_async_end_label = end_label
199
200
// Unpack any destructured formal arguments into their leaf
201
// frame fields before the body reads them. The plain path's
202
// AST walk does this from visit(Variables.VARIABLE); this
203
// path drives its own walk and never reaches those nodes.
204
_gen_destructured_formal_prologue(function)
205
206
// Walk the body. visit(AWAIT) appends AWAIT_SUSPEND IR
207
// Values into move_next_block; other statements emit
208
// normally.
209
if function.body? then
210
function.body.walk(self)
211
fi
212
213
// What the body produces goes into the frame's result before
214
// leaving to success, exactly as `visit(RETURN)` puts it there.
215
_gen_async_body_result(function.body, frame)
216
217
// Body fell off the end: leave success_label so the
218
// trailer fires.
219
add(Values.BRANCH_TO(ILOpCode.LEAVE, success_label))
220
221
_current_async_state_machine = prev_async_sm
222
_current_async_success_label = prev_success
223
_current_async_end_label = prev_end
224
225
// Populate the outer dispatch from the holder that
226
// accumulated awaits not enclosed in any inner `.try`.
227
_pop_async_dispatch_holder(outer_dispatch_holder)
228
229
leave_block()
230
leave_scope(function)
231
232
async_state_machine.uninstall_body_emission_overrides()
233
234
_pending_async_state_machines.add(
235
PENDING_ASYNC_STATE_MACHINE(
236
async_state_machine,
237
move_next_block,
238
success_label,
239
end_label
240
)
241
)
242
243
return true
244
si
245
246
// The state-local IL spelling of the innermost open
247
// dispatch holder — `'.async_state'` for async state
248
// machines, `'.gen_state'` for generators, null when no
249
// state machine is open. Used by `.try` emission and the
250
// finally-body state guard so the same code paths emit the
251
// right local name without each caller having to know.
252
_current_state_local_il() -> string? is
253
if _async_dispatch_stack.count == 0 then
254
return null
255
fi
256
return _async_dispatch_stack[_async_dispatch_stack.count - 1].state_local_il
257
si
258
259
// Declare a CLR `int32` local in the MoveNext frame, copy
260
// the state field into it, and push the outer dispatch
261
// holder bound to that local. Returns the pushed holder so
262
// the caller can pop it at the end of the body walk via
263
// `_pop_async_dispatch_holder`. Used by every state-machine
264
// entry point (named function, lambda closure, generator).
265
// `state_local_il` is the local's IL spelling —
266
// `'.async_state'` for async, `'.gen_state'` for
267
// generators — and travels with the holder so inner
268
// dispatch sites pick it up.
269
_open_state_machine_entry(
270
frame: Semantic.Symbols.STATE_MACHINE_FRAME_BASE,
271
state_field: Semantic.Symbols.Field,
272
state_local_il: string
273
) -> ASYNC_DISPATCH_HOLDER is
274
add(Values.DECLARE_LOCAL(state_local_il, _innate_symbol_lookup.get_int_type()))
275
add(_build_frame_field_load(frame, state_field))
276
add(Values.STORE_TEMP(state_local_il, _innate_symbol_lookup.get_int_type()))
277
278
let block = Values.BLOCK()
279
let top_label = IR.LABEL()
280
block.add(Values.MARK_LABEL(top_label))
281
add(block)
282
let holder = ASYNC_DISPATCH_HOLDER(block, state_local_il, top_label, true)
283
_async_dispatch_stack.add(holder)
284
return holder
285
si
286
287
// Push an inner dispatch holder at the top of a `.try` body
288
// if we're inside a state-machine method. Returns the
289
// holder (to pop later) or null when no state machine is
290
// open — the caller can pass that null back to
291
// `_maybe_pop_dispatch_holder` for symmetric cleanup
292
// without an `if` at every call site.
293
_maybe_push_dispatch_holder() -> ASYNC_DISPATCH_HOLDER? is
294
if !_current_state_local_il()? then
295
return null
296
fi
297
return _push_async_dispatch_holder()
298
si
299
300
_maybe_pop_dispatch_holder(holder: ASYNC_DISPATCH_HOLDER?) is
301
if holder? then
302
_pop_async_dispatch_holder(holder)
303
fi
304
si
305
306
// The state-machine frame for the current function — async
307
// or generator, whichever applies. Used by the BLOCK-with-
308
// suspend spill in `LIST.visit` / `visit(IF)` / `visit(CASE)`
309
// (in expression position) to allocate spill fields that
310
// survive across MoveNext re-entries.
311
_current_state_machine_frame() -> Semantic.Symbols.STATE_MACHINE_FRAME_BASE? is
312
let casm = _current_async_state_machine
313
314
if casm? /\ casm.frame? then
315
return casm.frame
316
fi
317
318
let cf = current_function
319
if cf? then
320
let sm = Semantic.Symbols.state_machine_for(cf)
321
if sm? /\ sm.frame? then
322
return sm.frame
323
fi
324
fi
325
326
return null
327
si
328
329
// Build the IR Value that loads from `field` on the current
330
// state-machine frame — used as the spilled BLOCK's stand-in
331
// value. The consumer that does `add(block)` reads this
332
// load when the BLOCK gens, in place of the original body
333
// IL.
334
_build_frame_field_load(frame: Semantic.Symbols.STATE_MACHINE_FRAME_BASE, spill_field: Semantic.Symbols.Field) -> IR.Values.Value =>
335
IR.Values.Load.INSTANCE_FIELD(
336
IR.Values.Load.REFERENCE_SELF(frame, frame.type),
337
spill_field
338
)
339
340
// The counterpart store: `value` is emitted between the frame
341
// reference and the `stfld`, so anything that leaves one item
342
// on the stack can stand in for it.
343
_build_frame_field_store(
344
frame: Semantic.Symbols.STATE_MACHINE_FRAME_BASE,
345
`field: Semantic.Symbols.Field,
346
value: IR.Values.Value
347
) -> IR.Values.Value =>
348
IR.Values.Store.INSTANCE_FIELD(
349
IR.Values.Load.REFERENCE_SELF(frame, frame.type),
350
`field,
351
value
352
)
353
354
// BLOCK-with-suspend spill path for `Statements.LIST` in
355
// expression position. The capture-mode LIST.visit wraps
356
// its body in lots of state-machine plumbing (dispatch
357
// holders, exception-handler temps, `.try` for `want
358
// _dispose`) that the spill path doesn't need — the body
359
// emits inline in outer current_block, the result lands
360
// in a frame field, and the BLOCK ends up holding just a
361
// load. Returns true if the spill path ran (caller skips
362
// the ordinary capture path); false otherwise.
363
_try_spill_block_with_suspend(list: Statements.LIST) -> bool is
364
let spiller = COMPOSITE_VALUE_SPILLER(
365
self,
366
list,
367
list.value,
368
_current_state_machine_frame()
369
)
370
371
if !spiller.is_spilling then
372
return false
373
fi
374
375
// Walk body in outer current_block. Statement IL —
376
// including visit(AWAIT) / visit(YIELD) suspend
377
// emissions and nested composites' own spills — lands
378
// in outer, in the same flat stream as the SM
379
// dispatcher.
380
for s in list.statements do
381
enter_node(s)
382
try
383
s.walk(self)
384
finally
385
leave_node(s)
386
yrt
387
od
388
389
// Tail-evaluate-and-spill. By now the tail's value is a
390
// clean load (any tail-internal suspends already
391
// hoisted by visit(AWAIT) / visit(YIELD), and any
392
// tail-internal composites already spilled themselves),
393
// so the ldarg.0 / value-IL / stfld sequence has no
394
// suspend between receiver push and stfld.
395
if let list.last?, last.value? then
396
spiller.emit_value(value)
397
fi
398
399
spiller.leave()
400
401
return true
402
si
403
404
405
// Record a (state, resume_label) pair with the innermost
406
// open dispatch holder, so its dispatch block (at the top
407
// of its `.try` body OR at MoveNext outer scope) routes
408
// cold-resume entries to {resume_label}. No-op outside a
409
// state machine.
410
_register_resume_with_dispatch(state: int, resume_label: IR.LABEL) is
411
if _async_dispatch_stack.count == 0 then
412
return
413
fi
414
let holder = _async_dispatch_stack[_async_dispatch_stack.count - 1]
415
holder.register(state, resume_label)
416
si
417
418
// Emit the `if V_state >= 0 → skip` guard at the top of a
419
// finally body in a state-machine method. The `leave` that
420
// ends a suspend (AwaitUnsafeOnCompleted or yield) fires the
421
// surrounding finally with state >= 0; the user's code must
422
// not run then. Real exits (state == -1) and exceptional
423
// unwinds run the finally normally.
424
//
425
// Returns the skip label so the caller can plant it at the
426
// end of the guarded code via `_close_finally_state_guard`.
427
// Returns null when not inside a state machine — caller
428
// emits the finally body unguarded.
429
_open_finally_state_guard() -> LABEL? is
430
let state_local_il = _current_state_local_il()
431
432
if !state_local_il? then
433
return null
434
fi
435
436
let skip = LABEL()
437
438
let int_type = _innate_symbol_lookup.get_int_type()
439
440
get_brancher_for_block().branch(
441
BRANCH.GE,
442
Values.Load.TEMP(state_local_il, int_type),
443
Values.Literal.NUMBER(0, int_type),
444
skip)
445
446
return skip
447
si
448
449
_close_finally_state_guard(skip: LABEL?) is
450
if skip? then
451
get_brancher_for_block().label(skip)
452
fi
453
si
454
455
// Push a new dispatch holder onto the stack and emit its
456
// placeholder block at the current emission point. Returns
457
// the holder so the caller can pop it once the protected
458
// region's body has been walked. Use when opening a `.try`
459
// body inside an async function or generator so the
460
// AWAIT_SUSPENDs / YIELDs that fire in the body register
461
// here and the dispatch ends up inside the same region as
462
// the targets it reaches. The new holder inherits its
463
// state-local IL spelling from the top of the stack — the
464
// outer holder (pushed at MoveNext entry) chose the name
465
// (`'.async_state'` for async, `'.gen_state'` for
466
// generators) so inner holders just match.
467
_push_async_dispatch_holder() -> ASYNC_DISPATCH_HOLDER is
468
assert _async_dispatch_stack.count > 0 else "no outer dispatch holder to inherit state-local name from"
469
let outer = _async_dispatch_stack[_async_dispatch_stack.count - 1]
470
let block = Values.BLOCK()
471
let top_label = IR.LABEL()
472
block.add(Values.MARK_LABEL(top_label))
473
add(block)
474
let holder = ASYNC_DISPATCH_HOLDER(block, outer.state_local_il, top_label, false)
475
_async_dispatch_stack.add(holder)
476
return holder
477
si
478
479
// Populate the holder's dispatch block from registered
480
// (state, cold_resume_label) entries and pop. No-op if no
481
// entries — keeps emitted IL minimal when a `.try` body
482
// contains no awaits. The popped region's states are handed
483
// to the enclosing holder against `top_label` (this region's
484
// first instruction), so a resuming MoveNext is routed from
485
// the MoveNext-entry dispatch directly into the innermost
486
// region holding the resume point, entering each region at
487
// its first instruction and skipping the statements between
488
// them — re-running those would re-execute hoisted-variable
489
// initializers and loop iterators that the frame fields
490
// already carry mid-suspend values for.
491
_pop_async_dispatch_holder(holder: ASYNC_DISPATCH_HOLDER) is
492
_populate_async_dispatch(holder)
493
assert _async_dispatch_stack.count > 0 else "dispatch holder stack underflow"
494
_async_dispatch_stack.remove_at(_async_dispatch_stack.count - 1)
495
496
// A state-machine entry holder (one per MoveNext body) is
497
// never propagated: the holder beneath it on the stack
498
// belongs to an ENCLOSING state machine — a different
499
// method body — and a branch there could not reach this
500
// body's labels. Try-to-try propagation stays within one
501
// body by construction.
502
if !holder.is_state_machine_entry /\ _async_dispatch_stack.count > 0 then
503
let parent = _async_dispatch_stack[_async_dispatch_stack.count - 1]
504
for entry in holder.entries do
505
parent.register(entry.state, holder.top_label)
506
od
507
fi
508
si
509
510
// Fill the holder's placeholder block with one
511
// `ldloc V_state; ldc.i4 N; beq cold_resume_N` entry per
512
// registered await. The placeholder was inserted into the
513
// surrounding emission stream when the holder was pushed —
514
// these instructions now sit at the top of the protected
515
// region's body, before any user code.
516
_populate_async_dispatch(holder: ASYNC_DISPATCH_HOLDER) is
517
for entry in holder.entries do
518
holder.block.add(
519
Values.Load.TEMP(holder.state_local_il, _innate_symbol_lookup.get_int_type()))
520
holder.block.add(
521
Values.Literal.NUMBER(entry.state, _innate_symbol_lookup.get_int_type()))
522
holder.block.add(
523
Values.BRANCH_TO(ILOpCode.BEQ, entry.cold_resume_label))
524
od
525
si
526
527
// Emit all pending state-machine classes accumulated during
528
// the walk of an enclosing class or namespace. Called at the
529
// tail of visit(`class) / visit(`namespace) so the classes
530
// land at namespace scope, parallel to the user's class.
531
emit_pending_state_machines() is
532
if _pending_state_machines.count > 0 then
533
let pending = _pending_state_machines
534
_pending_state_machines = Collections.LIST[PENDING_STATE_MACHINE]()
535
536
for psm in pending do
537
_emit_state_machine_class(psm.state_machine, psm.move_next_block)
538
od
539
fi
540
541
if _pending_async_state_machines.count > 0 then
542
let pending = _pending_async_state_machines
543
_pending_async_state_machines = Collections.LIST[PENDING_ASYNC_STATE_MACHINE]()
544
545
for pasm in pending do
546
_emit_async_state_machine_class(
547
pasm.async_state_machine,
548
pasm.move_next_block,
549
pasm.success_label,
550
pasm.end_label
551
)
552
od
553
fi
554
si
555
556
// Emits the synthesised IAsyncStateMachine class — parallels
557
// `_emit_state_machine_class` for generators. Class members:
558
// `$state`, `$builder`, `$result` (value-async), `$outer_self`
559
// (instance), `$arg_*`, `$local_*`, `$awaiter_*` fields; ctor;
560
// MoveNext (with entry-dispatch and try/catch routing through
561
// the builder); trivial SetStateMachine.
562
_emit_async_state_machine_class(
563
async_state_machine: Semantic.Symbols.ASYNC_STATE_MACHINE,
564
move_next_block: Values.BLOCK,
565
success_label: IR.LABEL,
566
end_label: IR.LABEL
567
) is
568
let frame = async_state_machine.frame
569
570
assert frame? else "async state machine has no frame at IL emission"
571
572
// Same settled-type refresh the generator emitter performs.
573
frame.refresh_local_field_types()
574
575
async_state_machine.install_body_emission_overrides()
576
577
try
578
_gen_async_state_machine_ctor_srm(frame)
579
580
_emit_async_frame_member_body_srm(
581
frame,
582
IR.Emitter.FrameMember.MOVE_NEXT,
583
() is
584
_gen_async_move_next_srm(
585
frame, move_next_block, success_label, end_label)
586
si)
587
588
_emit_async_frame_member_body_srm(
589
frame,
590
IR.Emitter.FrameMember.SET_STATE_MACHINE,
591
() is si)
592
finally
593
async_state_machine.uninstall_body_emission_overrides()
594
yrt
595
si
596
597
// A synthesised async frame member, encoded into a body of its
598
// own and deposited against the key the structure walk reads.
599
// `emit` writes the body; SetStateMachine's writes nothing,
600
// because the whole of it is the `ret` added here.
601
_emit_async_frame_member_body_srm(
602
frame: Semantic.Symbols.ASYNC_STATE_MACHINE_FRAME,
603
member: IR.Emitter.FrameMember,
604
emit: () -> void
605
) is
606
let assembly_emitter = _context.srm_assembly_emitter
607
let enclosing = _context.current_srm_body_emitter
608
let body = IR.Emitter.SRM_METHOD_BODY_EMITTER()
609
610
_context.current_srm_body_emitter = body
611
612
try
613
emit()
614
615
body.ret()
616
617
assembly_emitter.handles.set_frame_member_body(
618
frame, member, body.flush(assembly_emitter))
619
finally
620
_context.current_srm_body_emitter = enclosing
621
yrt
622
si
623
624
// MoveNext, wrapped in the try/catch that routes an escaping
625
// exception through the builder. The success path falls out of
626
// the region to `success_label`, completes the task, and both
627
// paths meet at `end_label`.
628
_gen_async_move_next_srm(
629
frame: Semantic.Symbols.ASYNC_STATE_MACHINE_FRAME,
630
move_next_block: Values.BLOCK,
631
success_label: IR.LABEL,
632
end_label: IR.LABEL
633
) is
634
let body = _context.current_srm_body_emitter!
635
636
let builder_field = frame.builder_field!
637
let builder_type = builder_field.type!
638
let exception_type = _innate_symbol_lookup.get_exception_type()
639
640
let state_field = _context.resolve_field_target(frame.state_field)
641
let builder_reference = _context.resolve_field_target(builder_field)
642
643
let try_start = body.mark_region_boundary()
644
645
move_next_block.gen(_context)
646
647
let try_end = body.mark_region_boundary()
648
let handler_start = try_end
649
650
body.declare_local(_EXCEPTION_LOCAL, exception_type)
651
body.stloc(_EXCEPTION_LOCAL)
652
body.ldarg(0)
653
body.ldc_i4(-1)
654
body.stfld(state_field)
655
body.ldarg(0)
656
IR.Values.ASYNC_METHOD_LAUNCH.load_builder(body, builder_type, builder_reference)
657
body.ldloc(_EXCEPTION_LOCAL)
658
body.call(
659
_context.resolve_async_builder_set_exception(builder_type))
660
body.branch(System.Reflection.Metadata.ILOpCode.LEAVE, end_label)
661
662
let handler_end = body.mark_region_boundary()
663
664
body.add_catch_region(
665
try_start,
666
try_end,
667
handler_start,
668
handler_end,
669
_context.resolve_type_token(exception_type))
670
671
body.mark_label(success_label)
672
body.ldarg(0)
673
body.ldc_i4(-1)
674
body.stfld(state_field)
675
body.ldarg(0)
676
IR.Values.ASYNC_METHOD_LAUNCH.load_builder(body, builder_type, builder_reference)
677
678
let result_field = frame.result_field
679
680
if result_field? then
681
body.ldarg(0)
682
body.ldfld(_context.resolve_field_target(result_field))
683
fi
684
685
body.call(_context.resolve_async_builder_set_result(builder_type, result_field?))
686
687
body.mark_label(end_label)
688
si
689
690
_EXCEPTION_LOCAL: string static => ".exception"
691
692
// The frame's constructor: chain to Object, then copy each
693
// constructor argument into the field the lowering declared for
694
// it. The builder is not among them — the outer method creates
695
// it and stores it after the newobj.
696
_gen_async_state_machine_ctor_srm(frame: Semantic.Symbols.ASYNC_STATE_MACHINE_FRAME) is
697
let assembly_emitter = _context.srm_assembly_emitter
698
let enclosing = _context.current_srm_body_emitter
699
let body = IR.Emitter.SRM_METHOD_BODY_EMITTER()
700
701
_context.current_srm_body_emitter = body
702
703
try
704
body.ldarg(0)
705
body.call(_context.resolve_object_constructor())
706
707
let slot mut = 1
708
709
if let outer_self_field = frame.outer_self_field then
710
body.ldarg(0)
711
body.ldarg(slot)
712
body.stfld(_context.resolve_field_target(outer_self_field))
713
714
slot = slot + 1
715
fi
716
717
for arg_field in frame.argument_fields do
718
body.ldarg(0)
719
body.ldarg(slot)
720
body.stfld(_context.resolve_field_target(arg_field))
721
722
slot = slot + 1
723
od
724
725
body.ret()
726
727
assembly_emitter.handles.set_body_offset(
728
frame.constructor, body.flush(assembly_emitter))
729
assembly_emitter.handles.set_il_outputs(
730
frame.constructor, body.il_outputs)
731
finally
732
_context.current_srm_body_emitter = enclosing
733
yrt
734
si
735
736
si
737
si