text: // Copyright (c) 2016, the Dart project authors.  Please see the AUTHORS file
  // for details. All rights reserved. Use of this source code is governed by a
  // BSD-style license that can be found in the LICENSE file.
  
  #include "vm/compiler/frontend/kernel_binary_flowgraph.h"
  
  #include "vm/compiler/ffi/callback.h"
  #include "vm/compiler/frontend/flow_graph_builder.h"  // For dart::FlowGraphBuilder::SimpleInstanceOfType.
  #include "vm/compiler/frontend/prologue_builder.h"
  #include "vm/compiler/jit/compiler.h"
  #include "vm/object_store.h"
  #include "vm/resolver.h"
  #include "vm/stack_frame.h"
  
  namespace dart {
  namespace kernel {
  
  #define Z (zone_)
  #define H (translation_helper_)
  #define T (type_translator_)
  #define I Isolate::Current()
  #define B (flow_graph_builder_)
  
  Class& StreamingFlowGraphBuilder::GetSuperOrDie() {
    Class& klass = Class::Handle(Z, parsed_function()->function().Owner());
    ASSERT(!klass.IsNull());
    klass = klass.SuperClass();
    ASSERT(!klass.IsNull());
    return klass;
  }
  
  FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFieldInitializer() {
    FieldHelper field_helper(this);
    field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
  
    // Constants are directly accessed at use sites of Dart code. In C++ - if
    // we need to access static constants - we do so directly using the kernel
    // evaluation instead of invoking the initializer function in Dart code.
    //
    // If the field is marked as @pragma('vm:entry-point') then the embedder might
    // invoke the getter, so we'll generate the initializer function.
    ASSERT(!field_helper.IsConst() ||
           Field::Handle(Z, parsed_function()->function().accessor_field())
                   .VerifyEntryPoint(EntryPointPragma::kGetterOnly) ==
               Error::null());
  
    Tag initializer_tag = ReadTag();  // read first part of initializer.
    if (initializer_tag != kSomething) {
      UNREACHABLE();
    }
  
    B->graph_entry_ = new (Z) GraphEntryInstr(*parsed_function(), B->osr_id_);
  
    auto normal_entry = B->BuildFunctionEntry(B->graph_entry_);
    B->graph_entry_->set_normal_entry(normal_entry);
  
    Fragment body(normal_entry);
    body += B->CheckStackOverflowInPrologue(field_helper.position_);
    body += SetupCapturedParameters(parsed_function()->function());
    body += BuildExpression();  // read initializer.
    body += Return(TokenPosition::kNoSource);
  
    PrologueInfo prologue_info(-1, -1);
    if (B->IsCompiledForOsr()) {
      B->graph_entry_->RelinkToOsrEntry(Z, B->last_used_block_id_ + 1);
    }
    return new (Z) FlowGraph(*parsed_function(), B->graph_entry_,
                             B->last_used_block_id_, prologue_info);
  }
  
  void StreamingFlowGraphBuilder::EvaluateConstFieldValue(const Field& field) {
    ASSERT(field.is_const() && field.IsUninitialized());
  
    FieldHelper field_helper(this);
    field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
    Tag initializer_tag = ReadTag();  // read first part of initializer.
  
    ASSERT(initializer_tag == kSomething);
  
    Instance& value =
        Instance::Handle(Z, constant_reader_.ReadConstantExpression());
    field.SetStaticValue(value);
  }
  
  void StreamingFlowGraphBuilder::SetupDefaultParameterValues() {
    intptr_t optional_parameter_count =
        parsed_function()->function().NumOptionalParameters();
    if (optional_parameter_count > 0) {
      ZoneGrowableArray<const Instance*>* default_values =
          new ZoneGrowableArray<const Instance*>(Z, optional_parameter_count);
  
      AlternativeReadingScope alt(&reader_);
      FunctionNodeHelper function_node_helper(this);
      function_node_helper.ReadUntilExcluding(
          FunctionNodeHelper::kPositionalParameters);
  
      if (parsed_function()->function().HasOptionalNamedParameters()) {
        // List of positional.
        intptr_t list_length = ReadListLength();  // read list length.
        for (intptr_t i = 0; i < list_length; ++i) {
          SkipVariableDeclaration();  // read ith variable declaration.
        }
  
        // List of named.
        list_length = ReadListLength();  // read list length.
        ASSERT(optional_parameter_count == list_length);
        ASSERT(!parsed_function()->function().HasOptionalPositionalParameters());
        for (intptr_t i = 0; i < list_length; ++i) {
          Instance* default_value;
  
          // Read ith variable declaration
          VariableDeclarationHelper helper(this);
          helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
          Tag tag = ReadTag();  // read (first part of) initializer.
          if (tag == kSomething) {
            // This will read the initializer.
            default_value = &Instance::ZoneHandle(
                Z, constant_reader_.ReadConstantExpression());
          } else {
            default_value = &Instance::ZoneHandle(Z, Instance::null());
          }
          default_values->Add(default_value);
        }
      } else {
        // List of positional.
        intptr_t list_length = ReadListLength();  // read list length.
        ASSERT(list_length == function_node_helper.required_parameter_count_ +
                                  optional_parameter_count);
        ASSERT(parsed_function()->function().HasOptionalPositionalParameters());
        for (intptr_t i = 0; i < function_node_helper.required_parameter_count_;
             ++i) {
          SkipVariableDeclaration();  // read ith variable declaration.
        }
        for (intptr_t i = 0; i < optional_parameter_count; ++i) {
          Instance* default_value;
  
          // Read ith variable declaration
          VariableDeclarationHelper helper(this);
          helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
          Tag tag = ReadTag();  // read (first part of) initializer.
          if (tag == kSomething) {
            // This will read the initializer.
            default_value = &Instance::ZoneHandle(
                Z, constant_reader_.ReadConstantExpression());
          } else {
            default_value = &Instance::ZoneHandle(Z, Instance::null());
          }
          default_values->Add(default_value);
        }
  
        // List of named.
        list_length = ReadListLength();  // read list length.
        ASSERT(list_length == 0);
      }
      parsed_function()->set_default_parameter_values(default_values);
    }
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFieldInitializer(
      const Field& field,
      bool only_for_side_effects) {
    ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
    if (PeekTag() == kNullLiteral) {
      SkipExpression();  // read past the null literal.
      if (H.thread()->IsMutatorThread()) {
        field.RecordStore(Object::null_object());
      } else {
        ASSERT(field.is_nullable(/* silence_assert = */ true));
      }
      return Fragment();
    }
  
    Fragment instructions;
    if (!only_for_side_effects) {
      instructions += LoadLocal(parsed_function()->receiver_var());
    }
    // All closures created inside BuildExpression will have
    // field.RawOwner() as its owner.
    closure_owner_ = field.RawOwner();
    instructions += BuildExpression();
    closure_owner_ = Object::null();
    if (only_for_side_effects) {
      instructions += Drop();
    } else {
      instructions += flow_graph_builder_->StoreInstanceFieldGuarded(
          field, StoreInstanceFieldInstr::Kind::kInitializing);
    }
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildLateFieldInitializer(
      const Field& field,
      bool has_initializer) {
    if (has_initializer && PeekTag() == kNullLiteral) {
      SkipExpression();  // read past the null literal.
      if (H.thread()->IsMutatorThread()) {
        field.RecordStore(Object::null_object());
      } else {
        ASSERT(field.is_nullable(/* silence_assert = */ true));
      }
      return Fragment();
    }
  
    Fragment instructions;
    instructions += LoadLocal(parsed_function()->receiver_var());
    instructions += flow_graph_builder_->Constant(Object::sentinel());
    instructions += flow_graph_builder_->StoreInstanceField(
        field, StoreInstanceFieldInstr::Kind::kInitializing);
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildInitializers(
      const Class& parent_class) {
    ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
    Fragment instructions;
  
    // Start by getting the position of the constructors initializer.
    intptr_t initializers_offset = -1;
    {
      AlternativeReadingScope alt(&reader_);
      SkipFunctionNode();  // read constructors function node.
      initializers_offset = ReaderOffset();
    }
  
    bool is_redirecting_constructor = false;
  
    // Field which will be initialized by the initializer with the given index.
    GrowableArray<const Field*> initializer_fields(5);
  
    // Check if this is a redirecting constructor and collect all fields which
    // will be initialized by the constructor initializer list.
    {
      AlternativeReadingScope alt(&reader_, initializers_offset);
  
      const intptr_t list_length =
          ReadListLength();  // read initializers list length.
      initializer_fields.EnsureLength(list_length, nullptr);
  
      bool has_field_initializers = false;
      for (intptr_t i = 0; i < list_length; ++i) {
        if (PeekTag() == kRedirectingInitializer) {
          is_redirecting_constructor = true;
        } else if (PeekTag() == kFieldInitializer) {
          has_field_initializers = true;
          ReadTag();
          ReadBool();
          const NameIndex field_name = ReadCanonicalNameReference();
          const Field& field =
              Field::Handle(Z, H.LookupFieldByKernelField(field_name));
          initializer_fields[i] = &field;
          SkipExpression();
          continue;
        }
        SkipInitializer();
      }
      ASSERT(!is_redirecting_constructor || !has_field_initializers);
    }
  
    // These come from:
    //
    //   class A {
    //     var x = (expr);
    //   }
    //
    // We don't want to do that when this is a Redirecting Constructors though
    // (i.e. has a single initializer being of type kRedirectingInitializer).
    if (!is_redirecting_constructor) {
      // Sort list of fields (represented as their kernel offsets) which will
      // be initialized by the constructor initializer list. We will not emit
      // StoreInstanceField instructions for those initializers though we will
      // still evaluate initialization expression for its side effects.
      GrowableArray<intptr_t> constructor_initialized_field_offsets(
          initializer_fields.length());
      for (auto field : initializer_fields) {
        if (field != nullptr) {
          constructor_initialized_field_offsets.Add(field->kernel_offset());
        }
      }
  
      constructor_initialized_field_offsets.Sort(
          [](const intptr_t* a, const intptr_t* b) {
            return static_cast<int>(*a) - static_cast<int>(*b);
          });
      constructor_initialized_field_offsets.Add(-1);
  
      ExternalTypedData& kernel_data = ExternalTypedData::Handle(Z);
      Array& class_fields = Array::Handle(Z, parent_class.fields());
      Field& class_field = Field::Handle(Z);
      intptr_t next_constructor_initialized_field_index = 0;
      for (intptr_t i = 0; i < class_fields.Length(); ++i) {
        class_field ^= class_fields.At(i);
        if (!class_field.is_static()) {
          const intptr_t field_offset = class_field.kernel_offset();
  
          // Check if this field will be initialized by the constructor
          // initializer list.
          // Note that both class_fields and the list of initialized fields
          // are sorted by their kernel offset (by construction) -
          // so we don't need to perform the search.
          bool is_constructor_initialized = false;
          const intptr_t constructor_initialized_field_offset =
              constructor_initialized_field_offsets
                  [next_constructor_initialized_field_index];
          if (constructor_initialized_field_offset == field_offset) {
            next_constructor_initialized_field_index++;
            is_constructor_initialized = true;
          }
  
          kernel_data = class_field.KernelData();
          ASSERT(!kernel_data.IsNull());
          AlternativeReadingScopeWithNewData alt(&reader_, &kernel_data,
                                                 field_offset);
          FieldHelper field_helper(this);
          field_helper.ReadUntilExcluding(FieldHelper::kInitializer);
          const Tag initializer_tag = ReadTag();
          if (class_field.is_late()) {
            if (!is_constructor_initialized) {
              instructions += BuildLateFieldInitializer(
                  Field::ZoneHandle(Z, class_field.raw()),
                  initializer_tag == kSomething);
            }
          } else if (initializer_tag == kSomething) {
            EnterScope(field_offset);
            // If this field is initialized in constructor then we can ignore the
            // value produced by the field initializer. However we still need to
            // execute it for its side effects.
            instructions += BuildFieldInitializer(
                Field::ZoneHandle(Z, class_field.raw()),
                /*only_for_side_effects=*/is_constructor_initialized);
            ExitScope(field_offset);
          }
        }
      }
    }
  
    // These to come from:
    //   class A {
    //     var x;
    //     var y;
    //     A(this.x) : super(expr), y = (expr);
    //   }
    {
      AlternativeReadingScope alt(&reader_, initializers_offset);
      intptr_t list_length = ReadListLength();  // read initializers list length.
      for (intptr_t i = 0; i < list_length; ++i) {
        Tag tag = ReadTag();
        bool isSynthetic = ReadBool();  // read isSynthetic flag.
        switch (tag) {
          case kInvalidInitializer:
            UNIMPLEMENTED();
            return Fragment();
          case kFieldInitializer: {
            ReadCanonicalNameReference();
            instructions += BuildFieldInitializer(
                Field::ZoneHandle(Z, initializer_fields[i]->raw()),
                /*only_for_size_effects=*/false);
            break;
          }
          case kAssertInitializer: {
            instructions += BuildStatement();
            break;
          }
          case kSuperInitializer: {
            TokenPosition position = ReadPosition();  // read position.
            NameIndex canonical_target =
                ReadCanonicalNameReference();  // read target_reference.
  
            instructions += LoadLocal(parsed_function()->receiver_var());
  
            // TODO(jensj): ASSERT(init->arguments()->types().length() == 0);
            Array& argument_names = Array::ZoneHandle(Z);
            intptr_t argument_count;
            instructions += BuildArguments(
                &argument_names, &argument_count,
                /* positional_parameter_count = */ NULL);  // read arguments.
            argument_count += 1;
  
            Class& parent_klass = GetSuperOrDie();
  
            const Function& target = Function::ZoneHandle(
                Z, H.LookupConstructorByKernelConstructor(
                       parent_klass, H.CanonicalNameString(canonical_target)));
            instructions += StaticCall(
                isSynthetic ? TokenPosition::kNoSource : position, target,
                argument_count, argument_names, ICData::kStatic);
            instructions += Drop();
            break;
          }
          case kRedirectingInitializer: {
            TokenPosition position = ReadPosition();  // read position.
            NameIndex canonical_target =
                ReadCanonicalNameReference();  // read target_reference.
  
            instructions += LoadLocal(parsed_function()->receiver_var());
  
            // TODO(jensj): ASSERT(init->arguments()->types().length() == 0);
            Array& argument_names = Array::ZoneHandle(Z);
            intptr_t argument_count;
            instructions += BuildArguments(
                &argument_names, &argument_count,
                /* positional_parameter_count = */ NULL);  // read arguments.
            argument_count += 1;
  
            const Function& target = Function::ZoneHandle(
                Z, H.LookupConstructorByKernelConstructor(canonical_target));
            instructions += StaticCall(
                isSynthetic ? TokenPosition::kNoSource : position, target,
                argument_count, argument_names, ICData::kStatic);
            instructions += Drop();
            break;
          }
          case kLocalInitializer: {
            // The other initializers following this one might read the variable.
            // This is used e.g. for evaluating the arguments to a super call
            // first, run normal field initializers next and then make the actual
            // super call:
            //
            //   The frontend converts
            //
            //      class A {
            //        var x;
            //        A(a, b) : super(a + b), x = 2*b {}
            //      }
            //
            //   to
            //
            //      class A {
            //        var x;
            //        A(a, b) : tmp = a + b, x = 2*b, super(tmp) {}
            //      }
            //
            // (This is strictly speaking not what one should do in terms of the
            //  specification but that is how it is currently implemented.)
            LocalVariable* variable =
                LookupVariable(ReaderOffset() + data_program_offset_);
  
            // Variable declaration
            VariableDeclarationHelper helper(this);
            helper.ReadUntilExcluding(VariableDeclarationHelper::kInitializer);
            ASSERT(!helper.IsConst());
            Tag tag = ReadTag();  // read (first part of) initializer.
            if (tag != kSomething) {
              UNREACHABLE();
            }
  
            instructions += BuildExpression();  // read initializer.
            instructions += StoreLocal(TokenPosition::kNoSource, variable);
            instructions += Drop();
            break;
          }
          default:
            ReportUnexpectedTag("initializer", tag);
            UNREACHABLE();
        }
      }
    }
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::DebugStepCheckInPrologue(
      const Function& dart_function,
      TokenPosition position) {
    if (!NeedsDebugStepCheck(dart_function, position)) {
      return {};
    }
  
    // Place this check at the last parameter to ensure parameters
    // are in scope in the debugger at method entry.
    const int parameter_count = dart_function.NumParameters();
    TokenPosition check_pos = TokenPosition::kNoSource;
    if (parameter_count > 0) {
      const LocalVariable& parameter =
          *parsed_function()->ParameterVariable(parameter_count - 1);
      check_pos = parameter.token_pos();
    }
    if (!check_pos.IsDebugPause()) {
      // No parameters or synthetic parameters.
      check_pos = position;
      ASSERT(check_pos.IsDebugPause());
    }
  
    return DebugStepCheck(check_pos);
  }
  
  Fragment StreamingFlowGraphBuilder::TypeArgumentsHandling(
      const Function& dart_function) {
    Fragment prologue = B->BuildDefaultTypeHandling(dart_function);
  
    if (dart_function.IsClosureFunction() &&
        dart_function.NumParentTypeParameters() > 0) {
      LocalVariable* closure = parsed_function()->ParameterVariable(0);
  
      // Function with yield points can not be generic itself but the outer
      // function can be.
      ASSERT(yield_continuations().is_empty() || !dart_function.IsGeneric());
  
      LocalVariable* fn_type_args = parsed_function()->function_type_arguments();
      ASSERT(fn_type_args != NULL && closure != NULL);
  
      if (dart_function.IsGeneric()) {
        prologue += LoadLocal(fn_type_args);
  
        prologue += LoadLocal(closure);
        prologue += LoadNativeField(Slot::Closure_function_type_arguments());
  
        prologue += IntConstant(dart_function.NumParentTypeParameters());
  
        prologue += IntConstant(dart_function.NumTypeParameters() +
                                dart_function.NumParentTypeParameters());
  
        const auto& prepend_function =
            flow_graph_builder_->PrependTypeArgumentsFunction();
  
        prologue += StaticCall(TokenPosition::kNoSource, prepend_function, 4,
                               ICData::kStatic);
        prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args);
        prologue += Drop();
      } else {
        prologue += LoadLocal(closure);
        prologue += LoadNativeField(Slot::Closure_function_type_arguments());
        prologue += StoreLocal(TokenPosition::kNoSource, fn_type_args);
        prologue += Drop();
      }
    }
  
    return prologue;
  }
  
  Fragment StreamingFlowGraphBuilder::CompleteBodyWithYieldContinuations(
      Fragment body) {
    // The code we are building will be executed right after we enter
    // the function and before any nested contexts are allocated.
    // Reset current context_depth_ to match this.
    const intptr_t current_context_depth = B->context_depth_;
    B->context_depth_ = scopes()->yield_jump_variable->owner()->context_level();
  
    // Prepend an entry corresponding to normal entry to the function.
    yield_continuations().InsertAt(
        0, YieldContinuation(new (Z) DropTempsInstr(0, NULL), kInvalidTryIndex));
    yield_continuations()[0].entry->LinkTo(body.entry);
  
    // Load :await_jump_var into a temporary.
    Fragment dispatch;
    dispatch += LoadLocal(scopes()->yield_jump_variable);
    dispatch += StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable);
    dispatch += Drop();
  
    const intptr_t continuation_count = yield_continuations().length();
  
    IndirectGotoInstr* indirect_goto;
    if (FLAG_async_igoto_threshold >= 0 &&
        continuation_count >= FLAG_async_igoto_threshold) {
      const auto& offsets = TypedData::ZoneHandle(
          Z, TypedData::New(kTypedDataInt32ArrayCid, continuation_count,
                            Heap::kOld));
  
      dispatch += Constant(offsets);
      dispatch += LoadLocal(scopes()->switch_variable);
  
      // Ideally this would just be LoadIndexedTypedData(kTypedDataInt32ArrayCid),
      // but that doesn't work in unoptimised code.
      // The optimiser will turn this into that in any case.
      dispatch += InstanceCall(TokenPosition::kNoSource, Symbols::IndexToken(),
                               Token::kINDEX, /*argument_count=*/2);
  
      Value* offset_from_start = Pop();
  
      indirect_goto = new (Z) IndirectGotoInstr(&offsets, offset_from_start);
      dispatch <<= indirect_goto;
  
      for (intptr_t i = 0; i < continuation_count; i++) {
        if (i >= 1) {
          Fragment resumption;
          // Every continuation after the first is not a normal entry but a
          // resumption.
          // Restore :current_context_var from :await_ctx_var.
          // Note: after this point context_depth_ does not match current context
          // depth so we should not access any local variables anymore.
          resumption += LoadLocal(scopes()->yield_context_variable);
          resumption += StoreLocal(TokenPosition::kNoSource,
                                   parsed_function()->current_context_var());
          resumption += Drop();
  
          Instruction* next = yield_continuations()[i].entry->next();
          yield_continuations()[i].entry->LinkTo(resumption.entry);
          resumption <<= next;
        }
  
        IndirectEntryInstr* indirect_entry = B->BuildIndirectEntry(
            /*indirect_id=*/i, yield_continuations()[i].try_index);
        indirect_entry->LinkTo(yield_continuations()[i].entry->next());
  
        TargetEntryInstr* target = B->BuildTargetEntry();
        Fragment(target) + Goto(indirect_entry);
  
        indirect_goto->AddSuccessor(target);
      }
    } else {
      BlockEntryInstr* block = nullptr;
      for (intptr_t i = 0; i < continuation_count; i++) {
        if (i == 1) {
          // This is not a normal entry but a resumption.  Restore
          // :current_context_var from :await_ctx_var.
          // Note: after this point context_depth_ does not match current context
          // depth so we should not access any local variables anymore.
          dispatch += LoadLocal(scopes()->yield_context_variable);
          dispatch += StoreLocal(TokenPosition::kNoSource,
                                 parsed_function()->current_context_var());
          dispatch += Drop();
        }
        if (i == (continuation_count - 1)) {
          // We reached the last possibility, no need to build more ifs.
          // Continue to the last continuation.
          // Note: continuations start with nop DropTemps instruction
          // which acts like an anchor, so we need to skip it.
          block->set_try_index(yield_continuations()[i].try_index);
          dispatch <<= yield_continuations()[i].entry->next();
          break;
        }
  
        // Build comparison:
        //
        //   if (:await_jump_var == i) {
        //     -> yield_continuations()[i]
        //   } else ...
        //
        TargetEntryInstr* then;
        TargetEntryInstr* otherwise;
        dispatch += LoadLocal(scopes()->switch_variable);
        dispatch += IntConstant(i);
        dispatch += B->BranchIfStrictEqual(&then, &otherwise);
  
        // True branch is linked to appropriate continuation point.
        // Note: continuations start with nop DropTemps instruction
        // which acts like an anchor, so we need to skip it.
        then->LinkTo(yield_continuations()[i].entry->next());
        then->set_try_index(yield_continuations()[i].try_index);
        // False branch will contain the next comparison.
        dispatch = Fragment(dispatch.entry, otherwise);
        block = otherwise;
      }
    }
    B->context_depth_ = current_context_depth;
    return dispatch;
  }
  
  Fragment StreamingFlowGraphBuilder::CheckStackOverflowInPrologue(
      const Function& dart_function) {
    if (dart_function.is_native()) return {};
    return B->CheckStackOverflowInPrologue(dart_function.token_pos());
  }
  
  Fragment StreamingFlowGraphBuilder::SetupCapturedParameters(
      const Function& dart_function) {
    Fragment body;
    const LocalScope* scope = parsed_function()->scope();
    if (scope->num_context_variables() > 0) {
      body += flow_graph_builder_->PushContext(scope);
      LocalVariable* context = MakeTemporary();
  
      // Copy captured parameters from the stack into the context.
      LocalScope* scope = parsed_function()->scope();
      intptr_t parameter_count = dart_function.NumParameters();
      const ParsedFunction& pf = *flow_graph_builder_->parsed_function_;
      const Function& function = pf.function();
  
      for (intptr_t i = 0; i < parameter_count; ++i) {
        LocalVariable* variable = pf.ParameterVariable(i);
        if (variable->is_captured()) {
          LocalVariable& raw_parameter = *pf.RawParameterVariable(i);
          ASSERT((function.HasOptionalParameters() &&
                  raw_parameter.owner() == scope) ||
                 (!function.HasOptionalParameters() &&
                  raw_parameter.owner() == NULL));
          ASSERT(!raw_parameter.is_captured());
  
          // Copy the parameter from the stack to the context.
          body += LoadLocal(context);
          body += LoadLocal(&raw_parameter);
          body += flow_graph_builder_->StoreInstanceField(
              TokenPosition::kNoSource,
              Slot::GetContextVariableSlotFor(thread(), *variable),
              StoreInstanceFieldInstr::Kind::kInitializing);
        }
      }
      body += Drop();  // The context.
    }
    return body;
  }
  
  Fragment StreamingFlowGraphBuilder::ShortcutForUserDefinedEquals(
      const Function& dart_function,
      LocalVariable* first_parameter) {
    // The specification defines the result of `a == b` to be:
    //
    //   a) if either side is `null` then the result is `identical(a, b)`.
    //   b) else the result is `a.operator==(b)`
    //
    // For user-defined implementations of `operator==` we need therefore
    // implement the handling of a).
    //
    // The default `operator==` implementation in `Object` is implemented in terms
    // of identical (which we assume here!) which means that case a) is actually
    // included in b).  So we just use the normal implementation in the body.
    Fragment body;
    if ((dart_function.NumParameters() == 2) &&
        (dart_function.name() == Symbols::EqualOperator().raw()) &&
        (dart_function.Owner() != I->object_store()->object_class())) {
      TargetEntryInstr* null_entry;
      TargetEntryInstr* non_null_entry;
  
      body += LoadLocal(first_parameter);
      body += BranchIfNull(&null_entry, &non_null_entry);
  
      // The argument was `null` and the receiver is not the null class (we only
      // go into this branch for user-defined == operators) so we can return
      // false.
      Fragment null_fragment(null_entry);
      null_fragment += Constant(Bool::False());
      null_fragment += Return(dart_function.end_token_pos());
  
      body = Fragment(body.entry, non_null_entry);
    }
    return body;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFunctionBody(
      const Function& dart_function,
      LocalVariable* first_parameter,
      bool constructor) {
    Fragment body;
  
    // TODO(27590): Currently the [VariableDeclaration]s from the
    // initializers will be visible inside the entire body of the constructor.
    // We should make a separate scope for them.
    if (constructor) {
      body += BuildInitializers(Class::Handle(Z, dart_function.Owner()));
    }
  
    if (body.is_closed()) return body;
  
    FunctionNodeHelper function_node_helper(this);
    function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kBody);
  
    const bool has_body = ReadTag() == kSomething;  // read first part of body.
  
    if (dart_function.is_native()) {
      body += B->NativeFunctionBody(dart_function, first_parameter);
    } else if (has_body) {
      body += BuildStatement();
    } else if (dart_function.is_external()) {
      body += ThrowNoSuchMethodError(dart_function);
    }
  
    if (body.is_open()) {
      body += NullConstant();
      body += Return(dart_function.end_token_pos());
    }
  
    return body;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildEveryTimePrologue(
      const Function& dart_function,
      TokenPosition token_position,
      intptr_t type_parameters_offset) {
    Fragment F;
    F += CheckStackOverflowInPrologue(dart_function);
    F += DebugStepCheckInPrologue(dart_function, token_position);
    F += B->InitConstantParameters();
    return F;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFirstTimePrologue(
      const Function& dart_function,
      LocalVariable* first_parameter,
      intptr_t type_parameters_offset) {
    Fragment F;
    F += SetupCapturedParameters(dart_function);
    F += ShortcutForUserDefinedEquals(dart_function, first_parameter);
    return F;
  }
  
  Fragment StreamingFlowGraphBuilder::ClearRawParameters(
      const Function& dart_function) {
    const ParsedFunction& pf = *flow_graph_builder_->parsed_function_;
    Fragment code;
    for (intptr_t i = 0; i < dart_function.NumParameters(); ++i) {
      LocalVariable* variable = pf.ParameterVariable(i);
  
      if (!variable->is_captured()) continue;
  
      // Captured 'this' is immutable, so within the outer method we don't need to
      // load it from the context. Therefore we don't reset it to null.
      if (pf.function().HasThisParameter() && pf.has_receiver_var() &&
          variable == pf.receiver_var()) {
        ASSERT(i == 0);
        continue;
      }
  
      variable = pf.RawParameterVariable(i);
      code += NullConstant();
      code += StoreLocal(TokenPosition::kNoSource, variable);
      code += Drop();
    }
    return code;
  }
  
  UncheckedEntryPointStyle StreamingFlowGraphBuilder::ChooseEntryPointStyle(
      const Function& dart_function,
      const Fragment& implicit_type_checks,
      const Fragment& first_time_prologue,
      const Fragment& every_time_prologue,
      const Fragment& type_args_handling) {
    ASSERT(!dart_function.IsImplicitClosureFunction());
    if (!dart_function.MayHaveUncheckedEntryPoint() ||
        implicit_type_checks.is_empty()) {
      return UncheckedEntryPointStyle::kNone;
    }
  
    // Record which entry-point was taken into a variable and test it later if
    // either:
    //
    // 1. There is a non-empty PrologueBuilder-prologue.
    //
    // 2. There is a non-empty "first-time" prologue.
    //
    // 3. The "every-time" prologue has more than two instructions (DebugStepCheck
    //    and CheckStackOverflow).
    //
    // TODO(#34162): For regular closures we can often avoid the
    // PrologueBuilder-prologue on non-dynamic invocations.
    if (!PrologueBuilder::HasEmptyPrologue(dart_function) ||
        !type_args_handling.is_empty() || !first_time_prologue.is_empty() ||
        !(every_time_prologue.entry == every_time_prologue.current ||
          every_time_prologue.current->previous() == every_time_prologue.entry)) {
      return UncheckedEntryPointStyle::kSharedWithVariable;
    }
  
    return UncheckedEntryPointStyle::kSeparate;
  }
  
  FlowGraph* StreamingFlowGraphBuilder::BuildGraphOfFunction(
      bool is_constructor) {
    const Function& dart_function = parsed_function()->function();
  
    intptr_t type_parameters_offset = 0;
    LocalVariable* first_parameter = nullptr;
    TokenPosition token_position = TokenPosition::kNoSource;
    {
      AlternativeReadingScope alt(&reader_);
      FunctionNodeHelper function_node_helper(this);
      function_node_helper.ReadUntilExcluding(
          FunctionNodeHelper::kTypeParameters);
      type_parameters_offset = ReaderOffset();
      function_node_helper.ReadUntilExcluding(
          FunctionNodeHelper::kPositionalParameters);
      intptr_t list_length = ReadListLength();  // read number of positionals.
      if (list_length > 0) {
        intptr_t first_parameter_offset = ReaderOffset() + data_program_offset_;
        first_parameter = LookupVariable(first_parameter_offset);
      }
      token_position = function_node_helper.position_;
    }
  
    auto graph_entry = flow_graph_builder_->graph_entry_ =
        new (Z) GraphEntryInstr(*parsed_function(), flow_graph_builder_->osr_id_);
  
    auto normal_entry = flow_graph_builder_->BuildFunctionEntry(graph_entry);
    graph_entry->set_normal_entry(normal_entry);
  
    PrologueInfo prologue_info(-1, -1);
    BlockEntryInstr* instruction_cursor =
        flow_graph_builder_->BuildPrologue(normal_entry, &prologue_info);
  
    // The 'every_time_prologue' runs first and is run when resuming from yield
    // points.
    const Fragment every_time_prologue = BuildEveryTimePrologue(
        dart_function, token_position, type_parameters_offset);
  
    // The 'first_time_prologue' run after 'every_time_prologue' and is *not* run
    // when resuming from yield points.
    const Fragment first_time_prologue = BuildFirstTimePrologue(
        dart_function, first_parameter, type_parameters_offset);
  
    // TODO(#34162): We can remove the default type handling (and
    // shorten the prologue type handling sequence) for non-dynamic invocations of
    // regular methods.
    const Fragment type_args_handling = TypeArgumentsHandling(dart_function);
  
    Fragment implicit_type_checks;
    if (dart_function.NeedsTypeArgumentTypeChecks()) {
      B->BuildTypeArgumentTypeChecks(
          TypeChecksToBuild::kCheckCovariantTypeParameterBounds,
          &implicit_type_checks);
    }
  
    Fragment explicit_type_checks;
    Fragment implicit_redefinitions;
    if (dart_function.NeedsArgumentTypeChecks()) {
      B->BuildArgumentTypeChecks(&explicit_type_checks, &implicit_type_checks,
                                 &implicit_redefinitions);
    }
  
    // The RawParameter variables should be set to null to avoid retaining more
    // objects than necessary during GC.
    const Fragment body =
        ClearRawParameters(dart_function) + B->BuildNullAssertions() +
        BuildFunctionBody(dart_function, first_parameter, is_constructor);
  
    auto extra_entry_point_style = ChooseEntryPointStyle(
        dart_function, implicit_type_checks, first_time_prologue,
        every_time_prologue, type_args_handling);
  
    Fragment function(instruction_cursor);
    if (yield_continuations().is_empty()) {
      FunctionEntryInstr* extra_entry = nullptr;
      switch (extra_entry_point_style) {
        case UncheckedEntryPointStyle::kNone: {
          function += every_time_prologue + first_time_prologue +
                      type_args_handling + implicit_type_checks +
                      explicit_type_checks + body;
          break;
        }
        case UncheckedEntryPointStyle::kSeparate: {
          ASSERT(instruction_cursor == normal_entry);
          ASSERT(first_time_prologue.is_empty());
          ASSERT(type_args_handling.is_empty());
  
          const Fragment prologue_copy = BuildEveryTimePrologue(
              dart_function, token_position, type_parameters_offset);
  
          extra_entry = B->BuildSeparateUncheckedEntryPoint(
              normal_entry,
              /*normal_prologue=*/every_time_prologue + implicit_type_checks,
              /*extra_prologue=*/prologue_copy,
              /*shared_prologue=*/explicit_type_checks,
              /*body=*/body);
          break;
        }
        case UncheckedEntryPointStyle::kSharedWithVariable: {
          Fragment prologue(normal_entry, instruction_cursor);
          prologue += every_time_prologue;
          prologue += first_time_prologue;
          prologue += type_args_handling;
          prologue += explicit_type_checks;
          extra_entry = B->BuildSharedUncheckedEntryPoint(
              /*shared_prologue_linked_in=*/prologue,
              /*skippable_checks=*/implicit_type_checks,
              /*redefinitions_if_skipped=*/implicit_redefinitions,
              /*body=*/body);
          break;
        }
      }
      if (extra_entry != nullptr) {
        B->RecordUncheckedEntryPoint(graph_entry, extra_entry);
      }
    } else {
      // If the function's body contains any yield points, build switch statement
      // that selects a continuation point based on the value of :await_jump_var.
      ASSERT(explicit_type_checks.is_empty());
  
      // If the function is generic, type_args_handling might require access to
      // (possibly captured) 'this' for preparing default type arguments, in which
      // case we can't run it before the 'first_time_prologue'.
      ASSERT(!dart_function.IsGeneric());
  
      // TODO(#34162): We can probably ignore the implicit checks
      // here as well since the arguments are passed from generated code.
      function += every_time_prologue + type_args_handling +
                  CompleteBodyWithYieldContinuations(first_time_prologue +
                                                     implicit_type_checks + body);
    }
  
    // When compiling for OSR, use a depth first search to find the OSR
    // entry and make graph entry jump to it instead of normal entry.
    // Catch entries are always considered reachable, even if they
    // become unreachable after OSR.
    if (flow_graph_builder_->IsCompiledForOsr()) {
      graph_entry->RelinkToOsrEntry(Z,
                                    flow_graph_builder_->last_used_block_id_ + 1);
    }
    return new (Z)
        FlowGraph(*parsed_function(), graph_entry,
                  flow_graph_builder_->last_used_block_id_, prologue_info);
  }
  
  FlowGraph* StreamingFlowGraphBuilder::BuildGraph() {
    ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
    ASSERT(flow_graph_builder_ != nullptr);
  
    const Function& function = parsed_function()->function();
  
    // Setup a [ActiveClassScope] and a [ActiveMemberScope] which will be used
    // e.g. for type translation.
    const Class& klass =
        Class::Handle(zone_, parsed_function()->function().Owner());
    Function& outermost_function =
        Function::Handle(Z, function.GetOutermostFunction());
  
    ActiveClassScope active_class_scope(active_class(), &klass);
    ActiveMemberScope active_member(active_class(), &outermost_function);
    ActiveTypeParametersScope active_type_params(active_class(), function, Z);
  
    ParseKernelASTFunction();
  
    switch (function.kind()) {
      case FunctionLayout::kRegularFunction:
      case FunctionLayout::kGetterFunction:
      case FunctionLayout::kSetterFunction:
      case FunctionLayout::kClosureFunction:
      case FunctionLayout::kConstructor: {
        if (B->IsRecognizedMethodForFlowGraph(function)) {
          return B->BuildGraphOfRecognizedMethod(function);
        }
        return BuildGraphOfFunction(function.IsGenerativeConstructor());
      }
      case FunctionLayout::kImplicitGetter:
      case FunctionLayout::kImplicitStaticGetter:
      case FunctionLayout::kImplicitSetter: {
        const Field& field = Field::Handle(Z, function.accessor_field());
        if (field.is_const() && field.IsUninitialized()) {
          EvaluateConstFieldValue(field);
        }
        return B->BuildGraphOfFieldAccessor(function);
      }
      case FunctionLayout::kFieldInitializer:
        return BuildGraphOfFieldInitializer();
      case FunctionLayout::kDynamicInvocationForwarder:
        return B->BuildGraphOfDynamicInvocationForwarder(function);
      case FunctionLayout::kMethodExtractor:
        return flow_graph_builder_->BuildGraphOfMethodExtractor(function);
      case FunctionLayout::kNoSuchMethodDispatcher:
        return flow_graph_builder_->BuildGraphOfNoSuchMethodDispatcher(function);
      case FunctionLayout::kInvokeFieldDispatcher:
        return flow_graph_builder_->BuildGraphOfInvokeFieldDispatcher(function);
      case FunctionLayout::kImplicitClosureFunction:
        return flow_graph_builder_->BuildGraphOfImplicitClosureFunction(function);
      case FunctionLayout::kFfiTrampoline:
        return flow_graph_builder_->BuildGraphOfFfiTrampoline(function);
      case FunctionLayout::kSignatureFunction:
      case FunctionLayout::kIrregexpFunction:
        break;
    }
    UNREACHABLE();
    return NULL;
  }
  
  void StreamingFlowGraphBuilder::ParseKernelASTFunction() {
    const Function& function = parsed_function()->function();
  
    const intptr_t kernel_offset = function.kernel_offset();
    ASSERT(kernel_offset >= 0);
  
    SetOffset(kernel_offset);
  
    // Mark forwarding stubs.
    switch (function.kind()) {
      case FunctionLayout::kRegularFunction:
      case FunctionLayout::kImplicitClosureFunction:
      case FunctionLayout::kGetterFunction:
      case FunctionLayout::kSetterFunction:
      case FunctionLayout::kClosureFunction:
      case FunctionLayout::kConstructor:
      case FunctionLayout::kDynamicInvocationForwarder:
        ReadForwardingStubTarget(function);
        break;
      default:
        break;
    }
  
    set_scopes(parsed_function()->EnsureKernelScopes());
  
    switch (function.kind()) {
      case FunctionLayout::kRegularFunction:
      case FunctionLayout::kGetterFunction:
      case FunctionLayout::kSetterFunction:
      case FunctionLayout::kClosureFunction:
      case FunctionLayout::kConstructor:
      case FunctionLayout::kImplicitClosureFunction:
        ReadUntilFunctionNode();
        SetupDefaultParameterValues();
        break;
      case FunctionLayout::kImplicitGetter:
      case FunctionLayout::kImplicitStaticGetter:
      case FunctionLayout::kImplicitSetter:
      case FunctionLayout::kFieldInitializer:
      case FunctionLayout::kMethodExtractor:
      case FunctionLayout::kNoSuchMethodDispatcher:
      case FunctionLayout::kInvokeFieldDispatcher:
      case FunctionLayout::kFfiTrampoline:
        break;
      case FunctionLayout::kDynamicInvocationForwarder:
        if (PeekTag() != kField) {
          ReadUntilFunctionNode();
          SetupDefaultParameterValues();
        }
        break;
      case FunctionLayout::kSignatureFunction:
      case FunctionLayout::kIrregexpFunction:
        UNREACHABLE();
        break;
    }
  }
  
  void StreamingFlowGraphBuilder::ReadForwardingStubTarget(
      const Function& function) {
    if (PeekTag() == kProcedure) {
      AlternativeReadingScope alt(&reader_);
      ProcedureHelper procedure_helper(this);
      procedure_helper.ReadUntilExcluding(ProcedureHelper::kFunction);
      if (procedure_helper.IsForwardingStub() && !procedure_helper.IsAbstract()) {
        const NameIndex target_name =
            procedure_helper.forwarding_stub_super_target_;
        ASSERT(target_name != NameIndex::kInvalidName);
        const String& name = function.IsSetterFunction()
                                 ? H.DartSetterName(target_name)
                                 : H.DartProcedureName(target_name);
        const Function* forwarding_target =
            &Function::ZoneHandle(Z, H.LookupMethodByMember(target_name, name));
        ASSERT(!forwarding_target->IsNull());
        parsed_function()->MarkForwardingStub(forwarding_target);
      }
    }
  }
  
  Fragment StreamingFlowGraphBuilder::BuildStatementAt(intptr_t kernel_offset) {
    SetOffset(kernel_offset);
    return BuildStatement();  // read statement.
  }
  
  Fragment StreamingFlowGraphBuilder::BuildExpression(TokenPosition* position) {
    uint8_t payload = 0;
    Tag tag = ReadTag(&payload);  // read tag.
    switch (tag) {
      case kInvalidExpression:
        return BuildInvalidExpression(position);
      case kVariableGet:
        return BuildVariableGet(position);
      case kSpecializedVariableGet:
        return BuildVariableGet(payload, position);
      case kVariableSet:
        return BuildVariableSet(position);
      case kSpecializedVariableSet:
        return BuildVariableSet(payload, position);
      case kPropertyGet:
        return BuildPropertyGet(position);
      case kPropertySet:
        return BuildPropertySet(position);
      case kSuperPropertyGet:
        return BuildSuperPropertyGet(position);
      case kSuperPropertySet:
        return BuildSuperPropertySet(position);
      case kStaticGet:
        return BuildStaticGet(position);
      case kStaticSet:
        return BuildStaticSet(position);
      case kMethodInvocation:
        return BuildMethodInvocation(position);
      case kSuperMethodInvocation:
        return BuildSuperMethodInvocation(position);
      case kStaticInvocation:
        return BuildStaticInvocation(position);
      case kConstructorInvocation:
        return BuildConstructorInvocation(position);
      case kNot:
        return BuildNot(position);
      case kNullCheck:
        return BuildNullCheck(position);
      case kLogicalExpression:
        return BuildLogicalExpression(position);
      case kConditionalExpression:
        return BuildConditionalExpression(position);
      case kStringConcatenation:
        return BuildStringConcatenation(position);
      case kIsExpression:
        return BuildIsExpression(position);
      case kAsExpression:
        return BuildAsExpression(position);
      case kTypeLiteral:
        return BuildTypeLiteral(position);
      case kThisExpression:
        return BuildThisExpression(position);
      case kRethrow:
        return BuildRethrow(position);
      case kThrow:
        return BuildThrow(position);
      case kListLiteral:
        return BuildListLiteral(position);
      case kSetLiteral:
        // Set literals are currently desugared in the frontend and will not
        // reach the VM. See http://dartbug.com/35124 for discussion.
        UNREACHABLE();
        break;
      case kMapLiteral:
        return BuildMapLiteral(position);
      case kFunctionExpression:
        return BuildFunctionExpression();
      case kLet:
        return BuildLet(position);
      case kBlockExpression:
        return BuildBlockExpression();
      case kBigIntLiteral:
        return BuildBigIntLiteral(position);
      case kStringLiteral:
        return BuildStringLiteral(position);
      case kSpecializedIntLiteral:
        return BuildIntLiteral(payload, position);
      case kNegativeIntLiteral:
        return BuildIntLiteral(true, position);
      case kPositiveIntLiteral:
        return BuildIntLiteral(false, position);
      case kDoubleLiteral:
        return BuildDoubleLiteral(position);
      case kTrueLiteral:
        return BuildBoolLiteral(true, position);
      case kFalseLiteral:
        return BuildBoolLiteral(false, position);
      case kNullLiteral:
        return BuildNullLiteral(position);
      case kConstantExpression:
        return BuildConstantExpression(position, tag);
      case kInstantiation:
        return BuildPartialTearoffInstantiation(position);
      case kLoadLibrary:
        return BuildLibraryPrefixAction(position, Symbols::LoadLibrary());
      case kCheckLibraryIsLoaded:
        return BuildLibraryPrefixAction(position, Symbols::CheckLoaded());
      case kConstStaticInvocation:
      case kConstConstructorInvocation:
      case kConstListLiteral:
      case kConstSetLiteral:
      case kConstMapLiteral:
      case kSymbolLiteral:
        // Const invocations and const literals are removed by the
        // constant evaluator.
      case kListConcatenation:
      case kSetConcatenation:
      case kMapConcatenation:
      case kInstanceCreation:
      case kFileUriExpression:
        // Collection concatenation, instance creation operations and
        // in-expression URI changes are internal to the front end and
        // removed by the constant evaluator.
      default:
        ReportUnexpectedTag("expression", tag);
        UNREACHABLE();
    }
  
    return Fragment();
  }
  
  Fragment StreamingFlowGraphBuilder::BuildStatement() {
    Tag tag = ReadTag();  // read tag.
    switch (tag) {
      case kExpressionStatement:
        return BuildExpressionStatement();
      case kBlock:
        return BuildBlock();
      case kEmptyStatement:
        return BuildEmptyStatement();
      case kAssertBlock:
        return BuildAssertBlock();
      case kAssertStatement:
        return BuildAssertStatement();
      case kLabeledStatement:
        return BuildLabeledStatement();
      case kBreakStatement:
        return BuildBreakStatement();
      case kWhileStatement:
        return BuildWhileStatement();
      case kDoStatement:
        return BuildDoStatement();
      case kForStatement:
        return BuildForStatement();
      case kForInStatement:
        return BuildForInStatement(false);
      case kAsyncForInStatement:
        return BuildForInStatement(true);
      case kSwitchStatement:
        return BuildSwitchStatement();
      case kContinueSwitchStatement:
        return BuildContinueSwitchStatement();
      case kIfStatement:
        return BuildIfStatement();
      case kReturnStatement:
        return BuildReturnStatement();
      case kTryCatch:
        return BuildTryCatch();
      case kTryFinally:
        return BuildTryFinally();
      case kYieldStatement:
        return BuildYieldStatement();
      case kVariableDeclaration:
        return BuildVariableDeclaration();
      case kFunctionDeclaration:
        return BuildFunctionDeclaration();
      default:
        ReportUnexpectedTag("statement", tag);
        UNREACHABLE();
    }
    return Fragment();
  }
  
  void StreamingFlowGraphBuilder::ReportUnexpectedTag(const char* variant,
                                                      Tag tag) {
    if ((flow_graph_builder_ == NULL) || (parsed_function() == NULL)) {
      KernelReaderHelper::ReportUnexpectedTag(variant, tag);
    } else {
      H.ReportError(script_, TokenPosition::kNoSource,
                    "Unexpected tag %d (%s) in %s, expected %s", tag,
                    Reader::TagName(tag),
                    parsed_function()->function().ToQualifiedCString(), variant);
    }
  }
  
  Tag KernelReaderHelper::ReadTag(uint8_t* payload) {
    return reader_.ReadTag(payload);
  }
  
  Tag KernelReaderHelper::PeekTag(uint8_t* payload) {
    return reader_.PeekTag(payload);
  }
  
  Nullability KernelReaderHelper::ReadNullability() {
    return reader_.ReadNullability();
  }
  
  Variance KernelReaderHelper::ReadVariance() {
    if (translation_helper_.info().kernel_binary_version() >= 34) {
      return reader_.ReadVariance();
    }
    return kCovariant;
  }
  
  void StreamingFlowGraphBuilder::loop_depth_inc() {
    ++flow_graph_builder_->loop_depth_;
  }
  
  void StreamingFlowGraphBuilder::loop_depth_dec() {
    --flow_graph_builder_->loop_depth_;
  }
  
  intptr_t StreamingFlowGraphBuilder::for_in_depth() {
    return flow_graph_builder_->for_in_depth_;
  }
  
  void StreamingFlowGraphBuilder::for_in_depth_inc() {
    ++flow_graph_builder_->for_in_depth_;
  }
  
  void StreamingFlowGraphBuilder::for_in_depth_dec() {
    --flow_graph_builder_->for_in_depth_;
  }
  
  void StreamingFlowGraphBuilder::catch_depth_inc() {
    ++flow_graph_builder_->catch_depth_;
  }
  
  void StreamingFlowGraphBuilder::catch_depth_dec() {
    --flow_graph_builder_->catch_depth_;
  }
  
  void StreamingFlowGraphBuilder::try_depth_inc() {
    ++flow_graph_builder_->try_depth_;
  }
  
  void StreamingFlowGraphBuilder::try_depth_dec() {
    --flow_graph_builder_->try_depth_;
  }
  
  intptr_t StreamingFlowGraphBuilder::block_expression_depth() {
    return flow_graph_builder_->block_expression_depth_;
  }
  
  void StreamingFlowGraphBuilder::block_expression_depth_inc() {
    ++flow_graph_builder_->block_expression_depth_;
  }
  
  void StreamingFlowGraphBuilder::block_expression_depth_dec() {
    --flow_graph_builder_->block_expression_depth_;
  }
  
  intptr_t StreamingFlowGraphBuilder::CurrentTryIndex() {
    return flow_graph_builder_->CurrentTryIndex();
  }
  
  intptr_t StreamingFlowGraphBuilder::AllocateTryIndex() {
    return flow_graph_builder_->AllocateTryIndex();
  }
  
  LocalVariable* StreamingFlowGraphBuilder::CurrentException() {
    return flow_graph_builder_->CurrentException();
  }
  
  LocalVariable* StreamingFlowGraphBuilder::CurrentStackTrace() {
    return flow_graph_builder_->CurrentStackTrace();
  }
  
  CatchBlock* StreamingFlowGraphBuilder::catch_block() {
    return flow_graph_builder_->catch_block_;
  }
  
  ActiveClass* StreamingFlowGraphBuilder::active_class() {
    return active_class_;
  }
  
  ScopeBuildingResult* StreamingFlowGraphBuilder::scopes() {
    return flow_graph_builder_->scopes_;
  }
  
  void StreamingFlowGraphBuilder::set_scopes(ScopeBuildingResult* scope) {
    flow_graph_builder_->scopes_ = scope;
  }
  
  ParsedFunction* StreamingFlowGraphBuilder::parsed_function() {
    return flow_graph_builder_->parsed_function_;
  }
  
  TryFinallyBlock* StreamingFlowGraphBuilder::try_finally_block() {
    return flow_graph_builder_->try_finally_block_;
  }
  
  SwitchBlock* StreamingFlowGraphBuilder::switch_block() {
    return flow_graph_builder_->switch_block_;
  }
  
  BreakableBlock* StreamingFlowGraphBuilder::breakable_block() {
    return flow_graph_builder_->breakable_block_;
  }
  
  GrowableArray<YieldContinuation>&
  StreamingFlowGraphBuilder::yield_continuations() {
    return flow_graph_builder_->yield_continuations_;
  }
  
  Value* StreamingFlowGraphBuilder::stack() {
    return flow_graph_builder_->stack_;
  }
  
  void StreamingFlowGraphBuilder::Push(Definition* definition) {
    flow_graph_builder_->Push(definition);
  }
  
  Value* StreamingFlowGraphBuilder::Pop() {
    return flow_graph_builder_->Pop();
  }
  
  Tag StreamingFlowGraphBuilder::PeekArgumentsFirstPositionalTag() {
    // read parts of arguments, then go back to before doing so.
    AlternativeReadingScope alt(&reader_);
    ReadUInt();  // read number of arguments.
  
    SkipListOfDartTypes();  // Read list of types.
  
    // List of positional.
    intptr_t list_length = ReadListLength();  // read list length.
    if (list_length > 0) {
      return ReadTag();  // read first tag.
    }
  
    UNREACHABLE();
    return kNothing;
  }
  
  const TypeArguments& StreamingFlowGraphBuilder::PeekArgumentsInstantiatedType(
      const Class& klass) {
    // read parts of arguments, then go back to before doing so.
    AlternativeReadingScope alt(&reader_);
    ReadUInt();                               // read argument count.
    intptr_t list_length = ReadListLength();  // read types list length.
    return T.BuildInstantiatedTypeArguments(klass, list_length);  // read types.
  }
  
  intptr_t StreamingFlowGraphBuilder::PeekArgumentsCount() {
    return PeekUInt();
  }
  
  LocalVariable* StreamingFlowGraphBuilder::LookupVariable(
      intptr_t kernel_offset) {
    return flow_graph_builder_->LookupVariable(kernel_offset);
  }
  
  LocalVariable* StreamingFlowGraphBuilder::MakeTemporary() {
    return flow_graph_builder_->MakeTemporary();
  }
  
  Function& StreamingFlowGraphBuilder::FindMatchingFunction(
      const Class& klass,
      const String& name,
      int type_args_len,
      int argument_count,
      const Array& argument_names) {
    // Search the superclass chain for the selector.
    ArgumentsDescriptor args_desc(
        Array::Handle(Z, ArgumentsDescriptor::NewBoxed(
                             type_args_len, argument_count, argument_names)));
    return Function::Handle(Z,
                            Resolver::ResolveDynamicForReceiverClassAllowPrivate(
                                klass, name, args_desc, /*allow_add=*/false));
  }
  
  bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(const Function& function,
                                                      TokenPosition position) {
    return flow_graph_builder_->NeedsDebugStepCheck(function, position);
  }
  
  bool StreamingFlowGraphBuilder::NeedsDebugStepCheck(Value* value,
                                                      TokenPosition position) {
    return flow_graph_builder_->NeedsDebugStepCheck(value, position);
  }
  
  void StreamingFlowGraphBuilder::InlineBailout(const char* reason) {
    flow_graph_builder_->InlineBailout(reason);
  }
  
  Fragment StreamingFlowGraphBuilder::DebugStepCheck(TokenPosition position) {
    return flow_graph_builder_->DebugStepCheck(position);
  }
  
  Fragment StreamingFlowGraphBuilder::LoadLocal(LocalVariable* variable) {
    return flow_graph_builder_->LoadLocal(variable);
  }
  
  Fragment StreamingFlowGraphBuilder::Return(TokenPosition position,
                                             intptr_t yield_index) {
    return flow_graph_builder_->Return(position, /*omit_result_type_check=*/false,
                                       yield_index);
  }
  
  Fragment StreamingFlowGraphBuilder::EvaluateAssertion() {
    return flow_graph_builder_->EvaluateAssertion();
  }
  
  Fragment StreamingFlowGraphBuilder::RethrowException(TokenPosition position,
                                                       int catch_try_index) {
    return flow_graph_builder_->RethrowException(position, catch_try_index);
  }
  
  Fragment StreamingFlowGraphBuilder::ThrowNoSuchMethodError(
      const Function& target) {
    return flow_graph_builder_->ThrowNoSuchMethodError(target);
  }
  
  Fragment StreamingFlowGraphBuilder::Constant(const Object& value) {
    return flow_graph_builder_->Constant(value);
  }
  
  Fragment StreamingFlowGraphBuilder::IntConstant(int64_t value) {
    return flow_graph_builder_->IntConstant(value);
  }
  
  Fragment StreamingFlowGraphBuilder::LoadStaticField(const Field& field,
                                                      bool calls_initializer) {
    return flow_graph_builder_->LoadStaticField(field, calls_initializer);
  }
  
  Fragment StreamingFlowGraphBuilder::RedefinitionWithType(
      const AbstractType& type) {
    return flow_graph_builder_->RedefinitionWithType(type);
  }
  
  Fragment StreamingFlowGraphBuilder::CheckNull(
      TokenPosition position,
      LocalVariable* receiver,
      const String& function_name,
      bool clear_the_temp /* = true */) {
    return flow_graph_builder_->CheckNull(position, receiver, function_name,
                                          clear_the_temp);
  }
  
  Fragment StreamingFlowGraphBuilder::StaticCall(TokenPosition position,
                                                 const Function& target,
                                                 intptr_t argument_count,
                                                 ICData::RebindRule rebind_rule) {
    if (!target.AreValidArgumentCounts(0, argument_count, 0, nullptr)) {
      return flow_graph_builder_->ThrowNoSuchMethodError(target);
    }
    return flow_graph_builder_->StaticCall(position, target, argument_count,
                                           rebind_rule);
  }
  
  Fragment StreamingFlowGraphBuilder::StaticCall(
      TokenPosition position,
      const Function& target,
      intptr_t argument_count,
      const Array& argument_names,
      ICData::RebindRule rebind_rule,
      const InferredTypeMetadata* result_type,
      intptr_t type_args_count,
      bool use_unchecked_entry) {
    if (!target.AreValidArguments(type_args_count, argument_count, argument_names,
                                  nullptr)) {
      return flow_graph_builder_->ThrowNoSuchMethodError(target);
    }
    return flow_graph_builder_->StaticCall(
        position, target, argument_count, argument_names, rebind_rule,
        result_type, type_args_count, use_unchecked_entry);
  }
  
  Fragment StreamingFlowGraphBuilder::InstanceCall(
      TokenPosition position,
      const String& name,
      Token::Kind kind,
      intptr_t argument_count,
      intptr_t checked_argument_count) {
    const intptr_t kTypeArgsLen = 0;
    return flow_graph_builder_->InstanceCall(position, name, kind, kTypeArgsLen,
                                             argument_count, Array::null_array(),
                                             checked_argument_count);
  }
  
  Fragment StreamingFlowGraphBuilder::InstanceCall(
      TokenPosition position,
      const String& name,
      Token::Kind kind,
      intptr_t type_args_len,
      intptr_t argument_count,
      const Array& argument_names,
      intptr_t checked_argument_count,
      const Function& interface_target,
      const Function& tearoff_interface_target,
      const InferredTypeMetadata* result_type,
      bool use_unchecked_entry,
      const CallSiteAttributesMetadata* call_site_attrs,
      bool receiver_is_not_smi) {
    return flow_graph_builder_->InstanceCall(
        position, name, kind, type_args_len, argument_count, argument_names,
        checked_argument_count, interface_target, tearoff_interface_target,
        result_type, use_unchecked_entry, call_site_attrs, receiver_is_not_smi);
  }
  
  Fragment StreamingFlowGraphBuilder::ThrowException(TokenPosition position) {
    return flow_graph_builder_->ThrowException(position);
  }
  
  Fragment StreamingFlowGraphBuilder::BooleanNegate() {
    return flow_graph_builder_->BooleanNegate();
  }
  
  Fragment StreamingFlowGraphBuilder::TranslateInstantiatedTypeArguments(
      const TypeArguments& type_arguments) {
    return flow_graph_builder_->TranslateInstantiatedTypeArguments(
        type_arguments);
  }
  
  Fragment StreamingFlowGraphBuilder::StrictCompare(TokenPosition position,
                                                    Token::Kind kind,
                                                    bool number_check) {
    return flow_graph_builder_->StrictCompare(position, kind, number_check);
  }
  
  Fragment StreamingFlowGraphBuilder::AllocateObject(TokenPosition position,
                                                     const Class& klass,
                                                     intptr_t argument_count) {
    return flow_graph_builder_->AllocateObject(position, klass, argument_count);
  }
  
  Fragment StreamingFlowGraphBuilder::AllocateContext(
      const ZoneGrowableArray<const Slot*>& context_slots) {
    return flow_graph_builder_->AllocateContext(context_slots);
  }
  
  Fragment StreamingFlowGraphBuilder::LoadNativeField(const Slot& field) {
    return flow_graph_builder_->LoadNativeField(field);
  }
  
  Fragment StreamingFlowGraphBuilder::StoreLocal(TokenPosition position,
                                                 LocalVariable* variable) {
    return flow_graph_builder_->StoreLocal(position, variable);
  }
  
  Fragment StreamingFlowGraphBuilder::StoreStaticField(TokenPosition position,
                                                       const Field& field) {
    return flow_graph_builder_->StoreStaticField(position, field);
  }
  
  Fragment StreamingFlowGraphBuilder::StringInterpolate(TokenPosition position) {
    return flow_graph_builder_->StringInterpolate(position);
  }
  
  Fragment StreamingFlowGraphBuilder::StringInterpolateSingle(
      TokenPosition position) {
    return flow_graph_builder_->StringInterpolateSingle(position);
  }
  
  Fragment StreamingFlowGraphBuilder::ThrowTypeError() {
    return flow_graph_builder_->ThrowTypeError();
  }
  
  Fragment StreamingFlowGraphBuilder::LoadInstantiatorTypeArguments() {
    return flow_graph_builder_->LoadInstantiatorTypeArguments();
  }
  
  Fragment StreamingFlowGraphBuilder::LoadFunctionTypeArguments() {
    return flow_graph_builder_->LoadFunctionTypeArguments();
  }
  
  Fragment StreamingFlowGraphBuilder::InstantiateType(const AbstractType& type) {
    return flow_graph_builder_->InstantiateType(type);
  }
  
  Fragment StreamingFlowGraphBuilder::CreateArray() {
    return flow_graph_builder_->CreateArray();
  }
  
  Fragment StreamingFlowGraphBuilder::StoreIndexed(intptr_t class_id) {
    return flow_graph_builder_->StoreIndexed(class_id);
  }
  
  Fragment StreamingFlowGraphBuilder::CheckStackOverflow(TokenPosition position) {
    return flow_graph_builder_->CheckStackOverflow(
        position, flow_graph_builder_->GetStackDepth(),
        flow_graph_builder_->loop_depth_);
  }
  
  Fragment StreamingFlowGraphBuilder::CloneContext(
      const ZoneGrowableArray<const Slot*>& context_slots) {
    return flow_graph_builder_->CloneContext(context_slots);
  }
  
  Fragment StreamingFlowGraphBuilder::TranslateFinallyFinalizers(
      TryFinallyBlock* outer_finally,
      intptr_t target_context_depth) {
    // TranslateFinallyFinalizers can move the readers offset.
    // Save the current position and restore it afterwards.
    AlternativeReadingScope alt(&reader_);
  
    // Save context.
    TryFinallyBlock* const saved_finally_block = B->try_finally_block_;
    TryCatchBlock* const saved_try_catch_block = B->CurrentTryCatchBlock();
    const intptr_t saved_context_depth = B->context_depth_;
    const ProgramState state(B->breakable_block_, B->switch_block_,
                             B->loop_depth_, B->for_in_depth_, B->try_depth_,
                             B->catch_depth_, B->block_expression_depth_);
  
    Fragment instructions;
  
    // While translating the body of a finalizer we need to set the try-finally
    // block which is active when translating the body.
    while (B->try_finally_block_ != outer_finally) {
      ASSERT(B->try_finally_block_ != nullptr);
      // Adjust program context to finalizer's position.
      B->try_finally_block_->state().assignTo(B);
  
      // Potentially restore the context to what is expected for the finally
      // block.
      instructions += B->AdjustContextTo(B->try_finally_block_->context_depth());
  
      // The to-be-translated finalizer has to have the correct try-index (namely
      // the one outside the try-finally block).
      bool changed_try_index = false;
      intptr_t target_try_index = B->try_finally_block_->try_index();
      while (B->CurrentTryIndex() != target_try_index) {
        B->SetCurrentTryCatchBlock(B->CurrentTryCatchBlock()->outer());
        changed_try_index = true;
      }
      if (changed_try_index) {
        JoinEntryInstr* entry = BuildJoinEntry();
        instructions += Goto(entry);
        instructions = Fragment(instructions.entry, entry);
      }
  
      intptr_t finalizer_kernel_offset =
          B->try_finally_block_->finalizer_kernel_offset();
      B->try_finally_block_ = B->try_finally_block_->outer();
      instructions += BuildStatementAt(finalizer_kernel_offset);
  
      // We only need to make sure that if the finalizer ended normally, we
      // continue towards the next outer try-finally.
      if (!instructions.is_open()) break;
    }
  
    if (instructions.is_open() && target_context_depth != -1) {
      // A target context depth of -1 indicates that the code after this
      // will not care about the context chain so we can leave it any way we
      // want after the last finalizer.  That is used when returning.
      instructions += B->AdjustContextTo(target_context_depth);
    }
  
    // Restore.
    B->try_finally_block_ = saved_finally_block;
    B->SetCurrentTryCatchBlock(saved_try_catch_block);
    B->context_depth_ = saved_context_depth;
    state.assignTo(B);
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BranchIfTrue(
      TargetEntryInstr** then_entry,
      TargetEntryInstr** otherwise_entry,
      bool negate) {
    return flow_graph_builder_->BranchIfTrue(then_entry, otherwise_entry, negate);
  }
  
  Fragment StreamingFlowGraphBuilder::BranchIfEqual(
      TargetEntryInstr** then_entry,
      TargetEntryInstr** otherwise_entry,
      bool negate) {
    return flow_graph_builder_->BranchIfEqual(then_entry, otherwise_entry,
                                              negate);
  }
  
  Fragment StreamingFlowGraphBuilder::BranchIfNull(
      TargetEntryInstr** then_entry,
      TargetEntryInstr** otherwise_entry,
      bool negate) {
    return flow_graph_builder_->BranchIfNull(then_entry, otherwise_entry, negate);
  }
  
  Fragment StreamingFlowGraphBuilder::CatchBlockEntry(const Array& handler_types,
                                                      intptr_t handler_index,
                                                      bool needs_stacktrace,
                                                      bool is_synthesized) {
    return flow_graph_builder_->CatchBlockEntry(handler_types, handler_index,
                                                needs_stacktrace, is_synthesized);
  }
  
  Fragment StreamingFlowGraphBuilder::TryCatch(int try_handler_index) {
    return flow_graph_builder_->TryCatch(try_handler_index);
  }
  
  Fragment StreamingFlowGraphBuilder::Drop() {
    return flow_graph_builder_->Drop();
  }
  
  Fragment StreamingFlowGraphBuilder::DropTempsPreserveTop(
      intptr_t num_temps_to_drop) {
    return flow_graph_builder_->DropTempsPreserveTop(num_temps_to_drop);
  }
  
  Fragment StreamingFlowGraphBuilder::MakeTemp() {
    return flow_graph_builder_->MakeTemp();
  }
  
  Fragment StreamingFlowGraphBuilder::NullConstant() {
    return flow_graph_builder_->NullConstant();
  }
  
  JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry() {
    return flow_graph_builder_->BuildJoinEntry();
  }
  
  JoinEntryInstr* StreamingFlowGraphBuilder::BuildJoinEntry(intptr_t try_index) {
    return flow_graph_builder_->BuildJoinEntry(try_index);
  }
  
  Fragment StreamingFlowGraphBuilder::Goto(JoinEntryInstr* destination) {
    return flow_graph_builder_->Goto(destination);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildImplicitClosureCreation(
      const Function& target) {
    return flow_graph_builder_->BuildImplicitClosureCreation(target);
  }
  
  Fragment StreamingFlowGraphBuilder::CheckBoolean(TokenPosition position) {
    return flow_graph_builder_->CheckBoolean(position);
  }
  
  Fragment StreamingFlowGraphBuilder::CheckArgumentType(
      LocalVariable* variable,
      const AbstractType& type) {
    return flow_graph_builder_->CheckAssignable(
        type, variable->name(), AssertAssignableInstr::kParameterCheck);
  }
  
  Fragment StreamingFlowGraphBuilder::EnterScope(
      intptr_t kernel_offset,
      const LocalScope** scope /* = nullptr */) {
    return flow_graph_builder_->EnterScope(kernel_offset, scope);
  }
  
  Fragment StreamingFlowGraphBuilder::ExitScope(intptr_t kernel_offset) {
    return flow_graph_builder_->ExitScope(kernel_offset);
  }
  
  TestFragment StreamingFlowGraphBuilder::TranslateConditionForControl() {
    // Skip all negations and go directly to the expression.
    bool negate = false;
    while (PeekTag() == kNot) {
      SkipBytes(1);
      negate = !negate;
    }
  
    TestFragment result;
    if (PeekTag() == kLogicalExpression) {
      // Handle '&&' and '||' operators specially to implement short circuit
      // evaluation.
      SkipBytes(1);  // tag.
  
      TestFragment left = TranslateConditionForControl();
      LogicalOperator op = static_cast<LogicalOperator>(ReadByte());
      TestFragment right = TranslateConditionForControl();
  
      result.entry = left.entry;
      if (op == kAnd) {
        left.CreateTrueSuccessor(flow_graph_builder_)->LinkTo(right.entry);
        result.true_successor_addresses = right.true_successor_addresses;
        result.false_successor_addresses = left.false_successor_addresses;
        result.false_successor_addresses->AddArray(
            *right.false_successor_addresses);
      } else {
        ASSERT(op == kOr);
        left.CreateFalseSuccessor(flow_graph_builder_)->LinkTo(right.entry);
        result.true_successor_addresses = left.true_successor_addresses;
        result.true_successor_addresses->AddArray(
            *right.true_successor_addresses);
        result.false_successor_addresses = right.false_successor_addresses;
      }
    } else {
      // Other expressions.
      TokenPosition position = TokenPosition::kNoSource;
      Fragment instructions = BuildExpression(&position);  // read expression.
  
      // Check if the top of the stack is already a StrictCompare that
      // can be merged with a branch. Otherwise compare TOS with
      // true value and branch on that.
      BranchInstr* branch;
      if (stack()->definition()->IsStrictCompare() &&
          stack()->definition() == instructions.current) {
        StrictCompareInstr* compare = Pop()->definition()->AsStrictCompare();
        if (negate) {
          compare->NegateComparison();
          negate = false;
        }
        branch =
            new (Z) BranchInstr(compare, flow_graph_builder_->GetNextDeoptId());
        branch->comparison()->ClearTempIndex();
        ASSERT(instructions.current->previous() != nullptr);
        instructions.current = instructions.current->previous();
      } else {
        instructions += CheckBoolean(position);
        instructions += Constant(Bool::True());
        Value* right_value = Pop();
        Value* left_value = Pop();
        StrictCompareInstr* compare = new (Z) StrictCompareInstr(
            TokenPosition::kNoSource,
            negate ? Token::kNE_STRICT : Token::kEQ_STRICT, left_value,
            right_value, false, flow_graph_builder_->GetNextDeoptId());
        branch =
            new (Z) BranchInstr(compare, flow_graph_builder_->GetNextDeoptId());
        negate = false;
      }
      instructions <<= branch;
  
      result = TestFragment(instructions.entry, branch);
    }
  
    return result.Negate(negate);
  }
  
  const TypeArguments& StreamingFlowGraphBuilder::BuildTypeArguments() {
    ReadUInt();                               // read arguments count.
    intptr_t type_count = ReadListLength();   // read type count.
    return T.BuildTypeArguments(type_count);  // read types.
  }
  
  Fragment StreamingFlowGraphBuilder::BuildArguments(Array* argument_names,
                                                     intptr_t* argument_count,
                                                     intptr_t* positional_count) {
    intptr_t dummy;
    if (argument_count == NULL) argument_count = &dummy;
    *argument_count = ReadUInt();  // read arguments count.
  
    // List of types.
    SkipListOfDartTypes();  // read list of types.
  
    {
      AlternativeReadingScope _(&reader_);
      if (positional_count == NULL) positional_count = &dummy;
      *positional_count = ReadListLength();  // read length of expression list
    }
    return BuildArgumentsFromActualArguments(argument_names);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildArgumentsFromActualArguments(
      Array* argument_names) {
    Fragment instructions;
  
    // List of positional.
    intptr_t list_length = ReadListLength();  // read list length.
    for (intptr_t i = 0; i < list_length; ++i) {
      instructions += BuildExpression();  // read ith expression.
    }
  
    // List of named.
    list_length = ReadListLength();  // read list length.
    if (argument_names != NULL && list_length > 0) {
      *argument_names = Array::New(list_length, Heap::kOld);
    }
    for (intptr_t i = 0; i < list_length; ++i) {
      String& name =
          H.DartSymbolObfuscate(ReadStringReference());  // read ith name index.
      instructions += BuildExpression();                 // read ith expression.
      if (argument_names != NULL) {
        argument_names->SetAt(i, name);
      }
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildInvalidExpression(
      TokenPosition* position) {
    // The frontend will take care of emitting normal errors (like
    // [NoSuchMethodError]s) and only emit [InvalidExpression]s in very special
    // situations (e.g. an invalid annotation).
    TokenPosition pos = ReadPosition();
    if (position != NULL) *position = pos;
    const String& message = H.DartString(ReadStringReference());
    // Invalid expression message has pointer to the source code, no need to
    // report it twice.
    H.ReportError(script(), TokenPosition::kNoSource, "%s", message.ToCString());
    return Fragment();
  }
  
  Fragment StreamingFlowGraphBuilder::BuildVariableGet(TokenPosition* position) {
    const TokenPosition pos = ReadPosition();
    if (position != nullptr) *position = pos;
    intptr_t variable_kernel_position = ReadUInt();  // read kernel position.
    ReadUInt();              // read relative variable index.
    SkipOptionalDartType();  // read promoted type.
    return BuildVariableGetImpl(variable_kernel_position, pos);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildVariableGet(uint8_t payload,
                                                       TokenPosition* position) {
    const TokenPosition pos = ReadPosition();
    if (position != nullptr) *position = pos;
    intptr_t variable_kernel_position = ReadUInt();  // read kernel position.
    return BuildVariableGetImpl(variable_kernel_position, pos);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildVariableGetImpl(
      intptr_t variable_kernel_position,
      TokenPosition position) {
    LocalVariable* variable = LookupVariable(variable_kernel_position);
    if (!variable->is_late()) {
      return LoadLocal(variable);
    }
  
    // Late variable, so check whether it has been initialized already.
    Fragment instructions = LoadLocal(variable);
    TargetEntryInstr *is_uninitialized, *is_initialized;
    instructions += Constant(Object::sentinel());
    instructions += flow_graph_builder_->BranchIfStrictEqual(&is_uninitialized,
                                                             &is_initialized);
    JoinEntryInstr* join = BuildJoinEntry();
  
    {
      AlternativeReadingScope alt(&reader_, variable->late_init_offset());
      const bool has_initializer = (ReadTag() != kNothing);
  
      if (has_initializer) {
        // If the variable isn't initialized, call the initializer and set it.
        Fragment initialize(is_uninitialized);
        initialize += BuildExpression();
        initialize += StoreLocal(position, variable);
        initialize += Drop();
        initialize += Goto(join);
      } else {
        // The variable has no initializer, so throw a LateInitializationError.
        Fragment initialize(is_uninitialized);
        initialize += flow_graph_builder_->ThrowLateInitializationError(
            position, "_throwLocalNotInitialized", variable->name());
        initialize += Goto(join);
      }
    }
  
    {
      // Already initialized, so there's nothing to do.
      Fragment already_initialized(is_initialized);
      already_initialized += Goto(join);
    }
  
    Fragment done = Fragment(instructions.entry, join);
    done += LoadLocal(variable);
    return done;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildVariableSet(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    intptr_t variable_kernel_position = ReadUInt();  // read kernel position.
    ReadUInt();  // read relative variable index.
    return BuildVariableSetImpl(position, variable_kernel_position);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildVariableSet(uint8_t payload,
                                                       TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    intptr_t variable_kernel_position = ReadUInt();  // read kernel position.
    return BuildVariableSetImpl(position, variable_kernel_position);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildVariableSetImpl(
      TokenPosition position,
      intptr_t variable_kernel_position) {
    Fragment instructions = BuildExpression();  // read expression.
    if (NeedsDebugStepCheck(stack(), position)) {
      instructions = DebugStepCheck(position) + instructions;
    }
  
    LocalVariable* variable = LookupVariable(variable_kernel_position);
    if (variable->is_late() && variable->is_final()) {
      // Late final variable, so check whether it has been initialized.
      LocalVariable* expr_temp = MakeTemporary();
      instructions += LoadLocal(variable);
      TargetEntryInstr *is_uninitialized, *is_initialized;
      instructions += Constant(Object::sentinel());
      instructions += flow_graph_builder_->BranchIfStrictEqual(&is_uninitialized,
                                                               &is_initialized);
      JoinEntryInstr* join = BuildJoinEntry();
  
      {
        // The variable is uninitialized, so store the expression value.
        Fragment initialize(is_uninitialized);
        initialize += LoadLocal(expr_temp);
        initialize += StoreLocal(position, variable);
        initialize += Drop();
        initialize += Goto(join);
      }
  
      {
        // Already initialized, so throw a LateInitializationError.
        Fragment already_initialized(is_initialized);
        already_initialized += flow_graph_builder_->ThrowLateInitializationError(
            position, "_throwLocalAlreadyInitialized", variable->name());
        already_initialized += Goto(join);
      }
  
      instructions = Fragment(instructions.entry, join);
    } else {
      instructions += StoreLocal(position, variable);
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildPropertyGet(TokenPosition* p) {
    const intptr_t offset = ReaderOffset() - 1;     // Include the tag.
    const TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const DirectCallMetadata direct_call =
        direct_call_metadata_helper_.GetDirectTargetForPropertyGet(offset);
    const InferredTypeMetadata result_type =
        inferred_type_metadata_helper_.GetInferredType(offset);
  
    Fragment instructions = BuildExpression();  // read receiver.
  
    LocalVariable* receiver = NULL;
    if (direct_call.check_receiver_for_null_) {
      // Duplicate receiver for CheckNull before it is consumed by PushArgument.
      receiver = MakeTemporary();
      instructions += LoadLocal(receiver);
    }
  
    const String& getter_name = ReadNameAsGetterName();  // read name.
  
    const Function* interface_target = &Function::null_function();
    const Function* tearoff_interface_target = &Function::null_function();
    const NameIndex itarget_name =
        ReadInterfaceMemberNameReference();  // read interface_target_reference.
    if (!H.IsRoot(itarget_name) &&
        (H.IsGetter(itarget_name) || H.IsField(itarget_name))) {
      interface_target = &Function::ZoneHandle(
          Z,
          H.LookupMethodByMember(itarget_name, H.DartGetterName(itarget_name)));
      ASSERT(getter_name.raw() == interface_target->name());
    } else if (!H.IsRoot(itarget_name) && H.IsMethod(itarget_name)) {
      tearoff_interface_target = &Function::ZoneHandle(
          Z,
          H.LookupMethodByMember(itarget_name, H.DartMethodName(itarget_name)));
    }
  
    if (direct_call.check_receiver_for_null_) {
      instructions += CheckNull(position, receiver, getter_name);
    }
  
    const String* mangled_name = &getter_name;
    const Function* direct_call_target = &direct_call.target_;
    if (H.IsRoot(itarget_name)) {
      mangled_name = &String::ZoneHandle(
          Z, Function::CreateDynamicInvocationForwarderName(getter_name));
      if (!direct_call_target->IsNull()) {
        direct_call_target = &Function::ZoneHandle(
            direct_call.target_.GetDynamicInvocationForwarder(*mangled_name));
      }
    }
  
    if (!direct_call_target->IsNull()) {
      ASSERT(CompilerState::Current().is_aot());
      instructions +=
          StaticCall(position, *direct_call_target, 1, Array::null_array(),
                     ICData::kNoRebind, &result_type);
    } else {
      const intptr_t kTypeArgsLen = 0;
      const intptr_t kNumArgsChecked = 1;
      instructions +=
          InstanceCall(position, *mangled_name, Token::kGET, kTypeArgsLen, 1,
                       Array::null_array(), kNumArgsChecked, *interface_target,
                       *tearoff_interface_target, &result_type);
    }
  
    if (direct_call.check_receiver_for_null_) {
      instructions += DropTempsPreserveTop(1);  // Drop receiver, preserve result.
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildPropertySet(TokenPosition* p) {
    const intptr_t offset = ReaderOffset() - 1;  // Include the tag.
  
    const DirectCallMetadata direct_call =
        direct_call_metadata_helper_.GetDirectTargetForPropertySet(offset);
    const CallSiteAttributesMetadata call_site_attributes =
        call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset);
    const InferredTypeMetadata inferred_type =
        inferred_type_metadata_helper_.GetInferredType(offset);
  
    // True if callee can skip argument type checks.
    bool is_unchecked_call = inferred_type.IsSkipCheck();
    if (call_site_attributes.receiver_type != nullptr &&
        call_site_attributes.receiver_type->HasTypeClass() &&
        !Class::Handle(call_site_attributes.receiver_type->type_class())
             .IsGeneric()) {
      is_unchecked_call = true;
    }
  
    Fragment instructions(MakeTemp());
    LocalVariable* variable = MakeTemporary();
  
    const TokenPosition position = ReadPosition();  // read position.
    if (p != nullptr) *p = position;
  
    if (PeekTag() == kThisExpression) {
      is_unchecked_call = true;
    }
    instructions += BuildExpression();  // read receiver.
  
    LocalVariable* receiver = nullptr;
    if (direct_call.check_receiver_for_null_) {
      // Duplicate receiver for CheckNull before it is consumed by PushArgument.
      receiver = MakeTemporary();
      instructions += LoadLocal(receiver);
    }
  
    const String& setter_name = ReadNameAsSetterName();  // read name.
  
    instructions += BuildExpression();  // read value.
    instructions += StoreLocal(TokenPosition::kNoSource, variable);
  
    const Function* interface_target = &Function::null_function();
    const NameIndex itarget_name =
        ReadInterfaceMemberNameReference();  // read interface_target_reference.
    if (!H.IsRoot(itarget_name)) {
      interface_target = &Function::ZoneHandle(
          Z,
          H.LookupMethodByMember(itarget_name, H.DartSetterName(itarget_name)));
      ASSERT(setter_name.raw() == interface_target->name());
    }
  
    if (direct_call.check_receiver_for_null_) {
      instructions += CheckNull(position, receiver, setter_name);
    }
  
    const String* mangled_name = &setter_name;
    const Function* direct_call_target = &direct_call.target_;
    if (H.IsRoot(itarget_name)) {
      mangled_name = &String::ZoneHandle(
          Z, Function::CreateDynamicInvocationForwarderName(setter_name));
      if (!direct_call_target->IsNull()) {
        direct_call_target = &Function::ZoneHandle(
            direct_call.target_.GetDynamicInvocationForwarder(*mangled_name));
      }
    }
  
    if (!direct_call_target->IsNull()) {
      ASSERT(CompilerState::Current().is_aot());
      instructions +=
          StaticCall(position, *direct_call_target, 2, Array::null_array(),
                     ICData::kNoRebind, /*result_type=*/nullptr,
                     /*type_args_count=*/0,
                     /*use_unchecked_entry=*/is_unchecked_call);
    } else {
      const intptr_t kTypeArgsLen = 0;
      const intptr_t kNumArgsChecked = 1;
  
      instructions += InstanceCall(
          position, *mangled_name, Token::kSET, kTypeArgsLen, 2,
          Array::null_array(), kNumArgsChecked, *interface_target,
          Function::null_function(),
          /*result_type=*/nullptr,
          /*use_unchecked_entry=*/is_unchecked_call, &call_site_attributes);
    }
  
    instructions += Drop();  // Drop result of the setter invocation.
  
    if (direct_call.check_receiver_for_null_) {
      instructions += Drop();  // Drop receiver.
    }
  
    return instructions;
  }
  
  static Function& GetNoSuchMethodOrDie(Thread* thread,
                                        Zone* zone,
                                        const Class& klass) {
    Function& nsm_function = Function::Handle(zone);
    Class& iterate_klass = Class::Handle(zone, klass.raw());
    if (!iterate_klass.IsNull() &&
        iterate_klass.EnsureIsFinalized(thread) == Error::null()) {
      while (!iterate_klass.IsNull()) {
        nsm_function = Resolver::ResolveDynamicFunction(zone, iterate_klass,
                                                        Symbols::NoSuchMethod());
        if (!nsm_function.IsNull() && nsm_function.NumParameters() == 2 &&
            nsm_function.NumTypeParameters() == 0) {
          break;
        }
        iterate_klass = iterate_klass.SuperClass();
      }
    }
    // We are guaranteed to find noSuchMethod of class Object.
    ASSERT(!nsm_function.IsNull());
  
    return nsm_function;
  }
  
  // Note, that this will always mark `super` flag to true.
  Fragment StreamingFlowGraphBuilder::BuildAllocateInvocationMirrorCall(
      TokenPosition position,
      const String& name,
      intptr_t num_type_arguments,
      intptr_t num_arguments,
      const Array& argument_names,
      LocalVariable* actuals_array,
      Fragment build_rest_of_actuals) {
    Fragment instructions;
  
    // Populate array containing the actual arguments. Just add [this] here.
    instructions += LoadLocal(actuals_array);                      // array
    instructions += IntConstant(num_type_arguments == 0 ? 0 : 1);  // index
    instructions += LoadLocal(parsed_function()->receiver_var());  // receiver
    instructions += StoreIndexed(kArrayCid);
    instructions += build_rest_of_actuals;
  
    // First argument is receiver.
    instructions += LoadLocal(parsed_function()->receiver_var());
  
    // Push the arguments for allocating the invocation mirror:
    //   - the name.
    instructions += Constant(String::ZoneHandle(Z, name.raw()));
  
    //   - the arguments descriptor.
    const Array& args_descriptor =
        Array::Handle(Z, ArgumentsDescriptor::NewBoxed(
                             num_type_arguments, num_arguments, argument_names));
    instructions += Constant(Array::ZoneHandle(Z, args_descriptor.raw()));
  
    //   - an array containing the actual arguments.
    instructions += LoadLocal(actuals_array);
  
    //   - [true] indicating this is a `super` NoSuchMethod.
    instructions += Constant(Bool::True());
  
    const Class& mirror_class =
        Class::Handle(Z, Library::LookupCoreClass(Symbols::InvocationMirror()));
    ASSERT(!mirror_class.IsNull());
    const auto& error = mirror_class.EnsureIsFinalized(thread());
    ASSERT(error == Error::null());
    const Function& allocation_function = Function::ZoneHandle(
        Z, mirror_class.LookupStaticFunction(
               Library::PrivateCoreLibName(Symbols::AllocateInvocationMirror())));
    ASSERT(!allocation_function.IsNull());
    instructions += StaticCall(position, allocation_function,
                               /* argument_count = */ 4, ICData::kStatic);
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildSuperPropertyGet(TokenPosition* p) {
    const intptr_t offset = ReaderOffset() - 1;     // Include the tag.
    const TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const InferredTypeMetadata result_type =
        inferred_type_metadata_helper_.GetInferredType(offset);
  
    Class& klass = GetSuperOrDie();
  
    StringIndex name_index = ReadStringReference();  // read name index.
    NameIndex library_reference =
        ((H.StringSize(name_index) >= 1) && H.CharacterAt(name_index, 0) == '_')
            ? ReadCanonicalNameReference()  // read library index.
            : NameIndex();
    const String& getter_name = H.DartGetterName(library_reference, name_index);
    const String& method_name = H.DartMethodName(library_reference, name_index);
  
    SkipInterfaceMemberNameReference();  // skip target_reference.
  
    // Search the superclass chain for the selector looking for either getter or
    // method.
    Function& function = Function::Handle(Z);
    if (!klass.IsNull() && klass.EnsureIsFinalized(thread()) == Error::null()) {
      while (!klass.IsNull()) {
        function = Resolver::ResolveDynamicFunction(Z, klass, method_name);
        if (!function.IsNull()) {
          Function& target =
              Function::ZoneHandle(Z, function.ImplicitClosureFunction());
          ASSERT(!target.IsNull());
          // Generate inline code for allocation closure object with context
          // which captures `this`.
          return BuildImplicitClosureCreation(target);
        }
        function = Resolver::ResolveDynamicFunction(Z, klass, getter_name);
        if (!function.IsNull()) break;
        klass = klass.SuperClass();
      }
    }
  
    Fragment instructions;
    if (klass.IsNull()) {
      instructions +=
          Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null()));
      instructions += IntConstant(1);  // array size
      instructions += CreateArray();
      LocalVariable* actuals_array = MakeTemporary();
  
      Class& parent_klass = GetSuperOrDie();
  
      instructions += BuildAllocateInvocationMirrorCall(
          position, getter_name,
          /* num_type_arguments = */ 0,
          /* num_arguments = */ 1,
          /* argument_names = */ Object::empty_array(), actuals_array,
          /* build_rest_of_actuals = */ Fragment());
  
      Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, parent_klass);
      instructions +=
          StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()),
                     /* argument_count = */ 2, ICData::kNSMDispatch);
      instructions += DropTempsPreserveTop(1);  // Drop array
    } else {
      ASSERT(!klass.IsNull());
      ASSERT(!function.IsNull());
  
      instructions += LoadLocal(parsed_function()->receiver_var());
  
      instructions +=
          StaticCall(position, Function::ZoneHandle(Z, function.raw()),
                     /* argument_count = */ 1, Array::null_array(),
                     ICData::kSuper, &result_type);
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildSuperPropertySet(TokenPosition* p) {
    const TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    Class& klass = GetSuperOrDie();
  
    const String& setter_name = ReadNameAsSetterName();  // read name.
  
    Function& function = Function::Handle(Z);
    if (klass.EnsureIsFinalized(thread()) == Error::null()) {
      function = Resolver::ResolveDynamicFunction(Z, klass, setter_name);
    }
  
    Fragment instructions(MakeTemp());
    LocalVariable* value = MakeTemporary();  // this holds RHS value
  
    if (function.IsNull()) {
      instructions +=
          Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null()));
      instructions += IntConstant(2);  // array size
      instructions += CreateArray();
      LocalVariable* actuals_array = MakeTemporary();
  
      Fragment build_rest_of_actuals;
      build_rest_of_actuals += LoadLocal(actuals_array);  // array
      build_rest_of_actuals += IntConstant(1);            // index
      build_rest_of_actuals += BuildExpression();         // value.
      build_rest_of_actuals += StoreLocal(position, value);
      build_rest_of_actuals += StoreIndexed(kArrayCid);
  
      instructions += BuildAllocateInvocationMirrorCall(
          position, setter_name, /* num_type_arguments = */ 0,
          /* num_arguments = */ 2,
          /* argument_names = */ Object::empty_array(), actuals_array,
          build_rest_of_actuals);
  
      SkipInterfaceMemberNameReference();  // skip target_reference.
  
      Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, klass);
      instructions +=
          StaticCall(position, Function::ZoneHandle(Z, nsm_function.raw()),
                     /* argument_count = */ 2, ICData::kNSMDispatch);
      instructions += Drop();  // Drop result of NoSuchMethod invocation
      instructions += Drop();  // Drop array
    } else {
      // receiver
      instructions += LoadLocal(parsed_function()->receiver_var());
  
      instructions += BuildExpression();  // read value.
      instructions += StoreLocal(position, value);
  
      SkipInterfaceMemberNameReference();  // skip target_reference.
  
      instructions += StaticCall(
          position, Function::ZoneHandle(Z, function.raw()),
          /* argument_count = */ 2, Array::null_array(), ICData::kSuper,
          /*result_type=*/nullptr, /*type_args_len=*/0,
          /*use_unchecked_entry=*/true);
      instructions += Drop();  // Drop result of the setter invocation.
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildStaticGet(TokenPosition* p) {
    ASSERT(Error::Handle(Z, H.thread()->sticky_error()).IsNull());
    const intptr_t offset = ReaderOffset() - 1;  // Include the tag.
  
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const InferredTypeMetadata result_type =
        inferred_type_metadata_helper_.GetInferredType(offset);
  
    NameIndex target = ReadCanonicalNameReference();  // read target_reference.
  
    if (H.IsField(target)) {
      const Field& field =
          Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
      if (field.is_const()) {
        // Since the CFE inlines all references to const variables and fields,
        // it never emits a StaticGet of a const field.
        // This situation only arises because of the static const fields in
        // the ClassID class, which are generated internally in the VM
        // during loading. See also Class::InjectCIDFields.
        ASSERT(Class::Handle(field.Owner()).library() ==
                   Library::InternalLibrary() &&
               Class::Handle(field.Owner()).Name() == Symbols::ClassID().raw());
        return Constant(Instance::ZoneHandle(Z, field.StaticValue()));
      } else {
        const Class& owner = Class::Handle(Z, field.Owner());
        const String& getter_name = H.DartGetterName(target);
        const Function& getter =
            Function::ZoneHandle(Z, owner.LookupStaticFunction(getter_name));
        if (!getter.IsNull() && field.NeedsGetter()) {
          return StaticCall(position, getter, 0, Array::null_array(),
                            ICData::kStatic, &result_type);
        } else {
          if (result_type.IsConstant()) {
            return Constant(result_type.constant_value);
          }
          return LoadStaticField(field, /*calls_initializer=*/false);
        }
      }
    } else {
      const Function& function =
          Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
  
      if (H.IsGetter(target)) {
        return StaticCall(position, function, 0, Array::null_array(),
                          ICData::kStatic, &result_type);
      } else if (H.IsMethod(target)) {
        const auto& closure_function =
            Function::Handle(Z, function.ImplicitClosureFunction());
        const auto& static_closure =
            Instance::Handle(Z, closure_function.ImplicitStaticClosure());
        return Constant(Instance::ZoneHandle(Z, H.Canonicalize(static_closure)));
      } else {
        UNIMPLEMENTED();
      }
    }
  
    return Fragment();
  }
  
  Fragment StreamingFlowGraphBuilder::BuildStaticSet(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    NameIndex target = ReadCanonicalNameReference();  // read target_reference.
  
    if (H.IsField(target)) {
      const Field& field =
          Field::ZoneHandle(Z, H.LookupFieldByKernelField(target));
      const Class& owner = Class::Handle(Z, field.Owner());
      const String& setter_name = H.DartSetterName(target);
      const Function& setter =
          Function::ZoneHandle(Z, owner.LookupStaticFunction(setter_name));
      Fragment instructions = BuildExpression();  // read expression.
      if (NeedsDebugStepCheck(stack(), position)) {
        instructions = DebugStepCheck(position) + instructions;
      }
      LocalVariable* variable = MakeTemporary();
      instructions += LoadLocal(variable);
      if (!setter.IsNull() && field.NeedsSetter()) {
        instructions += StaticCall(position, setter, 1, ICData::kStatic);
        instructions += Drop();
      } else {
        instructions += StoreStaticField(position, field);
      }
      return instructions;
    } else {
      ASSERT(H.IsProcedure(target));
  
      // Evaluate the expression on the right hand side.
      Fragment instructions = BuildExpression();  // read expression.
      LocalVariable* variable = MakeTemporary();
  
      // Prepare argument.
      instructions += LoadLocal(variable);
  
      // Invoke the setter function.
      const Function& function =
          Function::ZoneHandle(Z, H.LookupStaticMethodByKernelProcedure(target));
      instructions += StaticCall(position, function, 1, ICData::kStatic);
  
      // Drop the unused result & leave the stored value on the stack.
      return instructions + Drop();
    }
  }
  
  Fragment StreamingFlowGraphBuilder::BuildMethodInvocation(TokenPosition* p) {
    const intptr_t offset = ReaderOffset() - 1;     // Include the tag.
    ReadFlags();                                    // read flags.
    const TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const DirectCallMetadata direct_call =
        direct_call_metadata_helper_.GetDirectTargetForMethodInvocation(offset);
    const InferredTypeMetadata result_type =
        inferred_type_metadata_helper_.GetInferredType(offset);
    const CallSiteAttributesMetadata call_site_attributes =
        call_site_attributes_metadata_helper_.GetCallSiteAttributes(offset);
  
    const Tag receiver_tag = PeekTag();  // peek tag for receiver.
  
    bool is_unchecked_closure_call = false;
    bool is_unchecked_call = result_type.IsSkipCheck();
    if (call_site_attributes.receiver_type != nullptr) {
      if (call_site_attributes.receiver_type->IsFunctionType()) {
        AlternativeReadingScope alt(&reader_);
        SkipExpression();  // skip receiver
        is_unchecked_closure_call =
            ReadNameAsMethodName().Equals(Symbols::Call());
      } else if (call_site_attributes.receiver_type->HasTypeClass() &&
                 !call_site_attributes.receiver_type->IsDynamicType() &&
                 !Class::Handle(call_site_attributes.receiver_type->type_class())
                      .IsGeneric()) {
        is_unchecked_call = true;
      }
    }
  
    Fragment instructions;
  
    intptr_t type_args_len = 0;
    LocalVariable* type_arguments_temp = NULL;
    {
      AlternativeReadingScope alt(&reader_);
      SkipExpression();                         // skip receiver
      SkipName();                               // skip method name
      ReadUInt();                               // read argument count.
      intptr_t list_length = ReadListLength();  // read types list length.
      if (list_length > 0) {
        const TypeArguments& type_arguments =
            T.BuildTypeArguments(list_length);  // read types.
        instructions += TranslateInstantiatedTypeArguments(type_arguments);
        if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) {
          // Don't yet push type arguments if we need to check receiver for null.
          // In this case receiver will be duplicated so instead of pushing
          // type arguments here we need to push it between receiver_temp
          // and actual receiver. See the code below.
          type_arguments_temp = MakeTemporary();
        }
      }
      type_args_len = list_length;
    }
  
    // Take note of whether the invocation is against the receiver of the current
    // function: in this case, we may skip some type checks in the callee.
    if (PeekTag() == kThisExpression) {
      is_unchecked_call = true;
    }
    instructions += BuildExpression();  // read receiver.
  
    const String& name = ReadNameAsMethodName();  // read name.
    const Token::Kind token_kind =
        MethodTokenRecognizer::RecognizeTokenKind(name);
  
    // Detect comparison with null.
    if ((token_kind == Token::kEQ || token_kind == Token::kNE) &&
        PeekArgumentsCount() == 1 &&
        (receiver_tag == kNullLiteral ||
         PeekArgumentsFirstPositionalTag() == kNullLiteral)) {
      ASSERT(type_args_len == 0);
      // "==" or "!=" with null on either side.
      instructions +=
          BuildArguments(NULL /* named */, NULL /* arg count */,
                         NULL /* positional arg count */);  // read arguments.
      SkipInterfaceMemberNameReference();  // read interface_target_reference.
      Token::Kind strict_cmp_kind =
          token_kind == Token::kEQ ? Token::kEQ_STRICT : Token::kNE_STRICT;
      return instructions +
             StrictCompare(position, strict_cmp_kind, /*number_check = */ true);
    }
  
    LocalVariable* receiver_temp = NULL;
    if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) {
      // Duplicate receiver for CheckNull before it is consumed by PushArgument.
      receiver_temp = MakeTemporary();
      if (type_arguments_temp != NULL) {
        // If call has type arguments then push them before pushing the receiver.
        // The stack will contain:
        //
        //   [type_arguments_temp][receiver_temp][type_arguments][receiver] ...
        //
        instructions += LoadLocal(type_arguments_temp);
      }
      instructions += LoadLocal(receiver_temp);
    }
  
    intptr_t argument_count;
    intptr_t positional_argument_count;
    Array& argument_names = Array::ZoneHandle(Z);
    instructions +=
        BuildArguments(&argument_names, &argument_count,
                       &positional_argument_count);  // read arguments.
    ++argument_count;                                // include receiver
  
    intptr_t checked_argument_count = 1;
    // If we have a special operation (e.g. +/-/==) we mark both arguments as
    // to be checked.
    if (token_kind != Token::kILLEGAL) {
      ASSERT(argument_count <= 2);
      checked_argument_count = argument_count;
    }
  
    const Function* interface_target = &Function::null_function();
    const NameIndex itarget_name =
        ReadInterfaceMemberNameReference();  // read interface_target_reference.
    // TODO(dartbug.com/34497): Once front-end desugars calls via
    // fields/getters, filtering of field and getter interface targets here
    // can be turned into assertions.
    if (!H.IsRoot(itarget_name) && !H.IsField(itarget_name) &&
        !H.IsGetter(itarget_name)) {
      interface_target = &Function::ZoneHandle(
          Z, H.LookupMethodByMember(itarget_name,
                                    H.DartProcedureName(itarget_name)));
      ASSERT(name.raw() == interface_target->name());
      ASSERT(!interface_target->IsGetterFunction());
    }
  
    // TODO(sjindel): Avoid the check for null on unchecked closure calls if TFA
    // allows.
    if (direct_call.check_receiver_for_null_ || is_unchecked_closure_call) {
      // Receiver temp is needed to load the function to call from the closure.
      instructions += CheckNull(position, receiver_temp, name,
                                /*clear_temp=*/!is_unchecked_closure_call);
    }
  
    const String* mangled_name = &name;
    // Do not mangle ==:
    //   * operator == takes an Object so its either not checked or checked
    //     at the entry because the parameter is marked covariant, neither of
    //     those cases require a dynamic invocation forwarder.
    const Function* direct_call_target = &direct_call.target_;
    if (H.IsRoot(itarget_name) &&
        (name.raw() != Symbols::EqualOperator().raw())) {
      mangled_name = &String::ZoneHandle(
          Z, Function::CreateDynamicInvocationForwarderName(name));
      if (!direct_call_target->IsNull()) {
        direct_call_target = &Function::ZoneHandle(
            direct_call_target->GetDynamicInvocationForwarder(*mangled_name));
      }
    }
  
    if (is_unchecked_closure_call) {
      // Lookup the function in the closure.
      instructions += LoadLocal(receiver_temp);
      instructions += LoadNativeField(Slot::Closure_function());
      if (parsed_function()->function().is_debuggable()) {
        ASSERT(!parsed_function()->function().is_native());
        instructions += DebugStepCheck(position);
      }
      instructions +=
          B->ClosureCall(position, type_args_len, argument_count, argument_names,
                         /*use_unchecked_entry=*/true);
    } else if (!direct_call_target->IsNull()) {
      // Even if TFA infers a concrete receiver type, the static type of the
      // call-site may still be dynamic and we need to call the dynamic invocation
      // forwarder to ensure type-checks are performed.
      ASSERT(CompilerState::Current().is_aot());
      instructions +=
          StaticCall(position, *direct_call_target, argument_count,
                     argument_names, ICData::kNoRebind, &result_type,
                     type_args_len, /*use_unchecked_entry=*/is_unchecked_call);
    } else {
      instructions +=
          InstanceCall(position, *mangled_name, token_kind, type_args_len,
                       argument_count, argument_names, checked_argument_count,
                       *interface_target, Function::null_function(), &result_type,
                       /*use_unchecked_entry=*/is_unchecked_call,
                       &call_site_attributes, result_type.ReceiverNotInt());
    }
  
    // Drop temporaries preserving result on the top of the stack.
    ASSERT((receiver_temp != NULL) || (type_arguments_temp == NULL));
    if (receiver_temp != NULL) {
      const intptr_t num_temps =
          (receiver_temp != NULL ? 1 : 0) + (type_arguments_temp != NULL ? 1 : 0);
      instructions += DropTempsPreserveTop(num_temps);
    }
  
    // Later optimization passes assume that result of a x.[]=(...) call is not
    // used. We must guarantee this invariant because violation will lead to an
    // illegal IL once we replace x.[]=(...) with a sequence that does not
    // actually produce any value. See http://dartbug.com/29135 for more details.
    if (name.raw() == Symbols::AssignIndexToken().raw()) {
      instructions += Drop();
      instructions += NullConstant();
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildSuperMethodInvocation(
      TokenPosition* p) {
    const intptr_t offset = ReaderOffset() - 1;     // Include the tag.
    const TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const InferredTypeMetadata result_type =
        inferred_type_metadata_helper_.GetInferredType(offset);
  
    intptr_t type_args_len = 0;
    {
      AlternativeReadingScope alt(&reader_);
      SkipName();                        // skip method name
      ReadUInt();                        // read argument count.
      type_args_len = ReadListLength();  // read types list length.
    }
  
    Class& klass = GetSuperOrDie();
  
    // Search the superclass chain for the selector.
    const String& method_name = ReadNameAsMethodName();  // read name.
  
    // Figure out selector signature.
    intptr_t argument_count;
    Array& argument_names = Array::Handle(Z);
    {
      AlternativeReadingScope alt(&reader_);
      argument_count = ReadUInt();
      SkipListOfDartTypes();
  
      SkipListOfExpressions();
      intptr_t named_list_length = ReadListLength();
      argument_names = Array::New(named_list_length, H.allocation_space());
      for (intptr_t i = 0; i < named_list_length; i++) {
        const String& arg_name = H.DartSymbolObfuscate(ReadStringReference());
        argument_names.SetAt(i, arg_name);
        SkipExpression();
      }
    }
  
    Function& function = FindMatchingFunction(
        klass, method_name, type_args_len,
        argument_count + 1 /* account for 'this' */, argument_names);
  
    if (function.IsNull()) {
      ReadUInt();  // argument count
      intptr_t type_list_length = ReadListLength();
  
      Fragment instructions;
      instructions +=
          Constant(TypeArguments::ZoneHandle(Z, TypeArguments::null()));
      instructions += IntConstant(argument_count + 1 /* this */ +
                                  (type_list_length == 0 ? 0 : 1));  // array size
      instructions += CreateArray();
      LocalVariable* actuals_array = MakeTemporary();
  
      // Call allocationInvocationMirror to get instance of Invocation.
      Fragment build_rest_of_actuals;
      intptr_t actuals_array_index = 0;
      if (type_list_length > 0) {
        const TypeArguments& type_arguments =
            T.BuildTypeArguments(type_list_length);
        build_rest_of_actuals += LoadLocal(actuals_array);
        build_rest_of_actuals += IntConstant(actuals_array_index);
        build_rest_of_actuals +=
            TranslateInstantiatedTypeArguments(type_arguments);
        build_rest_of_actuals += StoreIndexed(kArrayCid);
        ++actuals_array_index;
      }
  
      ++actuals_array_index;  // account for 'this'.
      // Read arguments
      intptr_t list_length = ReadListLength();
      intptr_t i = 0;
      while (i < list_length) {
        build_rest_of_actuals += LoadLocal(actuals_array);              // array
        build_rest_of_actuals += IntConstant(actuals_array_index + i);  // index
        build_rest_of_actuals += BuildExpression();                     // value.
        build_rest_of_actuals += StoreIndexed(kArrayCid);
        ++i;
      }
      // Read named arguments
      intptr_t named_list_length = ReadListLength();
      if (named_list_length > 0) {
        ASSERT(argument_count == list_length + named_list_length);
        while ((i - list_length) < named_list_length) {
          SkipStringReference();
          build_rest_of_actuals += LoadLocal(actuals_array);              // array
          build_rest_of_actuals += IntConstant(i + actuals_array_index);  // index
          build_rest_of_actuals += BuildExpression();  // value.
          build_rest_of_actuals += StoreIndexed(kArrayCid);
          ++i;
        }
      }
      instructions += BuildAllocateInvocationMirrorCall(
          position, method_name, type_list_length,
          /* num_arguments = */ argument_count + 1, argument_names, actuals_array,
          build_rest_of_actuals);
  
      SkipInterfaceMemberNameReference();  //  skip target_reference.
  
      Function& nsm_function = GetNoSuchMethodOrDie(thread(), Z, klass);
      instructions += StaticCall(TokenPosition::kNoSource,
                                 Function::ZoneHandle(Z, nsm_function.raw()),
                                 /* argument_count = */ 2, ICData::kNSMDispatch);
      instructions += DropTempsPreserveTop(1);  // Drop actuals_array temp.
      return instructions;
    } else {
      Fragment instructions;
  
      {
        AlternativeReadingScope alt(&reader_);
        ReadUInt();                               // read argument count.
        intptr_t list_length = ReadListLength();  // read types list length.
        if (list_length > 0) {
          const TypeArguments& type_arguments =
              T.BuildTypeArguments(list_length);  // read types.
          instructions += TranslateInstantiatedTypeArguments(type_arguments);
        }
      }
  
      // receiver
      instructions += LoadLocal(parsed_function()->receiver_var());
  
      Array& argument_names = Array::ZoneHandle(Z);
      intptr_t argument_count;
      instructions += BuildArguments(
          &argument_names, &argument_count,
          /* positional_argument_count = */ NULL);  // read arguments.
      ++argument_count;                             // include receiver
      SkipInterfaceMemberNameReference();           // interfaceTargetReference
      return instructions +
             StaticCall(position, Function::ZoneHandle(Z, function.raw()),
                        argument_count, argument_names, ICData::kSuper,
                        &result_type, type_args_len,
                        /*use_unchecked_entry_point=*/true);
    }
  }
  
  Fragment StreamingFlowGraphBuilder::BuildStaticInvocation(TokenPosition* p) {
    const intptr_t offset = ReaderOffset() - 1;  // Include the tag.
    TokenPosition position = ReadPosition();     // read position.
    if (p != NULL) *p = position;
  
    const InferredTypeMetadata result_type =
        inferred_type_metadata_helper_.GetInferredType(offset);
  
    NameIndex procedure_reference =
        ReadCanonicalNameReference();  // read procedure reference.
    intptr_t argument_count = PeekArgumentsCount();
    const Function& target = Function::ZoneHandle(
        Z, H.LookupStaticMethodByKernelProcedure(procedure_reference));
    const Class& klass = Class::ZoneHandle(Z, target.Owner());
    if (target.IsGenerativeConstructor() || target.IsFactory()) {
      // The VM requires a TypeArguments object as first parameter for
      // every factory constructor.
      ++argument_count;
    }
  
    const auto recognized_kind = target.recognized_kind();
    if (recognized_kind == MethodRecognizer::kFfiAsFunctionInternal) {
      return BuildFfiAsFunctionInternal();
    } else if (CompilerState::Current().is_aot() &&
               recognized_kind == MethodRecognizer::kFfiNativeCallbackFunction) {
      return BuildFfiNativeCallbackFunction();
    }
  
    Fragment instructions;
    LocalVariable* instance_variable = NULL;
  
    const bool special_case_unchecked_cast =
        klass.IsTopLevel() && (klass.library() == Library::InternalLibrary()) &&
        (target.name() == Symbols::UnsafeCast().raw());
  
    const bool special_case_identical =
        klass.IsTopLevel() && (klass.library() == Library::CoreLibrary()) &&
        (target.name() == Symbols::Identical().raw());
  
    const bool special_case =
        special_case_identical || special_case_unchecked_cast;
  
    // If we cross the Kernel -> VM core library boundary, a [StaticInvocation]
    // can appear, but the thing we're calling is not a static method, but a
    // factory constructor.
    // The `H.LookupStaticmethodByKernelProcedure` will potentially resolve to the
    // forwarded constructor.
    // In that case we'll make an instance and pass it as first argument.
    //
    // TODO(27590): Get rid of this after we're using core libraries compiled
    // into Kernel.
    intptr_t type_args_len = 0;
    if (target.IsGenerativeConstructor()) {
      if (klass.NumTypeArguments() > 0) {
        const TypeArguments& type_arguments =
            PeekArgumentsInstantiatedType(klass);
        instructions += TranslateInstantiatedTypeArguments(type_arguments);
        instructions += AllocateObject(position, klass, 1);
      } else {
        instructions += AllocateObject(position, klass, 0);
      }
  
      instance_variable = MakeTemporary();
  
      instructions += LoadLocal(instance_variable);
    } else if (target.IsFactory()) {
      // The VM requires currently a TypeArguments object as first parameter for
      // every factory constructor :-/ !
      //
      // TODO(27590): Get rid of this after we're using core libraries compiled
      // into Kernel.
      const TypeArguments& type_arguments = PeekArgumentsInstantiatedType(klass);
      instructions += TranslateInstantiatedTypeArguments(type_arguments);
    } else if (!special_case) {
      AlternativeReadingScope alt(&reader_);
      ReadUInt();                               // read argument count.
      intptr_t list_length = ReadListLength();  // read types list length.
      if (list_length > 0) {
        const TypeArguments& type_arguments =
            T.BuildTypeArguments(list_length);  // read types.
        instructions += TranslateInstantiatedTypeArguments(type_arguments);
      }
      type_args_len = list_length;
    }
  
    Array& argument_names = Array::ZoneHandle(Z);
    instructions +=
        BuildArguments(&argument_names, NULL /* arg count */,
                       NULL /* positional arg count */);  // read arguments.
    ASSERT(!special_case ||
           target.AreValidArguments(type_args_len, argument_count, argument_names,
                                    NULL));
  
    // Special case identical(x, y) call.
    // TODO(27590) consider moving this into the inliner and force inline it
    // there.
    if (special_case_identical) {
      ASSERT(argument_count == 2);
      instructions +=
          StrictCompare(position, Token::kEQ_STRICT, /*number_check=*/true);
    } else if (special_case_unchecked_cast) {
      // Simply do nothing: the result value is already pushed on the stack.
    } else {
      instructions += StaticCall(position, target, argument_count, argument_names,
                                 ICData::kStatic, &result_type, type_args_len);
      if (target.IsGenerativeConstructor()) {
        // Drop the result of the constructor call and leave [instance_variable]
        // on top-of-stack.
        instructions += Drop();
      }
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildConstructorInvocation(
      TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    NameIndex kernel_name =
        ReadCanonicalNameReference();  // read target_reference.
  
    Class& klass = Class::ZoneHandle(
        Z, H.LookupClassByKernelClass(H.EnclosingName(kernel_name)));
  
    Fragment instructions;
  
    if (klass.NumTypeArguments() > 0) {
      if (!klass.IsGeneric()) {
        Type& type = Type::ZoneHandle(Z, T.ReceiverType(klass).raw());
  
        // TODO(27590): Can we move this code into [ReceiverType]?
        type ^= ClassFinalizer::FinalizeType(type, ClassFinalizer::kFinalize);
        TypeArguments& canonicalized_type_arguments =
            TypeArguments::ZoneHandle(Z, type.arguments());
        canonicalized_type_arguments =
            canonicalized_type_arguments.Canonicalize(thread(), nullptr);
        instructions += Constant(canonicalized_type_arguments);
      } else {
        const TypeArguments& type_arguments =
            PeekArgumentsInstantiatedType(klass);
        instructions += TranslateInstantiatedTypeArguments(type_arguments);
      }
  
      instructions += AllocateObject(position, klass, 1);
    } else {
      instructions += AllocateObject(position, klass, 0);
    }
    LocalVariable* variable = MakeTemporary();
  
    instructions += LoadLocal(variable);
  
    Array& argument_names = Array::ZoneHandle(Z);
    intptr_t argument_count;
    instructions += BuildArguments(
        &argument_names, &argument_count,
        /* positional_argument_count = */ NULL);  // read arguments.
  
    const Function& target = Function::ZoneHandle(
        Z, H.LookupConstructorByKernelConstructor(klass, kernel_name));
    ++argument_count;
    instructions += StaticCall(position, target, argument_count, argument_names,
                               ICData::kStatic, /* result_type = */ NULL);
    return instructions + Drop();
  }
  
  Fragment StreamingFlowGraphBuilder::BuildNot(TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    TokenPosition operand_position = TokenPosition::kNoSource;
    Fragment instructions =
        BuildExpression(&operand_position);  // read expression.
    instructions += CheckBoolean(operand_position);
    instructions += BooleanNegate();
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildNullCheck(TokenPosition* p) {
    const TokenPosition position = ReadPosition();  // read position.
    if (p != nullptr) *p = position;
  
    TokenPosition operand_position = TokenPosition::kNoSource;
    Fragment instructions = BuildExpression(&operand_position);
    LocalVariable* expr_temp = MakeTemporary();
    instructions += CheckNull(position, expr_temp, String::null_string(),
                              /* clear_the_temp = */ false);
  
    return instructions;
  }
  
  // Translate the logical expression (lhs && rhs or lhs || rhs) in a context
  // where a value is required.
  //
  // Translation accumulates short-circuit exits from logical
  // subexpressions in the side_exits. These exits are expected to store
  // true and false into :expr_temp.
  //
  // The result of evaluating the last
  // expression in chain would be stored in :expr_temp directly to avoid
  // generating graph like:
  //
  //     if (v) :expr_temp = true; else :expr_temp = false;
  //
  // Outer negations are stripped and instead negation is passed down via
  // negated parameter.
  Fragment StreamingFlowGraphBuilder::TranslateLogicalExpressionForValue(
      bool negated,
      TestFragment* side_exits) {
    TestFragment left = TranslateConditionForControl().Negate(negated);
    LogicalOperator op = static_cast<LogicalOperator>(ReadByte());
    if (negated) {
      op = (op == kAnd) ? kOr : kAnd;
    }
  
    // Short circuit the control flow after the left hand side condition.
    if (op == kAnd) {
      side_exits->false_successor_addresses->AddArray(
          *left.false_successor_addresses);
    } else {
      side_exits->true_successor_addresses->AddArray(
          *left.true_successor_addresses);
    }
  
    // Skip negations of the right hand side.
    while (PeekTag() == kNot) {
      SkipBytes(1);
      negated = !negated;
    }
  
    Fragment right_value(op == kAnd
                             ? left.CreateTrueSuccessor(flow_graph_builder_)
                             : left.CreateFalseSuccessor(flow_graph_builder_));
  
    if (PeekTag() == kLogicalExpression) {
      SkipBytes(1);
      // Handle nested logical expressions specially to avoid materializing
      // intermediate boolean values.
      right_value += TranslateLogicalExpressionForValue(negated, side_exits);
    } else {
      // Arbitrary expression on the right hand side. Translate it for value.
      TokenPosition position = TokenPosition::kNoSource;
      right_value += BuildExpression(&position);  // read expression.
  
      // Check if the top of the stack is known to be a non-nullable boolean.
      // Note that in strong mode we know that any value that reaches here
      // is at least a nullable boolean - so there is no need to compare
      // with true like in Dart 1.
      Definition* top = stack()->definition();
      const bool is_bool = top->IsStrictCompare() || top->IsBooleanNegate();
      if (!is_bool) {
        right_value += CheckBoolean(position);
      }
      if (negated) {
        right_value += BooleanNegate();
      }
      right_value += StoreLocal(TokenPosition::kNoSource,
                                parsed_function()->expression_temp_var());
      right_value += Drop();
    }
  
    return Fragment(left.entry, right_value.current);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildLogicalExpression(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    TestFragment exits;
    exits.true_successor_addresses = new TestFragment::SuccessorAddressArray(2);
    exits.false_successor_addresses = new TestFragment::SuccessorAddressArray(2);
  
    JoinEntryInstr* join = BuildJoinEntry();
    Fragment instructions =
        TranslateLogicalExpressionForValue(/*negated=*/false, &exits);
    instructions += Goto(join);
  
    // Generate :expr_temp = true if needed and connect it to true side-exits.
    if (!exits.true_successor_addresses->is_empty()) {
      Fragment constant_fragment(exits.CreateTrueSuccessor(flow_graph_builder_));
      constant_fragment += Constant(Bool::Get(true));
      constant_fragment += StoreLocal(TokenPosition::kNoSource,
                                      parsed_function()->expression_temp_var());
      constant_fragment += Drop();
      constant_fragment += Goto(join);
    }
  
    // Generate :expr_temp = false if needed and connect it to false side-exits.
    if (!exits.false_successor_addresses->is_empty()) {
      Fragment constant_fragment(exits.CreateFalseSuccessor(flow_graph_builder_));
      constant_fragment += Constant(Bool::Get(false));
      constant_fragment += StoreLocal(TokenPosition::kNoSource,
                                      parsed_function()->expression_temp_var());
      constant_fragment += Drop();
      constant_fragment += Goto(join);
    }
  
    return Fragment(instructions.entry, join) +
           LoadLocal(parsed_function()->expression_temp_var());
  }
  
  Fragment StreamingFlowGraphBuilder::BuildConditionalExpression(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    TestFragment condition = TranslateConditionForControl();  // read condition.
  
    Value* top = stack();
    Fragment then_fragment(condition.CreateTrueSuccessor(flow_graph_builder_));
    then_fragment += BuildExpression();  // read then.
    then_fragment += StoreLocal(TokenPosition::kNoSource,
                                parsed_function()->expression_temp_var());
    then_fragment += Drop();
    ASSERT(stack() == top);
  
    Fragment otherwise_fragment(
        condition.CreateFalseSuccessor(flow_graph_builder_));
    otherwise_fragment += BuildExpression();  // read otherwise.
    otherwise_fragment += StoreLocal(TokenPosition::kNoSource,
                                     parsed_function()->expression_temp_var());
    otherwise_fragment += Drop();
    ASSERT(stack() == top);
  
    JoinEntryInstr* join = BuildJoinEntry();
    then_fragment += Goto(join);
    otherwise_fragment += Goto(join);
  
    SkipOptionalDartType();  // read unused static type.
  
    return Fragment(condition.entry, join) +
           LoadLocal(parsed_function()->expression_temp_var());
  }
  
  void StreamingFlowGraphBuilder::FlattenStringConcatenation(
      PiecesCollector* collector) {
    const auto length = ReadListLength();
    for (intptr_t i = 0; i < length; ++i) {
      const auto offset = reader_.offset();
      switch (PeekTag()) {
        case kStringLiteral: {
          ReadTag();
          const String& s = H.DartSymbolPlain(ReadStringReference());
          // Skip empty strings.
          if (!s.Equals("")) {
            collector->Add({-1, &s});
          }
          break;
        }
        case kStringConcatenation: {
          // Flatten by hoisting nested expressions up into the outer concat.
          ReadTag();
          ReadPosition();
          FlattenStringConcatenation(collector);
          break;
        }
        default: {
          collector->Add({offset, nullptr});
          SkipExpression();
        }
      }
    }
  }
  
  Fragment StreamingFlowGraphBuilder::BuildStringConcatenation(TokenPosition* p) {
    TokenPosition position = ReadPosition();
    if (p != nullptr) {
      *p = position;
    }
  
    // Collect and flatten all pieces of this and any nested StringConcats.
    // The result is a single sequence of pieces, potentially flattened to
    // a single String.
    // The collector will hold concatenated strings and Reader offsets of
    // non-string pieces.
    PiecesCollector collector(Z, &H);
    FlattenStringConcatenation(&collector);
    collector.FlushRun();
  
    if (collector.pieces.length() == 1) {
      // No need to Interp. a single string, so return string as a Constant:
      if (collector.pieces[0].literal != nullptr) {
        return Constant(*collector.pieces[0].literal);
      }
      // A single non-string piece is handle by StringInterpolateSingle:
      AlternativeReadingScope scope(&reader_, collector.pieces[0].offset);
      Fragment instructions;
      instructions += BuildExpression();
      instructions += StringInterpolateSingle(position);
      return instructions;
    }
  
    Fragment instructions;
    instructions += Constant(TypeArguments::ZoneHandle(Z));
    instructions += IntConstant(collector.pieces.length());
    instructions += CreateArray();
    LocalVariable* array = MakeTemporary();
    for (intptr_t i = 0; i < collector.pieces.length(); ++i) {
      // All pieces are now either a concat'd string or an expression we can
      // read at a given offset.
      if (collector.pieces[i].literal != nullptr) {
        instructions += LoadLocal(array);
        instructions += IntConstant(i);
        instructions += Constant(*collector.pieces[i].literal);
      } else {
        AlternativeReadingScope scope(&reader_, collector.pieces[i].offset);
        instructions += LoadLocal(array);
        instructions += IntConstant(i);
        instructions += BuildExpression();
      }
      instructions += StoreIndexed(kArrayCid);
    }
  
    instructions += StringInterpolate(position);
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildIsExpression(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    if (translation_helper_.info().kernel_binary_version() >= 38) {
      // We do not use the library mode for the type test, which is indicated by
      // the flag kIsExpressionFlagForNonNullableByDefault.
      ReadFlags();
    }
  
    Fragment instructions = BuildExpression();  // read operand.
  
    const AbstractType& type = T.BuildType();  // read type.
  
    // The VM does not like an instanceOf call with a dynamic type. We need to
    // special case this situation by detecting a top type.
    if (type.IsTopTypeForInstanceOf()) {
      // Evaluate the expression on the left but ignore its result.
      instructions += Drop();
  
      // Let condition be always true.
      instructions += Constant(Bool::True());
    } else {
      // See if simple instanceOf is applicable.
      if (dart::SimpleInstanceOfType(type)) {
        instructions += Constant(type);
        instructions += InstanceCall(
            position, Library::PrivateCoreLibName(Symbols::_simpleInstanceOf()),
            Token::kIS, 2, 2);  // 2 checked arguments.
        return instructions;
      }
  
      if (!type.IsInstantiated(kCurrentClass)) {
        instructions += LoadInstantiatorTypeArguments();
      } else {
        instructions += NullConstant();
      }
  
      if (!type.IsInstantiated(kFunctions)) {
        instructions += LoadFunctionTypeArguments();
      } else {
        instructions += NullConstant();
      }
  
      instructions += Constant(type);
  
      instructions += InstanceCall(
          position, Library::PrivateCoreLibName(Symbols::_instanceOf()),
          Token::kIS, 4);
    }
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildAsExpression(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const uint8_t flags = ReadFlags();  // read flags.
    const bool is_type_error = (flags & kAsExpressionFlagTypeError) != 0;
  
    Fragment instructions = BuildExpression();  // read operand.
  
    const AbstractType& type = T.BuildType();  // read type.
    if (type.IsInstantiated() && type.IsTopTypeForSubtyping()) {
      // We already evaluated the operand on the left and just leave it there as
      // the result of the `obj as dynamic` expression.
    } else {
      // We do not care whether the 'as' cast as implicitly added by the frontend
      // or explicitly written by the user, in both cases we use an assert
      // assignable.
      instructions += LoadLocal(MakeTemporary());
      instructions += B->AssertAssignableLoadTypeArguments(
          position, type,
          is_type_error ? Symbols::Empty() : Symbols::InTypeCast(),
          AssertAssignableInstr::kInsertedByFrontend);
      instructions += Drop();
    }
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildTypeLiteral(TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    const AbstractType& type = T.BuildType();  // read type.
    Fragment instructions;
    if (type.IsInstantiated()) {
      instructions += Constant(type);
    } else {
      if (!type.IsInstantiated(kCurrentClass)) {
        instructions += LoadInstantiatorTypeArguments();
      } else {
        instructions += NullConstant();
      }
      if (!type.IsInstantiated(kFunctions)) {
        instructions += LoadFunctionTypeArguments();
      } else {
        instructions += NullConstant();
      }
      instructions += InstantiateType(type);
    }
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildThisExpression(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    return LoadLocal(parsed_function()->receiver_var());
  }
  
  Fragment StreamingFlowGraphBuilder::BuildRethrow(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    Fragment instructions = DebugStepCheck(position);
    instructions += LoadLocal(catch_block()->exception_var());
    instructions += LoadLocal(catch_block()->stack_trace_var());
    instructions += RethrowException(position, catch_block()->catch_try_index());
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildThrow(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    Fragment instructions;
  
    instructions += BuildExpression();  // read expression.
  
    if (NeedsDebugStepCheck(stack(), position)) {
      instructions = DebugStepCheck(position) + instructions;
    }
    instructions += ThrowException(position);
    ASSERT(instructions.is_closed());
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildListLiteral(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const TypeArguments& type_arguments = T.BuildTypeArguments(1);  // read type.
    intptr_t length = ReadListLength();  // read list length.
    // Note: there will be "length" expressions.
  
    // The type argument for the factory call.
    Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments);
  
    if (length == 0) {
      instructions += IntConstant(0);
      instructions += StaticCall(
          position,
          Function::ZoneHandle(Z, I->object_store()->growable_list_factory()), 2,
          ICData::kStatic);
      return instructions;
    }
  
    LocalVariable* type = MakeTemporary();
    instructions += LoadLocal(type);
  
    // The type arguments for CreateArray.
    instructions += LoadLocal(type);
    instructions += IntConstant(length);
    instructions += CreateArray();
  
    LocalVariable* array = MakeTemporary();
    for (intptr_t i = 0; i < length; ++i) {
      instructions += LoadLocal(array);
      instructions += IntConstant(i);
      instructions += BuildExpression();  // read ith expression.
      instructions += StoreIndexed(kArrayCid);
    }
  
    const Class& factory_class =
        Class::Handle(Z, Library::LookupCoreClass(Symbols::List()));
    const Function& factory_method = Function::ZoneHandle(
        Z, factory_class.LookupFactory(
               Library::PrivateCoreLibName(Symbols::ListLiteralFactory())));
  
    instructions += StaticCall(position, factory_method, 2, ICData::kStatic);
    instructions += DropTempsPreserveTop(1);  // Instantiated type_arguments.
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildMapLiteral(TokenPosition* p) {
    TokenPosition position = ReadPosition();  // read position.
    if (p != NULL) *p = position;
  
    const TypeArguments& type_arguments =
        T.BuildTypeArguments(2);  // read key_type and value_type.
  
    // The type argument for the factory call `new Map<K, V>._fromLiteral(List)`.
    Fragment instructions = TranslateInstantiatedTypeArguments(type_arguments);
  
    intptr_t length = ReadListLength();  // read list length.
    // Note: there will be "length" map entries (i.e. key and value expressions).
  
    if (length == 0) {
      instructions += Constant(Object::empty_array());
    } else {
      // The type arguments for `new List<X>(int len)`.
      instructions += Constant(TypeArguments::ZoneHandle(Z));
  
      // We generate a list of tuples, i.e. [key1, value1, ..., keyN, valueN].
      instructions += IntConstant(2 * length);
      instructions += CreateArray();
  
      LocalVariable* array = MakeTemporary();
      for (intptr_t i = 0; i < length; ++i) {
        instructions += LoadLocal(array);
        instructions += IntConstant(2 * i);
        instructions += BuildExpression();  // read ith key.
        instructions += StoreIndexed(kArrayCid);
  
        instructions += LoadLocal(array);
        instructions += IntConstant(2 * i + 1);
        instructions += BuildExpression();  // read ith value.
        instructions += StoreIndexed(kArrayCid);
      }
    }
  
    const Class& map_class =
        Class::Handle(Z, Library::LookupCoreClass(Symbols::Map()));
    Function& factory_method = Function::ZoneHandle(Z);
    if (map_class.EnsureIsFinalized(H.thread()) == Error::null()) {
      factory_method = map_class.LookupFactory(
          Library::PrivateCoreLibName(Symbols::MapLiteralFactory()));
    }
  
    return instructions +
           StaticCall(position, factory_method, 2, ICData::kStatic);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFunctionExpression() {
    ReadPosition();  // read position.
    return BuildFunctionNode(TokenPosition::kNoSource, StringIndex());
  }
  
  Fragment StreamingFlowGraphBuilder::BuildLet(TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    Fragment instructions = BuildVariableDeclaration();  // read variable.
    instructions += BuildExpression();                   // read body.
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildBlockExpression() {
    block_expression_depth_inc();
    const intptr_t offset = ReaderOffset() - 1;  // Include the tag.
  
    Fragment instructions;
  
    instructions += EnterScope(offset);
    const intptr_t list_length = ReadListLength();  // read number of statements.
    for (intptr_t i = 0; i < list_length; ++i) {
      instructions += BuildStatement();  // read ith statement.
    }
    instructions += BuildExpression();  // read expression (inside scope).
    instructions += ExitScope(offset);
  
    block_expression_depth_dec();
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildBigIntLiteral(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    const String& value =
        H.DartString(ReadStringReference());  // read index into string table.
    const Integer& integer = Integer::ZoneHandle(Z, Integer::NewCanonical(value));
    if (integer.IsNull()) {
      H.ReportError(script_, TokenPosition::kNoSource,
                    "Integer literal %s is out of range", value.ToCString());
      UNREACHABLE();
    }
    return Constant(integer);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildStringLiteral(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    return Constant(H.DartSymbolPlain(
        ReadStringReference()));  // read index into string table.
  }
  
  Fragment StreamingFlowGraphBuilder::BuildIntLiteral(uint8_t payload,
                                                      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    int64_t value = static_cast<int32_t>(payload) - SpecializedIntLiteralBias;
    return IntConstant(value);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildIntLiteral(bool is_negative,
                                                      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    int64_t value = is_negative ? -static_cast<int64_t>(ReadUInt())
                                : ReadUInt();  // read value.
    return IntConstant(value);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildDoubleLiteral(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    Double& constant = Double::ZoneHandle(
        Z, Double::NewCanonical(ReadDouble()));  // read double.
    return Constant(constant);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildBoolLiteral(bool value,
                                                       TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    return Constant(Bool::Get(value));
  }
  
  Fragment StreamingFlowGraphBuilder::BuildNullLiteral(TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    return Constant(Instance::ZoneHandle(Z, Instance::null()));
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFutureNullValue(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
    const Class& future = Class::Handle(Z, I->object_store()->future_class());
    ASSERT(!future.IsNull());
    const auto& error = future.EnsureIsFinalized(thread());
    ASSERT(error == Error::null());
    Function& constructor = Function::ZoneHandle(
        Z, Resolver::ResolveFunction(Z, future, Symbols::FutureValue()));
    ASSERT(!constructor.IsNull());
  
    Fragment instructions;
    instructions += BuildNullLiteral(position);
    instructions += StaticCall(TokenPosition::kNoSource, constructor,
                               /* argument_count = */ 1, ICData::kStatic);
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildConstantExpression(
      TokenPosition* position,
      Tag tag) {
    TokenPosition p = TokenPosition::kNoSource;
    if (tag == kConstantExpression) {
      p = ReadPosition();
      SkipDartType();
    }
    if (position != nullptr) *position = p;
    const intptr_t constant_offset = ReadUInt();
    Fragment result = Constant(
        Object::ZoneHandle(Z, constant_reader_.ReadConstant(constant_offset)));
    return result;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildPartialTearoffInstantiation(
      TokenPosition* position) {
    if (position != NULL) *position = TokenPosition::kNoSource;
  
    // Create a copy of the closure.
  
    Fragment instructions = BuildExpression();
    LocalVariable* original_closure = MakeTemporary();
  
    instructions += AllocateObject(
        TokenPosition::kNoSource,
        Class::ZoneHandle(Z, I->object_store()->closure_class()), 0);
    LocalVariable* new_closure = MakeTemporary();
  
    intptr_t num_type_args = ReadListLength();
    const TypeArguments& type_args = T.BuildTypeArguments(num_type_args);
    instructions += TranslateInstantiatedTypeArguments(type_args);
    LocalVariable* type_args_vec = MakeTemporary();
  
    // Check the bounds.
    //
    // TODO(sjindel): We should be able to skip this check in many cases, e.g.
    // when the closure is coming from a tearoff of a top-level method or from a
    // local closure.
    instructions += LoadLocal(original_closure);
    instructions += LoadLocal(type_args_vec);
    const Library& dart_internal = Library::Handle(Z, Library::InternalLibrary());
    const Function& bounds_check_function = Function::ZoneHandle(
        Z, dart_internal.LookupFunctionAllowPrivate(
               Symbols::BoundsCheckForPartialInstantiation()));
    ASSERT(!bounds_check_function.IsNull());
    instructions += StaticCall(TokenPosition::kNoSource, bounds_check_function, 2,
                               ICData::kStatic);
    instructions += Drop();
  
    instructions += LoadLocal(new_closure);
    instructions += LoadLocal(type_args_vec);
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_delayed_type_arguments(),
        StoreInstanceFieldInstr::Kind::kInitializing);
    instructions += Drop();  // Drop type args.
  
    // Copy over the target function.
    instructions += LoadLocal(new_closure);
    instructions += LoadLocal(original_closure);
    instructions +=
        flow_graph_builder_->LoadNativeField(Slot::Closure_function());
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_function(),
        StoreInstanceFieldInstr::Kind::kInitializing);
  
    // Copy over the instantiator type arguments.
    instructions += LoadLocal(new_closure);
    instructions += LoadLocal(original_closure);
    instructions += flow_graph_builder_->LoadNativeField(
        Slot::Closure_instantiator_type_arguments());
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_instantiator_type_arguments(),
        StoreInstanceFieldInstr::Kind::kInitializing);
  
    // Copy over the function type arguments.
    instructions += LoadLocal(new_closure);
    instructions += LoadLocal(original_closure);
    instructions += flow_graph_builder_->LoadNativeField(
        Slot::Closure_function_type_arguments());
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_function_type_arguments(),
        StoreInstanceFieldInstr::Kind::kInitializing);
  
    // Copy over the context.
    instructions += LoadLocal(new_closure);
    instructions += LoadLocal(original_closure);
    instructions += flow_graph_builder_->LoadNativeField(Slot::Closure_context());
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_context(),
        StoreInstanceFieldInstr::Kind::kInitializing);
  
    instructions += DropTempsPreserveTop(1);  // Drop old closure.
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildLibraryPrefixAction(
      TokenPosition* position,
      const String& selector) {
    const intptr_t dependency_index = ReadUInt();
    const Library& current_library = Library::Handle(
        Z, Class::Handle(Z, parsed_function()->function().origin()).library());
    const Array& dependencies = Array::Handle(Z, current_library.dependencies());
    const LibraryPrefix& prefix =
        LibraryPrefix::CheckedZoneHandle(Z, dependencies.At(dependency_index));
    const Function& function =
        Function::ZoneHandle(Z, Library::Handle(Z, Library::CoreLibrary())
                                    .LookupFunctionAllowPrivate(selector));
    ASSERT(!function.IsNull());
    Fragment instructions;
    instructions += Constant(prefix);
    instructions +=
        StaticCall(TokenPosition::kNoSource, function, 1, ICData::kStatic);
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildExpressionStatement() {
    Fragment instructions = BuildExpression();  // read expression.
    instructions += Drop();
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildBlock() {
    intptr_t offset = ReaderOffset() - 1;  // Include the tag.
  
    Fragment instructions;
  
    instructions += EnterScope(offset);
    ReadPosition();                           // read file offset.
    ReadPosition();                           // read file end offset.
    intptr_t list_length = ReadListLength();  // read number of statements.
    for (intptr_t i = 0; i < list_length; ++i) {
      if (instructions.is_open()) {
        instructions += BuildStatement();  // read ith statement.
      } else {
        SkipStatement();  // read ith statement.
      }
    }
    instructions += ExitScope(offset);
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildEmptyStatement() {
    return Fragment();
  }
  
  Fragment StreamingFlowGraphBuilder::BuildAssertBlock() {
    if (!I->asserts()) {
      SkipStatementList();
      return Fragment();
    }
  
    intptr_t offset = ReaderOffset() - 1;  // Include the tag.
  
    Fragment instructions;
  
    instructions += EnterScope(offset);
    intptr_t list_length = ReadListLength();  // read number of statements.
    for (intptr_t i = 0; i < list_length; ++i) {
      if (instructions.is_open()) {
        instructions += BuildStatement();  // read ith statement.
      } else {
        SkipStatement();  // read ith statement.
      }
    }
    instructions += ExitScope(offset);
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildAssertStatement() {
    if (!I->asserts()) {
      SetOffset(ReaderOffset() - 1);  // Include the tag.
      SkipStatement();                // read this statement.
      return Fragment();
    }
  
    TargetEntryInstr* then;
    TargetEntryInstr* otherwise;
  
    Fragment instructions;
    // Asserts can be of the following two kinds:
    //
    //    * `assert(expr)`
    //    * `assert(() { ... })`
    //
    // The call to `_AssertionError._evaluateAssertion()` will take care of both
    // and returns a boolean.
    instructions += BuildExpression();  // read condition.
  
    const TokenPosition condition_start_offset =
        ReadPosition();  // read condition start offset.
    const TokenPosition condition_end_offset =
        ReadPosition();  // read condition end offset.
  
    instructions += EvaluateAssertion();
    instructions += CheckBoolean(condition_start_offset);
    instructions += Constant(Bool::True());
    instructions += BranchIfEqual(&then, &otherwise, false);
  
    const Class& klass =
        Class::ZoneHandle(Z, Library::LookupCoreClass(Symbols::AssertionError()));
    ASSERT(!klass.IsNull());
    const Function& target = Function::ZoneHandle(
        Z, klass.LookupStaticFunctionAllowPrivate(Symbols::ThrowNew()));
    ASSERT(!target.IsNull());
  
    // Build equivalent of `throw _AssertionError._throwNew(start, end, message)`
    // expression. We build throw (even through _throwNew already throws) because
    // call is not a valid last instruction for the block. Blocks can only
    // terminate with explicit control flow instructions (Branch, Goto, Return
    // or Throw).
    Fragment otherwise_fragment(otherwise);
    otherwise_fragment += IntConstant(condition_start_offset.Pos());
    otherwise_fragment += IntConstant(condition_end_offset.Pos());
    Tag tag = ReadTag();  // read (first part of) message.
    if (tag == kSomething) {
      otherwise_fragment += BuildExpression();  // read (rest of) message.
    } else {
      otherwise_fragment += Constant(Instance::ZoneHandle(Z));  // null.
    }
  
    // Note: condition_start_offset points to the first token after the opening
    // paren, not the beginning of 'assert'.
    otherwise_fragment +=
        StaticCall(condition_start_offset, target, 3, ICData::kStatic);
    otherwise_fragment += ThrowException(TokenPosition::kNoSource);
    otherwise_fragment += Drop();
  
    return Fragment(instructions.entry, then);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildLabeledStatement() {
    // There can be serveral cases:
    //
    //   * the body contains a break
    //   * the body doesn't contain a break
    //
    //   * translating the body results in a closed fragment
    //   * translating the body results in a open fragment
    //
    // => We will only know which case we are in after the body has been
    //    traversed.
  
    BreakableBlock block(flow_graph_builder_);
    Fragment instructions = BuildStatement();  // read body.
    if (block.HadJumper()) {
      if (instructions.is_open()) {
        instructions += Goto(block.destination());
      }
      return Fragment(instructions.entry, block.destination());
    } else {
      return instructions;
    }
  }
  
  Fragment StreamingFlowGraphBuilder::BuildBreakStatement() {
    TokenPosition position = ReadPosition();  // read position.
    intptr_t target_index = ReadUInt();       // read target index.
  
    TryFinallyBlock* outer_finally = NULL;
    intptr_t target_context_depth = -1;
    JoinEntryInstr* destination = breakable_block()->BreakDestination(
        target_index, &outer_finally, &target_context_depth);
  
    Fragment instructions;
    // Break statement should pause before manipulation of context, which
    // will possibly cause debugger having incorrect context object.
    if (NeedsDebugStepCheck(parsed_function()->function(), position)) {
      instructions += DebugStepCheck(position);
    }
    instructions +=
        TranslateFinallyFinalizers(outer_finally, target_context_depth);
    if (instructions.is_open()) {
      instructions += Goto(destination);
    }
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildWhileStatement() {
    ASSERT(block_expression_depth() == 0);  // no while in block-expr
    loop_depth_inc();
    const TokenPosition position = ReadPosition();            // read position.
    TestFragment condition = TranslateConditionForControl();  // read condition.
    const Fragment body = BuildStatement();                   // read body
  
    Fragment body_entry(condition.CreateTrueSuccessor(flow_graph_builder_));
    body_entry += body;
  
    Instruction* entry;
    if (body_entry.is_open()) {
      JoinEntryInstr* join = BuildJoinEntry();
      body_entry += Goto(join);
  
      Fragment loop(join);
      ASSERT(B->GetStackDepth() == 0);
      loop += CheckStackOverflow(position);
      loop.current->LinkTo(condition.entry);
  
      entry = Goto(join).entry;
    } else {
      entry = condition.entry;
    }
  
    loop_depth_dec();
    return Fragment(entry, condition.CreateFalseSuccessor(flow_graph_builder_));
  }
  
  Fragment StreamingFlowGraphBuilder::BuildDoStatement() {
    ASSERT(block_expression_depth() == 0);  // no do-while in block-expr
    loop_depth_inc();
    const TokenPosition position = ReadPosition();  // read position.
    Fragment body = BuildStatement();               // read body.
  
    if (body.is_closed()) {
      SkipExpression();  // read condition.
      loop_depth_dec();
      return body;
    }
  
    TestFragment condition = TranslateConditionForControl();
  
    JoinEntryInstr* join = BuildJoinEntry();
    Fragment loop(join);
    ASSERT(B->GetStackDepth() == 0);
    loop += CheckStackOverflow(position);
    loop += body;
    loop <<= condition.entry;
  
    condition.IfTrueGoto(flow_graph_builder_, join);
  
    loop_depth_dec();
    return Fragment(
        new (Z) GotoInstr(join, CompilerState::Current().GetNextDeoptId()),
        condition.CreateFalseSuccessor(flow_graph_builder_));
  }
  
  Fragment StreamingFlowGraphBuilder::BuildForStatement() {
    intptr_t offset = ReaderOffset() - 1;  // Include the tag.
  
    const TokenPosition position = ReadPosition();  // read position.
  
    Fragment declarations;
  
    loop_depth_inc();
  
    const LocalScope* context_scope = nullptr;
    declarations += EnterScope(offset, &context_scope);
  
    intptr_t list_length = ReadListLength();  // read number of variables.
    for (intptr_t i = 0; i < list_length; ++i) {
      declarations += BuildVariableDeclaration();  // read ith variable.
    }
  
    Tag tag = ReadTag();  // Read first part of condition.
    TestFragment condition;
    BlockEntryInstr* body_entry;
    BlockEntryInstr* loop_exit;
    if (tag != kNothing) {
      condition = TranslateConditionForControl();
      body_entry = condition.CreateTrueSuccessor(flow_graph_builder_);
      loop_exit = condition.CreateFalseSuccessor(flow_graph_builder_);
    } else {
      body_entry = BuildJoinEntry();
      loop_exit = BuildJoinEntry();
    }
  
    Fragment updates;
    list_length = ReadListLength();  // read number of updates.
    for (intptr_t i = 0; i < list_length; ++i) {
      updates += BuildExpression();  // read ith update.
      updates += Drop();
    }
  
    Fragment body(body_entry);
    body += BuildStatement();  // read body.
  
    if (body.is_open()) {
      // We allocated a fresh context before the loop which contains captured
      // [ForStatement] variables.  Before jumping back to the loop entry we clone
      // the context object (at same depth) which ensures the next iteration of
      // the body gets a fresh set of [ForStatement] variables (with the old
      // (possibly updated) values).
      if (context_scope->num_context_variables() > 0) {
        body += CloneContext(context_scope->context_slots());
      }
  
      body += updates;
      JoinEntryInstr* join = BuildJoinEntry();
      declarations += Goto(join);
      body += Goto(join);
  
      Fragment loop(join);
      loop += CheckStackOverflow(position);  // may have non-empty stack
      if (condition.entry != nullptr) {
        loop <<= condition.entry;
      } else {
        loop += Goto(body_entry->AsJoinEntry());
      }
    } else {
      if (condition.entry != nullptr) {
        declarations <<= condition.entry;
      } else {
        declarations += Goto(body_entry->AsJoinEntry());
      }
    }
  
    Fragment loop(declarations.entry, loop_exit);
  
    loop += ExitScope(offset);
  
    loop_depth_dec();
  
    return loop;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildForInStatement(bool async) {
    intptr_t offset = ReaderOffset() - 1;  // Include the tag.
  
    const TokenPosition position = ReadPosition();  // read position.
    TokenPosition body_position = ReadPosition();   // read body position.
    intptr_t variable_kernel_position = ReaderOffset() + data_program_offset_;
    SkipVariableDeclaration();  // read variable.
  
    TokenPosition iterable_position = TokenPosition::kNoSource;
    Fragment instructions =
        BuildExpression(&iterable_position);  // read iterable.
  
    const String& iterator_getter =
        String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Iterator()));
    instructions +=
        InstanceCall(iterable_position, iterator_getter, Token::kGET, 1);
    LocalVariable* iterator = scopes()->iterator_variables[for_in_depth()];
    instructions += StoreLocal(TokenPosition::kNoSource, iterator);
    instructions += Drop();
  
    for_in_depth_inc();
    loop_depth_inc();
    Fragment condition = LoadLocal(iterator);
    condition +=
        InstanceCall(iterable_position, Symbols::MoveNext(), Token::kILLEGAL, 1);
    TargetEntryInstr* body_entry;
    TargetEntryInstr* loop_exit;
    condition += BranchIfTrue(&body_entry, &loop_exit, false);
  
    Fragment body(body_entry);
    body += EnterScope(offset);
    body += LoadLocal(iterator);
    const String& current_getter =
        String::ZoneHandle(Z, Field::GetterSymbol(Symbols::Current()));
    body += InstanceCall(body_position, current_getter, Token::kGET, 1);
    body += StoreLocal(TokenPosition::kNoSource,
                       LookupVariable(variable_kernel_position));
    body += Drop();
    body += BuildStatement();  // read body.
    body += ExitScope(offset);
  
    if (body.is_open()) {
      JoinEntryInstr* join = BuildJoinEntry();
      instructions += Goto(join);
      body += Goto(join);
  
      Fragment loop(join);
      loop += CheckStackOverflow(position);  // may have non-empty stack
      loop += condition;
    } else {
      instructions += condition;
    }
  
    loop_depth_dec();
    for_in_depth_dec();
    return Fragment(instructions.entry, loop_exit);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildSwitchStatement() {
    ReadPosition();  // read position.
    // We need the number of cases. So start by getting that, then go back.
    intptr_t offset = ReaderOffset();
    SkipExpression();                   // temporarily skip condition
    int case_count = ReadListLength();  // read number of cases.
    SetOffset(offset);
  
    SwitchBlock block(flow_graph_builder_, case_count);
  
    // Instead of using a variable we should reuse the expression on the stack,
    // since it won't be assigned again, we don't need phi nodes.
    Fragment head_instructions = BuildExpression();  // read condition.
    head_instructions +=
        StoreLocal(TokenPosition::kNoSource, scopes()->switch_variable);
    head_instructions += Drop();
  
    case_count = ReadListLength();  // read number of cases.
  
    // Phase 1: Generate bodies and try to find out whether a body will be target
    // of a jump due to:
    //   * `continue case_label`
    //   * `case e1: case e2: body`
    Fragment* body_fragments = Z->Alloc<Fragment>(case_count);
    intptr_t* case_expression_offsets = Z->Alloc<intptr_t>(case_count);
    int default_case = -1;
  
    for (intptr_t i = 0; i < case_count; ++i) {
      case_expression_offsets[i] = ReaderOffset();
      int expression_count = ReadListLength();  // read number of expressions.
      for (intptr_t j = 0; j < expression_count; ++j) {
        ReadPosition();    // read jth position.
        SkipExpression();  // read jth expression.
      }
      bool is_default = ReadBool();  // read is_default.
      if (is_default) default_case = i;
      Fragment& body_fragment = body_fragments[i] =
          BuildStatement();  // read body.
  
      if (body_fragment.entry == NULL) {
        // Make a NOP in order to ensure linking works properly.
        body_fragment = NullConstant();
        body_fragment += Drop();
      }
  
      // The Dart language specification mandates fall-throughs in [SwitchCase]es
      // to be runtime errors.
      if (!is_default && body_fragment.is_open() && (i < (case_count - 1))) {
        const Class& klass = Class::ZoneHandle(
            Z, Library::LookupCoreClass(Symbols::FallThroughError()));
        ASSERT(!klass.IsNull());
        const auto& error = klass.EnsureIsFinalized(thread());
        ASSERT(error == Error::null());
  
        GrowableHandlePtrArray<const String> pieces(Z, 3);
        pieces.Add(Symbols::FallThroughError());
        pieces.Add(Symbols::Dot());
        pieces.Add(H.DartSymbolObfuscate("_create"));
  
        const Function& constructor = Function::ZoneHandle(
            Z, klass.LookupConstructorAllowPrivate(String::ZoneHandle(
                   Z, Symbols::FromConcatAll(H.thread(), pieces))));
        ASSERT(!constructor.IsNull());
        const String& url = H.DartSymbolPlain(
            parsed_function()->function().ToLibNamePrefixedQualifiedCString());
  
        // Create instance of _FallThroughError
        body_fragment += AllocateObject(TokenPosition::kNoSource, klass, 0);
        LocalVariable* instance = MakeTemporary();
  
        // Call _FallThroughError._create constructor.
        body_fragment += LoadLocal(instance);  // this
        body_fragment += Constant(url);        // url
        body_fragment += NullConstant();       // line
  
        body_fragment +=
            StaticCall(TokenPosition::kNoSource, constructor, 3, ICData::kStatic);
        body_fragment += Drop();
  
        // Throw the exception
        body_fragment += ThrowException(TokenPosition::kNoSource);
        body_fragment += Drop();
      }
  
      // If there is an implicit fall-through we have one [SwitchCase] and
      // multiple expressions, e.g.
      //
      //    switch(expr) {
      //      case a:
      //      case b:
      //        <stmt-body>
      //    }
      //
      // This means that the <stmt-body> will have more than 1 incoming edge (one
      // from `a == expr` and one from `a != expr && b == expr`). The
      // `block.Destination()` records the additional jump.
      if (expression_count > 1) {
        block.DestinationDirect(i);
      }
    }
  
    intptr_t end_offset = ReaderOffset();
  
    // Phase 2: Generate everything except the real bodies:
    //   * jump directly to a body (if there is no jumper)
    //   * jump to a wrapper block which jumps to the body (if there is a jumper)
    Fragment current_instructions = head_instructions;
    for (intptr_t i = 0; i < case_count; ++i) {
      SetOffset(case_expression_offsets[i]);
      int expression_count = ReadListLength();  // read length of expressions.
  
      if (i == default_case) {
        ASSERT(i == (case_count - 1));
  
        if (block.HadJumper(i)) {
          // There are several branches to the body, so we will make a goto to
          // the join block (and prepend a join instruction to the real body).
          JoinEntryInstr* join = block.DestinationDirect(i);
          current_instructions += Goto(join);
  
          current_instructions = Fragment(current_instructions.entry, join);
          current_instructions += body_fragments[i];
        } else {
          current_instructions += body_fragments[i];
        }
      } else {
        JoinEntryInstr* body_join = NULL;
        if (block.HadJumper(i)) {
          body_join = block.DestinationDirect(i);
          body_fragments[i] = Fragment(body_join) + body_fragments[i];
        }
  
        for (intptr_t j = 0; j < expression_count; ++j) {
          TargetEntryInstr* then;
          TargetEntryInstr* otherwise;
  
          TokenPosition position = ReadPosition();  // read jth position.
          current_instructions += Constant(
              Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression()));
          current_instructions += LoadLocal(scopes()->switch_variable);
          current_instructions +=
              InstanceCall(position, Symbols::EqualOperator(), Token::kEQ,
                           /*argument_count=*/2,
                           /*checked_argument_count=*/2);
          current_instructions += BranchIfTrue(&then, &otherwise, false);
  
          Fragment then_fragment(then);
  
          if (body_join != NULL) {
            // There are several branches to the body, so we will make a goto to
            // the join block (the real body has already been prepended with a
            // join instruction).
            then_fragment += Goto(body_join);
          } else {
            // There is only a signle branch to the body, so we will just append
            // the body fragment.
            then_fragment += body_fragments[i];
          }
  
          current_instructions = Fragment(otherwise);
        }
      }
    }
  
    if (case_count > 0 && default_case < 0) {
      // There is no default, which means we have an open [current_instructions]
      // (which is a [TargetEntryInstruction] for the last "otherwise" branch).
      //
      // Furthermore the last [SwitchCase] can be open as well.  If so, we need
      // to join these two.
      Fragment& last_body = body_fragments[case_count - 1];
      if (last_body.is_open()) {
        ASSERT(current_instructions.is_open());
        ASSERT(current_instructions.current->IsTargetEntry());
  
        // Join the last "otherwise" branch and the last [SwitchCase] fragment.
        JoinEntryInstr* join = BuildJoinEntry();
        current_instructions += Goto(join);
        last_body += Goto(join);
  
        current_instructions = Fragment(join);
      }
    } else {
      // All non-default cases will be closed (i.e. break/continue/throw/return)
      // So it is fine to just let more statements after the switch append to the
      // default case.
    }
  
    SetOffset(end_offset);
    return Fragment(head_instructions.entry, current_instructions.current);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildContinueSwitchStatement() {
    TokenPosition position = ReadPosition();  // read position.
    intptr_t target_index = ReadUInt();       // read target index.
  
    TryFinallyBlock* outer_finally = NULL;
    intptr_t target_context_depth = -1;
    JoinEntryInstr* entry = switch_block()->Destination(
        target_index, &outer_finally, &target_context_depth);
  
    Fragment instructions;
    instructions +=
        TranslateFinallyFinalizers(outer_finally, target_context_depth);
    if (instructions.is_open()) {
      if (NeedsDebugStepCheck(parsed_function()->function(), position)) {
        instructions += DebugStepCheck(position);
      }
      instructions += Goto(entry);
    }
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildIfStatement() {
    ReadPosition();  // read position.
  
    TestFragment condition = TranslateConditionForControl();
  
    Fragment then_fragment(condition.CreateTrueSuccessor(flow_graph_builder_));
    then_fragment += BuildStatement();  // read then.
  
    Fragment otherwise_fragment(
        condition.CreateFalseSuccessor(flow_graph_builder_));
    otherwise_fragment += BuildStatement();  // read otherwise.
  
    if (then_fragment.is_open()) {
      if (otherwise_fragment.is_open()) {
        JoinEntryInstr* join = BuildJoinEntry();
        then_fragment += Goto(join);
        otherwise_fragment += Goto(join);
        return Fragment(condition.entry, join);
      } else {
        return Fragment(condition.entry, then_fragment.current);
      }
    } else if (otherwise_fragment.is_open()) {
      return Fragment(condition.entry, otherwise_fragment.current);
    } else {
      return Fragment(condition.entry, nullptr);
    }
  }
  
  Fragment StreamingFlowGraphBuilder::BuildReturnStatement() {
    TokenPosition position = ReadPosition();  // read position.
    Tag tag = ReadTag();                      // read first part of expression.
  
    bool inside_try_finally = try_finally_block() != NULL;
  
    Fragment instructions = tag == kNothing
                                ? NullConstant()
                                : BuildExpression();  // read rest of expression.
  
    if (instructions.is_open()) {
      if (inside_try_finally) {
        ASSERT(scopes()->finally_return_variable != NULL);
        const Function& function = parsed_function()->function();
        if (NeedsDebugStepCheck(function, position)) {
          instructions += DebugStepCheck(position);
        }
        instructions += StoreLocal(position, scopes()->finally_return_variable);
        instructions += Drop();
        instructions += TranslateFinallyFinalizers(NULL, -1);
        if (instructions.is_open()) {
          instructions += LoadLocal(scopes()->finally_return_variable);
          instructions += Return(TokenPosition::kNoSource);
        }
      } else {
        instructions += Return(position);
      }
    } else {
      Pop();
    }
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildTryCatch() {
    ASSERT(block_expression_depth() == 0);  // no try-catch in block-expr
    InlineBailout("kernel::FlowgraphBuilder::VisitTryCatch");
  
    intptr_t try_handler_index = AllocateTryIndex();
    Fragment try_body = TryCatch(try_handler_index);
    JoinEntryInstr* after_try = BuildJoinEntry();
  
    // Fill in the body of the try.
    try_depth_inc();
    {
      TryCatchBlock block(flow_graph_builder_, try_handler_index);
      try_body += BuildStatement();  // read body.
      try_body += Goto(after_try);
    }
    try_depth_dec();
  
    const int kNeedsStracktraceBit = 1 << 0;
    const int kIsSyntheticBit = 1 << 1;
  
    uint8_t flags = ReadByte();
    bool needs_stacktrace =
        (flags & kNeedsStracktraceBit) == kNeedsStracktraceBit;
    bool is_synthetic = (flags & kIsSyntheticBit) == kIsSyntheticBit;
  
    catch_depth_inc();
    intptr_t catch_count = ReadListLength();  // read number of catches.
    const Array& handler_types =
        Array::ZoneHandle(Z, Array::New(catch_count, Heap::kOld));
  
    Fragment catch_body = CatchBlockEntry(handler_types, try_handler_index,
                                          needs_stacktrace, is_synthetic);
    // Fill in the body of the catch.
    for (intptr_t i = 0; i < catch_count; ++i) {
      intptr_t catch_offset = ReaderOffset();          // Catch has no tag.
      TokenPosition position = ReadPosition();         // read position.
      const AbstractType& type_guard = T.BuildType();  // read guard.
      handler_types.SetAt(i, type_guard);
  
      Fragment catch_handler_body = EnterScope(catch_offset);
  
      Tag tag = ReadTag();  // read first part of exception.
      if (tag == kSomething) {
        catch_handler_body += LoadLocal(CurrentException());
        catch_handler_body +=
            StoreLocal(TokenPosition::kNoSource,
                       LookupVariable(ReaderOffset() + data_program_offset_));
        catch_handler_body += Drop();
        SkipVariableDeclaration();  // read exception.
      }
  
      tag = ReadTag();  // read first part of stack trace.
      if (tag == kSomething) {
        catch_handler_body += LoadLocal(CurrentStackTrace());
        catch_handler_body +=
            StoreLocal(TokenPosition::kNoSource,
                       LookupVariable(ReaderOffset() + data_program_offset_));
        catch_handler_body += Drop();
        SkipVariableDeclaration();  // read stack trace.
      }
  
      {
        CatchBlock block(flow_graph_builder_, CurrentException(),
                         CurrentStackTrace(), try_handler_index);
  
        catch_handler_body += BuildStatement();  // read body.
  
        // Note: ExitScope adjusts context_depth_ so even if catch_handler_body
        // is closed we still need to execute ExitScope for its side effect.
        catch_handler_body += ExitScope(catch_offset);
        if (catch_handler_body.is_open()) {
          catch_handler_body += Goto(after_try);
        }
      }
  
      if (!type_guard.IsCatchAllType()) {
        catch_body += LoadLocal(CurrentException());
  
        if (!type_guard.IsInstantiated(kCurrentClass)) {
          catch_body += LoadInstantiatorTypeArguments();
        } else {
          catch_body += NullConstant();
        }
  
        if (!type_guard.IsInstantiated(kFunctions)) {
          catch_body += LoadFunctionTypeArguments();
        } else {
          catch_body += NullConstant();
        }
  
        catch_body += Constant(type_guard);
  
        catch_body += InstanceCall(
            position, Library::PrivateCoreLibName(Symbols::_instanceOf()),
            Token::kIS, 4);
  
        TargetEntryInstr* catch_entry;
        TargetEntryInstr* next_catch_entry;
        catch_body += BranchIfTrue(&catch_entry, &next_catch_entry, false);
  
        Fragment(catch_entry) + catch_handler_body;
        catch_body = Fragment(next_catch_entry);
      } else {
        catch_body += catch_handler_body;
      }
    }
  
    // In case the last catch body was not handling the exception and branching to
    // after the try block, we will rethrow the exception (i.e. no default catch
    // handler).
    if (catch_body.is_open()) {
      catch_body += LoadLocal(CurrentException());
      catch_body += LoadLocal(CurrentStackTrace());
      catch_body += RethrowException(TokenPosition::kNoSource, try_handler_index);
      Drop();
    }
    catch_depth_dec();
  
    return Fragment(try_body.entry, after_try);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildTryFinally() {
    ASSERT(block_expression_depth() == 0);  // no try-finally in block-expr
    // Note on streaming:
    // We only stream this TryFinally if we can stream everything inside it,
    // so creating a "TryFinallyBlock" with a kernel binary offset instead of an
    // AST node isn't a problem.
  
    InlineBailout("kernel::FlowgraphBuilder::VisitTryFinally");
  
    // There are 5 different cases where we need to execute the finally block:
    //
    //  a) 1/2/3th case: Special control flow going out of `node->body()`:
    //
    //   * [BreakStatement] transfers control to a [LabeledStatement]
    //   * [ContinueSwitchStatement] transfers control to a [SwitchCase]
    //   * [ReturnStatement] returns a value
    //
    //   => All three cases will automatically append all finally blocks
    //      between the branching point and the destination (so we don't need to
    //      do anything here).
    //
    //  b) 4th case: Translating the body resulted in an open fragment (i.e. body
    //               executes without any control flow out of it)
    //
    //   => We are responsible for jumping out of the body to a new block (with
    //      different try index) and execute the finalizer.
    //
    //  c) 5th case: An exception occurred inside the body.
    //
    //   => We are responsible for catching it, executing the finally block and
    //      rethrowing the exception.
    intptr_t try_handler_index = AllocateTryIndex();
    Fragment try_body = TryCatch(try_handler_index);
    JoinEntryInstr* after_try = BuildJoinEntry();
  
    intptr_t offset = ReaderOffset();
    SkipStatement();  // temporarily read body.
    intptr_t finalizer_offset = ReaderOffset();
    SetOffset(offset);
  
    // Fill in the body of the try.
    try_depth_inc();
    {
      TryFinallyBlock tfb(flow_graph_builder_, finalizer_offset);
      TryCatchBlock tcb(flow_graph_builder_, try_handler_index);
      try_body += BuildStatement();  // read body.
    }
    try_depth_dec();
  
    if (try_body.is_open()) {
      // Please note: The try index will be on level out of this block,
      // thereby ensuring if there's an exception in the finally block we
      // won't run it twice.
      JoinEntryInstr* finally_entry = BuildJoinEntry();
  
      try_body += Goto(finally_entry);
  
      Fragment finally_body(finally_entry);
      finally_body += BuildStatement();  // read finalizer.
      finally_body += Goto(after_try);
    }
  
    // Fill in the body of the catch.
    catch_depth_inc();
    const Array& handler_types = Array::ZoneHandle(Z, Array::New(1, Heap::kOld));
    handler_types.SetAt(0, Object::dynamic_type());
    // Note: rethrow will actually force mark the handler as needing a stacktrace.
    Fragment finally_body = CatchBlockEntry(handler_types, try_handler_index,
                                            /* needs_stacktrace = */ false,
                                            /* is_synthesized = */ true);
    SetOffset(finalizer_offset);
    finally_body += BuildStatement();  // read finalizer
    if (finally_body.is_open()) {
      finally_body += LoadLocal(CurrentException());
      finally_body += LoadLocal(CurrentStackTrace());
      finally_body +=
          RethrowException(TokenPosition::kNoSource, try_handler_index);
      Drop();
    }
    catch_depth_dec();
  
    return Fragment(try_body.entry, after_try);
  }
  
  Fragment StreamingFlowGraphBuilder::BuildYieldStatement() {
    TokenPosition position = ReadPosition();  // read position.
    uint8_t flags = ReadByte();               // read flags.
  
    ASSERT(flags == kNativeYieldFlags);  // Must have been desugared.
  
    // Setup yield/continue point:
    //
    //   ...
    //   :await_jump_var = index;
    //   :await_ctx_var = :current_context_var
    //   return <expr>
    //
    // Continuation<index>:
    //   Drop(1)
    //   ...
    //
    // BuildGraphOfFunction will create a dispatch that jumps to
    // Continuation<:await_jump_var> upon entry to the function.
    //
    const intptr_t new_yield_pos = yield_continuations().length() + 1;
    Fragment instructions = IntConstant(new_yield_pos);
    instructions +=
        StoreLocal(TokenPosition::kNoSource, scopes()->yield_jump_variable);
    instructions += Drop();
    instructions += LoadLocal(parsed_function()->current_context_var());
    instructions +=
        StoreLocal(TokenPosition::kNoSource, scopes()->yield_context_variable);
    instructions += Drop();
    instructions += BuildExpression();  // read expression.
    instructions += Return(position, new_yield_pos);
  
    // Note: DropTempsInstr serves as an anchor instruction. It will not
    // be linked into the resulting graph.
    DropTempsInstr* anchor = new (Z) DropTempsInstr(0, NULL);
    yield_continuations().Add(YieldContinuation(anchor, CurrentTryIndex()));
  
    Fragment continuation(instructions.entry, anchor);
  
    RELEASE_ASSERT(parsed_function()->function().IsAsyncClosure() ||
                   parsed_function()->function().IsAsyncGenClosure() ||
                   parsed_function()->function().IsSyncGenClosure());
  
    // TODO(43900): Only emit this when needed.
    {
      // If function is {async, async gen, sync yielding} closure it takes three
      // parameters where the second and the third are exception and stack_trace.
      // Check if exception is non-null and rethrow it.
      //
      //   :sync_op(:iterator, [:exception, :stack_trace]) {
      // or:
      //   :async_op(:result, [:exception, :stack_trace]) {
      //     ...
      //     Continuation<index>:
      //       if (:exception != null) rethrow(:exception, :stack_trace);
      //     ...
      //   }
      //
      LocalVariable* exception_var = parsed_function()->ParameterVariable(2);
      LocalVariable* stack_trace_var = parsed_function()->ParameterVariable(3);
      ASSERT(exception_var->name().raw() == Symbols::ExceptionParameter().raw());
      ASSERT(stack_trace_var->name().raw() ==
             Symbols::StackTraceParameter().raw());
  
      TargetEntryInstr* no_error;
      TargetEntryInstr* error;
  
      continuation += LoadLocal(exception_var);
      continuation += BranchIfNull(&no_error, &error);
  
      Fragment rethrow(/*instruction=*/error);
      rethrow += LoadLocal(exception_var);
      rethrow += LoadLocal(stack_trace_var);
  
      rethrow += RethrowException(position, kInvalidTryIndex);
      Drop();
  
      // Set current to the end of the no_error branch.
      continuation = Fragment(/*entry=*/continuation.entry, /*current=*/no_error);
    }
  
    return continuation;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildVariableDeclaration() {
    intptr_t kernel_position_no_tag = ReaderOffset() + data_program_offset_;
    LocalVariable* variable = LookupVariable(kernel_position_no_tag);
  
    VariableDeclarationHelper helper(this);
    helper.ReadUntilExcluding(VariableDeclarationHelper::kType);
    T.BuildType();  // read type.
    bool has_initializer = (ReadTag() != kNothing);
  
    Fragment instructions;
    if (variable->is_late()) {
      // TODO(liama): Treat the field as non-late if the initializer is trivial.
      if (has_initializer) {
        SkipExpression();
      }
      instructions += Constant(Object::sentinel());
    } else if (!has_initializer) {
      instructions += NullConstant();
    } else if (helper.IsConst()) {
      // Read const initializer form current position.
      const Instance& constant_value =
          Instance::ZoneHandle(Z, constant_reader_.ReadConstantExpression());
      variable->SetConstValue(constant_value);
      instructions += Constant(constant_value);
    } else {
      // Initializer
      instructions += BuildExpression();  // read (actual) initializer.
    }
  
    // Use position of equal sign if it exists. If the equal sign does not exist
    // use the position of the identifier.
    TokenPosition debug_position =
        Utils::Maximum(helper.position_, helper.equals_position_);
    if (NeedsDebugStepCheck(stack(), debug_position)) {
      instructions = DebugStepCheck(debug_position) + instructions;
    }
    instructions += StoreLocal(helper.position_, variable);
    instructions += Drop();
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFunctionDeclaration() {
    TokenPosition position = ReadPosition();  // read position.
    intptr_t variable_offset = ReaderOffset() + data_program_offset_;
  
    // read variable declaration.
    VariableDeclarationHelper helper(this);
    helper.ReadUntilExcluding(VariableDeclarationHelper::kEnd);
  
    Fragment instructions = DebugStepCheck(position);
    instructions += BuildFunctionNode(position, helper.name_index_);
    instructions += StoreLocal(position, LookupVariable(variable_offset));
    instructions += Drop();
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFunctionNode(
      TokenPosition parent_position,
      StringIndex name_index) {
    intptr_t offset = ReaderOffset();
  
    FunctionNodeHelper function_node_helper(this);
    function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kTypeParameters);
    TokenPosition position = function_node_helper.position_;
  
    bool declaration = name_index >= 0;
  
    if (declaration) {
      position = parent_position;
    }
    if (!position.IsReal()) {
      // Positions has to be unique in regards to the parent.
      // A non-real at this point is probably -1, we cannot blindly use that
      // as others might use it too. Create a new dummy non-real TokenPosition.
      position = TokenPosition(offset).ToSynthetic();
    }
  
    // The VM has a per-isolate table of functions indexed by the enclosing
    // function and token position.
    Function& function = Function::ZoneHandle(Z);
  
    // NOTE: This is not TokenPosition in the general sense!
    function = I->LookupClosureFunction(parsed_function()->function(), position);
    if (function.IsNull()) {
      for (intptr_t i = 0; i < scopes()->function_scopes.length(); ++i) {
        if (scopes()->function_scopes[i].kernel_offset != offset) {
          continue;
        }
  
        const String* name;
        if (declaration) {
          name = &H.DartSymbolObfuscate(name_index);
        } else {
          name = &Symbols::AnonymousClosure();
        }
        // NOTE: This is not TokenPosition in the general sense!
        if (!closure_owner_.IsNull()) {
          function = Function::NewClosureFunctionWithKind(
              FunctionLayout::kClosureFunction, *name,
              parsed_function()->function(), position, closure_owner_);
        } else {
          function = Function::NewClosureFunction(
              *name, parsed_function()->function(), position);
        }
  
        function.set_is_debuggable(function_node_helper.dart_async_marker_ ==
                                   FunctionNodeHelper::kSync);
        switch (function_node_helper.dart_async_marker_) {
          case FunctionNodeHelper::kSyncStar:
            function.set_modifier(FunctionLayout::kSyncGen);
            break;
          case FunctionNodeHelper::kAsync:
            function.set_modifier(FunctionLayout::kAsync);
            function.set_is_inlinable(!FLAG_causal_async_stacks);
            break;
          case FunctionNodeHelper::kAsyncStar:
            function.set_modifier(FunctionLayout::kAsyncGen);
            function.set_is_inlinable(!FLAG_causal_async_stacks);
            break;
          default:
            // no special modifier
            break;
        }
        function.set_is_generated_body(function_node_helper.async_marker_ ==
                                       FunctionNodeHelper::kSyncYielding);
        // sync* functions contain two nested synthetic functions, the first of
        // which (sync_op_gen) is a regular sync function so we need to manually
        // label it generated:
        if (function.parent_function() != Function::null()) {
          const auto& parent = Function::Handle(function.parent_function());
          if (parent.IsSyncGenerator()) {
            function.set_is_generated_body(true);
          }
        }
        // Note: Is..() methods use the modifiers set above, so order matters.
        if (function.IsAsyncClosure() || function.IsAsyncGenClosure()) {
          function.set_is_inlinable(!FLAG_causal_async_stacks &&
                                    !FLAG_lazy_async_stacks);
        }
  
        function.set_end_token_pos(function_node_helper.end_position_);
        LocalScope* scope = scopes()->function_scopes[i].scope;
        const ContextScope& context_scope = ContextScope::Handle(
            Z, scope->PreserveOuterScope(flow_graph_builder_->context_depth_));
        function.set_context_scope(context_scope);
        function.set_kernel_offset(offset);
        type_translator_.SetupFunctionParameters(Class::Handle(Z), function,
                                                 false,  // is_method
                                                 true,   // is_closure
                                                 &function_node_helper);
        // type_translator_.SetupUnboxingInfoMetadata is not called here at the
        // moment because closures do not have unboxed parameters and return value
        function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
  
        // Finalize function type.
        Type& signature_type = Type::Handle(Z, function.SignatureType());
        signature_type ^= ClassFinalizer::FinalizeType(signature_type);
        function.SetSignatureType(signature_type);
  
        I->AddClosureFunction(function);
        break;
      }
    }
  
    function_node_helper.ReadUntilExcluding(FunctionNodeHelper::kEnd);
  
    Fragment instructions =
        flow_graph_builder_->AllocateClosure(TokenPosition::kNoSource, function);
    LocalVariable* closure = MakeTemporary();
  
    // The function signature can have uninstantiated class type parameters.
    if (!function.HasInstantiatedSignature(kCurrentClass)) {
      instructions += LoadLocal(closure);
      instructions += LoadInstantiatorTypeArguments();
      instructions += flow_graph_builder_->StoreInstanceField(
          TokenPosition::kNoSource, Slot::Closure_instantiator_type_arguments(),
          StoreInstanceFieldInstr::Kind::kInitializing);
    }
  
    // TODO(30455): We only need to save these if the closure uses any captured
    // type parameters.
    instructions += LoadLocal(closure);
    instructions += LoadFunctionTypeArguments();
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_function_type_arguments(),
        StoreInstanceFieldInstr::Kind::kInitializing);
  
    if (function.IsGeneric()) {
      // Only generic functions need to have properly initialized
      // delayed_type_arguments.
      instructions += LoadLocal(closure);
      instructions += Constant(Object::empty_type_arguments());
      instructions += flow_graph_builder_->StoreInstanceField(
          TokenPosition::kNoSource, Slot::Closure_delayed_type_arguments(),
          StoreInstanceFieldInstr::Kind::kInitializing);
    }
  
    // Store the function and the context in the closure.
    instructions += LoadLocal(closure);
    instructions += Constant(function);
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_function(),
        StoreInstanceFieldInstr::Kind::kInitializing);
  
    instructions += LoadLocal(closure);
    instructions += LoadLocal(parsed_function()->current_context_var());
    instructions += flow_graph_builder_->StoreInstanceField(
        TokenPosition::kNoSource, Slot::Closure_context(),
        StoreInstanceFieldInstr::Kind::kInitializing);
  
    return instructions;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFfiAsFunctionInternal() {
    const intptr_t argc = ReadUInt();               // read argument count.
    ASSERT(argc == 1);                              // pointer
    const intptr_t list_length = ReadListLength();  // read types list length.
    ASSERT(list_length == 2);  // dart signature, then native signature
    const TypeArguments& type_arguments =
        T.BuildTypeArguments(list_length);  // read types.
    Fragment code;
    const intptr_t positional_count =
        ReadListLength();  // read positional argument count
    ASSERT(positional_count == 1);
    code += BuildExpression();  // build first positional argument (pointer)
    const intptr_t named_args_len =
        ReadListLength();  // skip (empty) named arguments list
    ASSERT(named_args_len == 0);
    code += B->BuildFfiAsFunctionInternalCall(type_arguments);
    return code;
  }
  
  Fragment StreamingFlowGraphBuilder::BuildFfiNativeCallbackFunction() {
    // The call-site must look like this (guaranteed by the FE which inserts it):
    //
    //   _nativeCallbackFunction<NativeSignatureType>(target, exceptionalReturn)
    //
    // The FE also guarantees that all three arguments are constants.
  
    const intptr_t argc = ReadUInt();  // read argument count
    ASSERT(argc == 2);                 // target, exceptionalReturn
  
    const intptr_t list_length = ReadListLength();  // read types list length
    ASSERT(list_length == 1);                       // native signature
    const TypeArguments& type_arguments =
        T.BuildTypeArguments(list_length);  // read types.
    ASSERT(type_arguments.Length() == 1 && type_arguments.IsInstantiated());
    const Function& native_sig = Function::Handle(
        Z, Type::CheckedHandle(Z, type_arguments.TypeAt(0)).signature());
  
    Fragment code;
    const intptr_t positional_count =
        ReadListLength();  // read positional argument count
    ASSERT(positional_count == 2);
  
    // Read target expression and extract the target function.
    code += BuildExpression();  // build first positional argument (target)
    Definition* target_def = B->Peek();
    ASSERT(target_def->IsConstant());
    const Closure& target_closure =
        Closure::Cast(target_def->AsConstant()->value());
    ASSERT(!target_closure.IsNull());
    Function& target = Function::Handle(Z, target_closure.function());
    ASSERT(!target.IsNull() && target.IsImplicitClosureFunction());
    target = target.parent_function();
    code += Drop();
  
    // Build second positional argument (exceptionalReturn).
    code += BuildExpression();
    Definition* exceptional_return_def = B->Peek();
    ASSERT(exceptional_return_def->IsConstant());
    const Instance& exceptional_return =
        Instance::Cast(exceptional_return_def->AsConstant()->value());
    code += Drop();
  
    const intptr_t named_args_len =
        ReadListLength();  // skip (empty) named arguments list
    ASSERT(named_args_len == 0);
  
    const Function& result =
        Function::ZoneHandle(Z, compiler::ffi::NativeCallbackFunction(
                                    native_sig, target, exceptional_return));
    code += Constant(result);
  
    auto& ffi_callback_functions = GrowableObjectArray::Handle(Z);
    ffi_callback_functions ^= I->object_store()->ffi_callback_functions();
    if (ffi_callback_functions.IsNull()) {
      ffi_callback_functions ^= GrowableObjectArray::New();
      I->object_store()->set_ffi_callback_functions(ffi_callback_functions);
    }
    ffi_callback_functions.Add(result);
  
    return code;
  }
  
  }  // namespace kernel
  }  // namespace dart
  