| 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 |  | 
| 23 | namespace dart { | 
| 24 |  | 
| 25 | class InlineExitCollector; | 
| 26 |  | 
| 27 | namespace kernel { | 
| 28 |  | 
| 29 | class StreamingFlowGraphBuilder; | 
| 30 | struct InferredTypeMetadata; | 
| 31 | class BreakableBlock; | 
| 32 | class CatchBlock; | 
| 33 | class FlowGraphBuilder; | 
| 34 | class SwitchBlock; | 
| 35 | class TryCatchBlock; | 
| 36 | class TryFinallyBlock; | 
| 37 |  | 
| 38 | struct 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 |  | 
| 48 | enum class TypeChecksToBuild { | 
| 49 |   kCheckAllTypeParameterBounds, | 
| 50 |   kCheckNonCovariantTypeParameterBounds, | 
| 51 |   kCheckCovariantTypeParameterBounds, | 
| 52 | }; | 
| 53 |  | 
| 54 | class 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* (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 , | 
| 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. | 
| 438 | class 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 |  | 
| 475 | class 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 |  | 
| 559 | class 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 |  | 
| 583 | class 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 |  | 
| 622 | class 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 |  | 
| 679 | class 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 |  |