1// Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#ifndef RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_
6#define RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_
7
8#if defined(DART_PRECOMPILED_RUNTIME)
9#error "AOT runtime should not use compiler sources (including header files)"
10#endif // defined(DART_PRECOMPILED_RUNTIME)
11
12#include "vm/growable_array.h"
13#include "vm/hash_map.h"
14
15#include "vm/compiler/backend/flow_graph.h"
16#include "vm/compiler/backend/il.h"
17#include "vm/compiler/ffi/marshaller.h"
18#include "vm/compiler/ffi/native_type.h"
19#include "vm/compiler/frontend/base_flow_graph_builder.h"
20#include "vm/compiler/frontend/kernel_translation_helper.h"
21#include "vm/compiler/frontend/scope_builder.h"
22
23namespace dart {
24
25class InlineExitCollector;
26
27namespace kernel {
28
29class StreamingFlowGraphBuilder;
30struct InferredTypeMetadata;
31class BreakableBlock;
32class CatchBlock;
33class FlowGraphBuilder;
34class SwitchBlock;
35class TryCatchBlock;
36class TryFinallyBlock;
37
38struct YieldContinuation {
39 Instruction* entry;
40 intptr_t try_index;
41
42 YieldContinuation(Instruction* entry, intptr_t try_index)
43 : entry(entry), try_index(try_index) {}
44
45 YieldContinuation() : entry(NULL), try_index(kInvalidTryIndex) {}
46};
47
48enum class TypeChecksToBuild {
49 kCheckAllTypeParameterBounds,
50 kCheckNonCovariantTypeParameterBounds,
51 kCheckCovariantTypeParameterBounds,
52};
53
54class FlowGraphBuilder : public BaseFlowGraphBuilder {
55 public:
56 FlowGraphBuilder(ParsedFunction* parsed_function,
57 ZoneGrowableArray<const ICData*>* ic_data_array,
58 ZoneGrowableArray<intptr_t>* context_level_array,
59 InlineExitCollector* exit_collector,
60 bool optimizing,
61 intptr_t osr_id,
62 intptr_t first_block_id = 1,
63 bool inlining_unchecked_entry = false);
64 virtual ~FlowGraphBuilder();
65
66 FlowGraph* BuildGraph();
67
68 private:
69 BlockEntryInstr* BuildPrologue(BlockEntryInstr* normal_entry,
70 PrologueInfo* prologue_info);
71
72 // Return names of optional named parameters of [function].
73 ArrayPtr GetOptionalParameterNames(const Function& function);
74
75 // Generate fragment which pushes all explicit parameters of [function].
76 Fragment PushExplicitParameters(
77 const Function& function,
78 const Function& target = Function::null_function());
79
80 FlowGraph* BuildGraphOfMethodExtractor(const Function& method);
81 FlowGraph* BuildGraphOfNoSuchMethodDispatcher(const Function& function);
82 FlowGraph* BuildGraphOfInvokeFieldDispatcher(const Function& function);
83 FlowGraph* BuildGraphOfFfiTrampoline(const Function& function);
84 FlowGraph* BuildGraphOfFfiCallback(const Function& function);
85 FlowGraph* BuildGraphOfFfiNative(const Function& function);
86
87 Fragment NativeFunctionBody(const Function& function,
88 LocalVariable* first_parameter);
89
90 // Every recognized method has a body expressed in IL.
91 bool IsRecognizedMethodForFlowGraph(const Function& function);
92 FlowGraph* BuildGraphOfRecognizedMethod(const Function& function);
93
94 Fragment BuildTypedDataViewFactoryConstructor(const Function& function,
95 classid_t cid);
96
97 Fragment EnterScope(intptr_t kernel_offset,
98 const LocalScope** scope = nullptr);
99 Fragment ExitScope(intptr_t kernel_offset);
100
101 Fragment AdjustContextTo(int depth);
102
103 Fragment PushContext(const LocalScope* scope);
104 Fragment PopContext();
105
106 Fragment LoadInstantiatorTypeArguments();
107 Fragment LoadFunctionTypeArguments();
108 Fragment TranslateInstantiatedTypeArguments(
109 const TypeArguments& type_arguments);
110
111 Fragment CatchBlockEntry(const Array& handler_types,
112 intptr_t handler_index,
113 bool needs_stacktrace,
114 bool is_synthesized);
115 Fragment TryCatch(int try_handler_index);
116 Fragment CheckStackOverflowInPrologue(TokenPosition position);
117 Fragment CloneContext(const ZoneGrowableArray<const Slot*>& context_slots);
118
119 Fragment InstanceCall(
120 TokenPosition position,
121 const String& name,
122 Token::Kind kind,
123 intptr_t type_args_len,
124 intptr_t argument_count,
125 const Array& argument_names,
126 intptr_t checked_argument_count,
127 const Function& interface_target = Function::null_function(),
128 const Function& tearoff_interface_target = Function::null_function(),
129 const InferredTypeMetadata* result_type = nullptr,
130 bool use_unchecked_entry = false,
131 const CallSiteAttributesMetadata* call_site_attrs = nullptr,
132 bool receiver_is_not_smi = false);
133
134 Fragment FfiCall(const compiler::ffi::CallMarshaller& marshaller);
135
136 Fragment ThrowException(TokenPosition position);
137 Fragment RethrowException(TokenPosition position, int catch_try_index);
138 Fragment LoadLocal(LocalVariable* variable);
139 Fragment StoreLateField(const Field& field,
140 LocalVariable* instance,
141 LocalVariable* setter_value);
142 Fragment NativeCall(const String* name, const Function* function);
143 Fragment Return(
144 TokenPosition position,
145 bool omit_result_type_check = false,
146 intptr_t yield_index = PcDescriptorsLayout::kInvalidYieldIndex);
147 void SetResultTypeForStaticCall(StaticCallInstr* call,
148 const Function& target,
149 intptr_t argument_count,
150 const InferredTypeMetadata* result_type);
151 Fragment StaticCall(TokenPosition position,
152 const Function& target,
153 intptr_t argument_count,
154 ICData::RebindRule rebind_rule);
155 Fragment StaticCall(TokenPosition position,
156 const Function& target,
157 intptr_t argument_count,
158 const Array& argument_names,
159 ICData::RebindRule rebind_rule,
160 const InferredTypeMetadata* result_type = NULL,
161 intptr_t type_args_len = 0,
162 bool use_unchecked_entry = false);
163 Fragment StringInterpolateSingle(TokenPosition position);
164 Fragment ThrowTypeError();
165 Fragment ThrowNoSuchMethodError(const Function& target);
166 Fragment ThrowLateInitializationError(TokenPosition position,
167 const String& name);
168 Fragment BuildImplicitClosureCreation(const Function& target);
169
170 Fragment EvaluateAssertion();
171 Fragment CheckVariableTypeInCheckedMode(const AbstractType& dst_type,
172 const String& name_symbol);
173 Fragment CheckBoolean(TokenPosition position);
174 Fragment CheckAssignable(
175 const AbstractType& dst_type,
176 const String& dst_name,
177 AssertAssignableInstr::Kind kind = AssertAssignableInstr::kUnknown);
178
179 Fragment AssertAssignableLoadTypeArguments(
180 TokenPosition position,
181 const AbstractType& dst_type,
182 const String& dst_name,
183 AssertAssignableInstr::Kind kind = AssertAssignableInstr::kUnknown);
184 Fragment AssertSubtype(TokenPosition position,
185 const AbstractType& sub_type,
186 const AbstractType& super_type,
187 const String& dst_name);
188
189 bool NeedsDebugStepCheck(const Function& function, TokenPosition position);
190 bool NeedsDebugStepCheck(Value* value, TokenPosition position);
191
192 // Truncates (instead of deoptimizing) if the origin does not fit into the
193 // target representation.
194 Fragment UnboxTruncate(Representation to);
195
196 // Creates an ffi.Pointer holding a given address (TOS).
197 Fragment FfiPointerFromAddress(const Type& result_type);
198
199 // Pushes an (unboxed) bogus value returned when a native -> Dart callback
200 // throws an exception.
201 Fragment FfiExceptionalReturnValue(const AbstractType& result_type,
202 const Representation target);
203
204 // Pops a Dart object and push the unboxed native version, according to the
205 // semantics of FFI argument translation.
206 Fragment FfiConvertArgumentToNative(
207 const compiler::ffi::BaseMarshaller& marshaller,
208 intptr_t arg_index,
209 LocalVariable* api_local_scope);
210
211 // Reverse of 'FfiConvertArgumentToNative'.
212 Fragment FfiConvertArgumentToDart(
213 const compiler::ffi::BaseMarshaller& marshaller,
214 intptr_t arg_index);
215
216 // Generates a call to `Thread::EnterApiScope`.
217 Fragment EnterHandleScope();
218
219 // Generates a call to `Thread::api_top_scope`.
220 Fragment GetTopHandleScope();
221
222 // Generates a call to `Thread::ExitApiScope`.
223 Fragment ExitHandleScope();
224
225 // Leaves a `LocalHandle` on the stack.
226 Fragment AllocateHandle(LocalVariable* api_local_scope);
227
228 // Populates the base + offset with a tagged value.
229 Fragment RawStoreField(int32_t offset);
230
231 // Wraps an `Object` from the stack and leaves a `LocalHandle` on the stack.
232 Fragment WrapHandle(LocalVariable* api_local_scope);
233
234 // Unwraps a `LocalHandle` from the stack and leaves the object on the stack.
235 Fragment UnwrapHandle();
236
237 // Wrap the current exception and stacktrace in an unhandled exception.
238 Fragment UnhandledException();
239
240 // Return from a native -> Dart callback. Can only be used in conjunction with
241 // NativeEntry and NativeParameter are used.
242 Fragment NativeReturn(const compiler::ffi::CallbackMarshaller& marshaller);
243
244 // Bit-wise cast between representations.
245 // Pops the input and pushes the converted result.
246 // Currently only works with equal sizes and floating point <-> integer.
247 Fragment BitCast(Representation from, Representation to);
248
249 LocalVariable* LookupVariable(intptr_t kernel_offset);
250
251 // Build argument type checks for the current function.
252 // ParsedFunction should have the following information:
253 // - is_forwarding_stub()
254 // - forwarding_stub_super_target()
255 // Scope should be populated with parameter variables including
256 // - needs_type_check()
257 // - is_explicit_covariant_parameter()
258 void BuildArgumentTypeChecks(TypeChecksToBuild mode,
259 Fragment* explicit_checks,
260 Fragment* implicit_checks,
261 Fragment* implicit_redefinitions);
262
263 // Returns true if null assertion is needed for
264 // a parameter of given type.
265 bool NeedsNullAssertion(const AbstractType& type);
266
267 // Builds null assertion for the given parameter.
268 Fragment NullAssertion(LocalVariable* variable);
269
270 // Builds null assertions for all parameters (if needed).
271 Fragment BuildNullAssertions();
272
273 // Builds flow graph for noSuchMethod forwarder.
274 //
275 // If throw_no_such_method_error is set to true, an
276 // instance of NoSuchMethodError is thrown. Otherwise, the instance
277 // noSuchMethod is called.
278 //
279 // ParsedFunction should have the following information:
280 // - default_parameter_values()
281 // - is_forwarding_stub()
282 // - forwarding_stub_super_target()
283 //
284 // Scope should be populated with parameter variables including
285 // - needs_type_check()
286 // - is_explicit_covariant_parameter()
287 //
288 FlowGraph* BuildGraphOfNoSuchMethodForwarder(
289 const Function& function,
290 bool is_implicit_closure_function,
291 bool throw_no_such_method_error);
292
293 // If no type arguments are passed to a generic function, we need to fill the
294 // type arguments in with the default types stored on the TypeParameter nodes
295 // in Kernel.
296 //
297 // ParsedFunction should have the following information:
298 // - DefaultFunctionTypeArguments()
299 // - function_type_arguments()
300 Fragment BuildDefaultTypeHandling(const Function& function);
301
302 FunctionEntryInstr* BuildSharedUncheckedEntryPoint(
303 Fragment prologue_from_normal_entry,
304 Fragment skippable_checks,
305 Fragment redefinitions_if_skipped,
306 Fragment body);
307 FunctionEntryInstr* BuildSeparateUncheckedEntryPoint(
308 BlockEntryInstr* normal_entry,
309 Fragment normal_prologue,
310 Fragment extra_prologue,
311 Fragment shared_prologue,
312 Fragment body);
313
314 // Builds flow graph for implicit closure function (tear-off).
315 //
316 // ParsedFunction should have the following information:
317 // - DefaultFunctionTypeArguments()
318 // - function_type_arguments()
319 // - default_parameter_values()
320 // - is_forwarding_stub()
321 // - forwarding_stub_super_target()
322 //
323 // Scope should be populated with parameter variables including
324 // - needs_type_check()
325 // - is_explicit_covariant_parameter()
326 //
327 FlowGraph* BuildGraphOfImplicitClosureFunction(const Function& function);
328
329 // Builds flow graph of implicit field getter, setter, or a
330 // dynamic invocation forwarder to a field setter.
331 //
332 // If field is const, its value should be evaluated and stored in
333 // - StaticValue()
334 //
335 // Scope should be populated with parameter variables including
336 // - needs_type_check()
337 //
338 FlowGraph* BuildGraphOfFieldAccessor(const Function& function);
339
340 // Builds flow graph of dynamic invocation forwarder.
341 //
342 // ParsedFunction should have the following information:
343 // - DefaultFunctionTypeArguments()
344 // - function_type_arguments()
345 // - default_parameter_values()
346 // - is_forwarding_stub()
347 // - forwarding_stub_super_target()
348 //
349 // Scope should be populated with parameter variables including
350 // - needs_type_check()
351 // - is_explicit_covariant_parameter()
352 //
353 FlowGraph* BuildGraphOfDynamicInvocationForwarder(const Function& function);
354
355 TranslationHelper translation_helper_;
356 Thread* thread_;
357 Zone* zone_;
358
359 ParsedFunction* parsed_function_;
360 const bool optimizing_;
361 ZoneGrowableArray<const ICData*>& ic_data_array_;
362
363 intptr_t next_function_id_;
364 intptr_t AllocateFunctionId() { return next_function_id_++; }
365
366 intptr_t loop_depth_;
367 intptr_t try_depth_;
368 intptr_t catch_depth_;
369 intptr_t for_in_depth_;
370 intptr_t block_expression_depth_;
371
372 GraphEntryInstr* graph_entry_;
373
374 ScopeBuildingResult* scopes_;
375
376 GrowableArray<YieldContinuation> yield_continuations_;
377
378 LocalVariable* CurrentException() {
379 return scopes_->exception_variables[catch_depth_ - 1];
380 }
381 LocalVariable* CurrentStackTrace() {
382 return scopes_->stack_trace_variables[catch_depth_ - 1];
383 }
384 LocalVariable* CurrentRawException() {
385 return scopes_->raw_exception_variables[catch_depth_ - 1];
386 }
387 LocalVariable* CurrentRawStackTrace() {
388 return scopes_->raw_stack_trace_variables[catch_depth_ - 1];
389 }
390 LocalVariable* CurrentCatchContext() {
391 return scopes_->catch_context_variables[try_depth_];
392 }
393
394 TryCatchBlock* CurrentTryCatchBlock() const { return try_catch_block_; }
395
396 void SetCurrentTryCatchBlock(TryCatchBlock* try_catch_block);
397
398 // A chained list of breakable blocks. Chaining and lookup is done by the
399 // [BreakableBlock] class.
400 BreakableBlock* breakable_block_;
401
402 // A chained list of switch blocks. Chaining and lookup is done by the
403 // [SwitchBlock] class.
404 SwitchBlock* switch_block_;
405
406 // A chained list of try-catch blocks. Chaining and lookup is done by the
407 // [TryCatchBlock] class.
408 TryCatchBlock* try_catch_block_;
409
410 // A chained list of try-finally blocks. Chaining and lookup is done by the
411 // [TryFinallyBlock] class.
412 TryFinallyBlock* try_finally_block_;
413
414 // A chained list of catch blocks. Chaining and lookup is done by the
415 // [CatchBlock] class.
416 CatchBlock* catch_block_;
417
418 ActiveClass active_class_;
419
420 // Cached _AssertionError._throwNewNullAssertion.
421 Function* throw_new_null_assertion_ = nullptr;
422
423 friend class BreakableBlock;
424 friend class CatchBlock;
425 friend class ProgramState;
426 friend class StreamingFlowGraphBuilder;
427 friend class SwitchBlock;
428 friend class TryCatchBlock;
429 friend class TryFinallyBlock;
430
431 DISALLOW_COPY_AND_ASSIGN(FlowGraphBuilder);
432};
433
434// Convenience class to save/restore program state.
435// This snapshot denotes a partial state of the flow
436// grap builder that is needed when recursing into
437// the statements and expressions of a finalizer block.
438class ProgramState {
439 public:
440 ProgramState(BreakableBlock* breakable_block,
441 SwitchBlock* switch_block,
442 intptr_t loop_depth,
443 intptr_t for_in_depth,
444 intptr_t try_depth,
445 intptr_t catch_depth,
446 intptr_t block_expression_depth)
447 : breakable_block_(breakable_block),
448 switch_block_(switch_block),
449 loop_depth_(loop_depth),
450 for_in_depth_(for_in_depth),
451 try_depth_(try_depth),
452 catch_depth_(catch_depth),
453 block_expression_depth_(block_expression_depth) {}
454
455 void assignTo(FlowGraphBuilder* builder) const {
456 builder->breakable_block_ = breakable_block_;
457 builder->switch_block_ = switch_block_;
458 builder->loop_depth_ = loop_depth_;
459 builder->for_in_depth_ = for_in_depth_;
460 builder->try_depth_ = try_depth_;
461 builder->catch_depth_ = catch_depth_;
462 builder->block_expression_depth_ = block_expression_depth_;
463 }
464
465 private:
466 BreakableBlock* const breakable_block_;
467 SwitchBlock* const switch_block_;
468 const intptr_t loop_depth_;
469 const intptr_t for_in_depth_;
470 const intptr_t try_depth_;
471 const intptr_t catch_depth_;
472 const intptr_t block_expression_depth_;
473};
474
475class SwitchBlock {
476 public:
477 SwitchBlock(FlowGraphBuilder* builder, intptr_t case_count)
478 : builder_(builder),
479 outer_(builder->switch_block_),
480 outer_finally_(builder->try_finally_block_),
481 case_count_(case_count),
482 context_depth_(builder->context_depth_),
483 try_index_(builder->CurrentTryIndex()) {
484 builder_->switch_block_ = this;
485 if (outer_ != NULL) {
486 depth_ = outer_->depth_ + outer_->case_count_;
487 } else {
488 depth_ = 0;
489 }
490 }
491 ~SwitchBlock() { builder_->switch_block_ = outer_; }
492
493 bool HadJumper(intptr_t case_num) {
494 return destinations_.Lookup(case_num) != NULL;
495 }
496
497 // Get destination via absolute target number (i.e. the correct destination
498 // is not necessarily in this block).
499 JoinEntryInstr* Destination(intptr_t target_index,
500 TryFinallyBlock** outer_finally = NULL,
501 intptr_t* context_depth = NULL) {
502 // Verify consistency of program state.
503 ASSERT(builder_->switch_block_ == this);
504 // Find corresponding destination.
505 SwitchBlock* block = this;
506 while (block->depth_ > target_index) {
507 block = block->outer_;
508 ASSERT(block != nullptr);
509 }
510
511 // Set the outer finally block.
512 if (outer_finally != NULL) {
513 *outer_finally = block->outer_finally_;
514 *context_depth = block->context_depth_;
515 }
516
517 // Ensure there's [JoinEntryInstr] for that [SwitchCase].
518 return block->EnsureDestination(target_index - block->depth_);
519 }
520
521 // Get destination via relative target number (i.e. relative to this block,
522 // 0 is first case in this block etc).
523 JoinEntryInstr* DestinationDirect(intptr_t case_num,
524 TryFinallyBlock** outer_finally = NULL,
525 intptr_t* context_depth = NULL) {
526 // Set the outer finally block.
527 if (outer_finally != NULL) {
528 *outer_finally = outer_finally_;
529 *context_depth = context_depth_;
530 }
531
532 // Ensure there's [JoinEntryInstr] for that [SwitchCase].
533 return EnsureDestination(case_num);
534 }
535
536 private:
537 JoinEntryInstr* EnsureDestination(intptr_t case_num) {
538 JoinEntryInstr* cached_inst = destinations_.Lookup(case_num);
539 if (cached_inst == NULL) {
540 JoinEntryInstr* inst = builder_->BuildJoinEntry(try_index_);
541 destinations_.Insert(case_num, inst);
542 return inst;
543 }
544 return cached_inst;
545 }
546
547 FlowGraphBuilder* builder_;
548 SwitchBlock* outer_;
549
550 IntMap<JoinEntryInstr*> destinations_;
551
552 TryFinallyBlock* outer_finally_;
553 intptr_t case_count_;
554 intptr_t depth_;
555 intptr_t context_depth_;
556 intptr_t try_index_;
557};
558
559class TryCatchBlock {
560 public:
561 explicit TryCatchBlock(FlowGraphBuilder* builder,
562 intptr_t try_handler_index = -1)
563 : builder_(builder),
564 outer_(builder->CurrentTryCatchBlock()),
565 try_index_(try_handler_index == -1 ? builder->AllocateTryIndex()
566 : try_handler_index) {
567 builder->SetCurrentTryCatchBlock(this);
568 }
569
570 ~TryCatchBlock() { builder_->SetCurrentTryCatchBlock(outer_); }
571
572 intptr_t try_index() { return try_index_; }
573 TryCatchBlock* outer() const { return outer_; }
574
575 private:
576 FlowGraphBuilder* const builder_;
577 TryCatchBlock* const outer_;
578 intptr_t const try_index_;
579
580 DISALLOW_COPY_AND_ASSIGN(TryCatchBlock);
581};
582
583class TryFinallyBlock {
584 public:
585 TryFinallyBlock(FlowGraphBuilder* builder, intptr_t finalizer_kernel_offset)
586 : builder_(builder),
587 outer_(builder->try_finally_block_),
588 finalizer_kernel_offset_(finalizer_kernel_offset),
589 context_depth_(builder->context_depth_),
590 try_index_(builder_->CurrentTryIndex()),
591 // Finalizers are executed outside of the try block hence
592 // try depth of finalizers are one less than current try
593 // depth. For others, program state is snapshot of current.
594 state_(builder_->breakable_block_,
595 builder_->switch_block_,
596 builder_->loop_depth_,
597 builder_->for_in_depth_,
598 builder_->try_depth_ - 1,
599 builder_->catch_depth_,
600 builder_->block_expression_depth_) {
601 builder_->try_finally_block_ = this;
602 }
603 ~TryFinallyBlock() { builder_->try_finally_block_ = outer_; }
604
605 TryFinallyBlock* outer() const { return outer_; }
606 intptr_t finalizer_kernel_offset() const { return finalizer_kernel_offset_; }
607 intptr_t context_depth() const { return context_depth_; }
608 intptr_t try_index() const { return try_index_; }
609 const ProgramState& state() const { return state_; }
610
611 private:
612 FlowGraphBuilder* const builder_;
613 TryFinallyBlock* const outer_;
614 const intptr_t finalizer_kernel_offset_;
615 const intptr_t context_depth_;
616 const intptr_t try_index_;
617 const ProgramState state_;
618
619 DISALLOW_COPY_AND_ASSIGN(TryFinallyBlock);
620};
621
622class BreakableBlock {
623 public:
624 explicit BreakableBlock(FlowGraphBuilder* builder)
625 : builder_(builder),
626 outer_(builder->breakable_block_),
627 destination_(NULL),
628 outer_finally_(builder->try_finally_block_),
629 context_depth_(builder->context_depth_),
630 try_index_(builder->CurrentTryIndex()) {
631 if (builder_->breakable_block_ == NULL) {
632 index_ = 0;
633 } else {
634 index_ = builder_->breakable_block_->index_ + 1;
635 }
636 builder_->breakable_block_ = this;
637 }
638 ~BreakableBlock() { builder_->breakable_block_ = outer_; }
639
640 bool HadJumper() { return destination_ != NULL; }
641
642 JoinEntryInstr* destination() { return destination_; }
643
644 JoinEntryInstr* BreakDestination(intptr_t label_index,
645 TryFinallyBlock** outer_finally,
646 intptr_t* context_depth) {
647 // Verify consistency of program state.
648 ASSERT(builder_->breakable_block_ == this);
649 // Find corresponding destination.
650 BreakableBlock* block = this;
651 while (block->index_ != label_index) {
652 block = block->outer_;
653 ASSERT(block != nullptr);
654 }
655 *outer_finally = block->outer_finally_;
656 *context_depth = block->context_depth_;
657 return block->EnsureDestination();
658 }
659
660 private:
661 JoinEntryInstr* EnsureDestination() {
662 if (destination_ == NULL) {
663 destination_ = builder_->BuildJoinEntry(try_index_);
664 }
665 return destination_;
666 }
667
668 FlowGraphBuilder* builder_;
669 intptr_t index_;
670 BreakableBlock* outer_;
671 JoinEntryInstr* destination_;
672 TryFinallyBlock* outer_finally_;
673 intptr_t context_depth_;
674 intptr_t try_index_;
675
676 DISALLOW_COPY_AND_ASSIGN(BreakableBlock);
677};
678
679class CatchBlock {
680 public:
681 CatchBlock(FlowGraphBuilder* builder,
682 LocalVariable* exception_var,
683 LocalVariable* stack_trace_var,
684 intptr_t catch_try_index)
685 : builder_(builder),
686 outer_(builder->catch_block_),
687 exception_var_(exception_var),
688 stack_trace_var_(stack_trace_var),
689 catch_try_index_(catch_try_index) {
690 builder_->catch_block_ = this;
691 }
692 ~CatchBlock() { builder_->catch_block_ = outer_; }
693
694 LocalVariable* exception_var() { return exception_var_; }
695 LocalVariable* stack_trace_var() { return stack_trace_var_; }
696 intptr_t catch_try_index() { return catch_try_index_; }
697
698 private:
699 FlowGraphBuilder* builder_;
700 CatchBlock* outer_;
701 LocalVariable* exception_var_;
702 LocalVariable* stack_trace_var_;
703 intptr_t catch_try_index_;
704
705 DISALLOW_COPY_AND_ASSIGN(CatchBlock);
706};
707
708} // namespace kernel
709} // namespace dart
710
711#endif // RUNTIME_VM_COMPILER_FRONTEND_KERNEL_TO_IL_H_
712