// Copyright (c) 2012, 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/parser.h"
#include "vm/flags.h"

#if !defined(DART_PRECOMPILED_RUNTIME)

#include "lib/invocation_mirror.h"
#include "platform/utils.h"
#include "vm/bit_vector.h"
#include "vm/bootstrap.h"
#include "vm/class_finalizer.h"
#include "vm/compiler/aot/precompiler.h"
#include "vm/compiler/backend/il_printer.h"
#include "vm/compiler/frontend/scope_builder.h"
#include "vm/compiler/jit/compiler.h"
#include "vm/dart_api_impl.h"
#include "vm/dart_entry.h"
#include "vm/growable_array.h"
#include "vm/handles.h"
#include "vm/hash_table.h"
#include "vm/isolate.h"
#include "vm/longjump.h"
#include "vm/native_arguments.h"
#include "vm/native_entry.h"
#include "vm/object.h"
#include "vm/object_store.h"
#include "vm/os.h"
#include "vm/resolver.h"
#include "vm/scopes.h"
#include "vm/stack_frame.h"
#include "vm/symbols.h"
#include "vm/tags.h"
#include "vm/timeline.h"
#include "vm/zone.h"

namespace dart {

// Quick access to the current thread, isolate and zone.
#define T (thread())
#define I (isolate())
#define Z (zone())

ParsedFunction::ParsedFunction(Thread* thread, const Function& function)
    : thread_(thread),
      function_(function),
      scope_(nullptr),
      regexp_compile_data_(nullptr),
      function_type_arguments_(nullptr),
      parent_type_arguments_(nullptr),
      current_context_var_(nullptr),
      arg_desc_var_(nullptr),
      expression_temp_var_(nullptr),
      entry_points_temp_var_(nullptr),
      dynamic_closure_call_vars_(nullptr),
      guarded_fields_(),
      default_parameter_values_(nullptr),
      raw_type_arguments_var_(nullptr),
      first_parameter_index_(),
      num_stack_locals_(0),
      have_seen_await_expr_(false),
      kernel_scopes_(nullptr) {
  DEBUG_ASSERT(function.IsNotTemporaryScopedHandle());
  // Every function has a local variable for the current context.
  LocalVariable* temp = new (zone())
      LocalVariable(function.token_pos(), function.token_pos(),
                    Symbols::CurrentContextVar(), Object::dynamic_type());
  current_context_var_ = temp;

  if (function.PrologueNeedsArgumentsDescriptor()) {
    arg_desc_var_ = new (zone())
        LocalVariable(TokenPosition::kNoSource, TokenPosition::kNoSource,
                      Symbols::ArgDescVar(), Object::dynamic_type());
  }

  // The code generated by the prologue builder for loading optional arguments
  // requires the expression temporary variable.
  if (function.HasOptionalParameters()) {
    EnsureExpressionTemp();
  }
}

void ParsedFunction::AddToGuardedFields(const Field* field) const {
  if ((field->guarded_cid() == kDynamicCid) ||
      (field->guarded_cid() == kIllegalCid)) {
    return;
  }

  const Field** other = guarded_fields_.Lookup(field);
  if (other != nullptr) {
    ASSERT(field->Original() == (*other)->Original());
    // Abort background compilation early if the guarded state of this field
    // has changed during compilation. We will not be able to commit
    // the resulting code anyway.
    if (Compiler::IsBackgroundCompilation()) {
      if (!(*other)->IsConsistentWith(*field)) {
        Compiler::AbortBackgroundCompilation(
            DeoptId::kNone, "Field's guarded state changed during compilation");
      }
    }
    return;
  }

  // Note: the list of guarded fields must contain copies during optimizing
  // compilation because we will look at their guarded_cid when copying
  // the array of guarded fields from callee into the caller during
  // inlining.
  ASSERT(field->IsOriginal() ==
         !CompilerState::Current().should_clone_fields());
  guarded_fields_.Insert(&Field::ZoneHandle(Z, field->ptr()));
}

void ParsedFunction::Bailout(const char* origin, const char* reason) const {
  Report::MessageF(Report::kBailout, Script::Handle(function_.script()),
                   function_.token_pos(), Report::AtLocation,
                   "%s Bailout in %s: %s", origin,
                   String::Handle(function_.name()).ToCString(), reason);
  UNREACHABLE();
}

kernel::ScopeBuildingResult* ParsedFunction::EnsureKernelScopes() {
  if (kernel_scopes_ == nullptr) {
    kernel::ScopeBuilder builder(this);
    kernel_scopes_ = builder.BuildScopes();
  }
  return kernel_scopes_;
}

LocalVariable* ParsedFunction::EnsureExpressionTemp() {
  if (!has_expression_temp_var()) {
    LocalVariable* temp =
        new (Z) LocalVariable(function_.token_pos(), function_.token_pos(),
                              Symbols::ExprTemp(), Object::dynamic_type());
    ASSERT(temp != nullptr);
    set_expression_temp_var(temp);
  }
  ASSERT(has_expression_temp_var());
  return expression_temp_var();
}

LocalVariable* ParsedFunction::EnsureEntryPointsTemp() {
  if (!has_entry_points_temp_var()) {
    LocalVariable* temp = new (Z)
        LocalVariable(function_.token_pos(), function_.token_pos(),
                      Symbols::EntryPointsTemp(), Object::dynamic_type());
    ASSERT(temp != nullptr);
    set_entry_points_temp_var(temp);
  }
  ASSERT(has_entry_points_temp_var());
  return entry_points_temp_var();
}

void ParsedFunction::SetRegExpCompileData(
    RegExpCompileData* regexp_compile_data) {
  ASSERT(regexp_compile_data_ == nullptr);
  ASSERT(regexp_compile_data != nullptr);
  regexp_compile_data_ = regexp_compile_data;
}

void ParsedFunction::AllocateVariables() {
  ASSERT(!function().IsIrregexpFunction());
  LocalScope* scope = this->scope();
  const intptr_t num_fixed_params = function().num_fixed_parameters();
  const intptr_t num_opt_params = function().NumOptionalParameters();
  const intptr_t num_params = num_fixed_params + num_opt_params;
  const bool copy_parameters = function().MakesCopyOfParameters();

  // Before we start allocating indices to variables, we'll setup the
  // parameters array, which can be used to access the raw parameters (i.e. not
  // the potentially variables which are in the context)

  raw_parameters_ = new (Z) ZoneGrowableArray<LocalVariable*>(Z, num_params);
  for (intptr_t param = 0; param < num_params; ++param) {
    LocalVariable* variable = ParameterVariable(param);
    LocalVariable* raw_parameter = variable;
    if (variable->is_captured()) {
      String& tmp = String::ZoneHandle(Z);
      tmp = Symbols::FromConcat(T, Symbols::OriginalParam(), variable->name());

      RELEASE_ASSERT(scope->LocalLookupVariable(
                         tmp, LocalVariable::kNoKernelOffset) == nullptr);
      raw_parameter = new LocalVariable(
          variable->declaration_token_pos(), variable->token_pos(), tmp,
          variable->static_type(), LocalVariable::kNoKernelOffset,
          variable->inferred_type(), variable->inferred_arg_type(),
          variable->inferred_arg_value());
      raw_parameter->set_annotations_offset(variable->annotations_offset());
      if (variable->is_explicit_covariant_parameter()) {
        raw_parameter->set_is_explicit_covariant_parameter();
      }
      if (variable->needs_covariant_check_in_method()) {
        raw_parameter->set_needs_covariant_check_in_method();
      }
      raw_parameter->set_type_check_mode(variable->type_check_mode());
      if (copy_parameters) {
        bool ok = scope->AddVariable(raw_parameter);
        ASSERT(ok);
      } else {
        raw_parameter->set_index(
            VariableIndex(function().NumParameters() - param));
      }
    }
    raw_parameters_->Add(raw_parameter);
  }
  if (function_type_arguments_ != nullptr) {
    LocalVariable* raw_type_args_parameter = function_type_arguments_;
    if (function_type_arguments_->is_captured()) {
      String& tmp = String::ZoneHandle(Z);
      tmp = Symbols::FromConcat(T, Symbols::OriginalParam(),
                                function_type_arguments_->name());

      ASSERT(scope->LocalLookupVariable(tmp, LocalVariable::kNoKernelOffset) ==
             nullptr);
      raw_type_args_parameter =
          new LocalVariable(function_type_arguments_->declaration_token_pos(),
                            function_type_arguments_->token_pos(), tmp,
                            function_type_arguments_->static_type(),
                            LocalVariable::kNoKernelOffset);
      bool ok = scope->AddVariable(raw_type_args_parameter);
      ASSERT(ok);
    }
    raw_type_arguments_var_ = raw_type_args_parameter;
  }

  // The copy parameters implementation will still write to local variables
  // which we assign indices as with the old CopyParams implementation.
  VariableIndex first_local_index;
  {
    // Compute start indices to parameters and locals, and the number of
    // parameters to copy.
    if (!copy_parameters) {
      ASSERT(suspend_state_var() == nullptr);
      first_parameter_index_ = VariableIndex(num_params);
      first_local_index = VariableIndex(0);
    } else {
      // :suspend_state variable is inserted at the fixed slot
      // before the copied parameters.
      const intptr_t reserved_var_slot_count =
          (suspend_state_var() != nullptr) ? 1 : 0;
      first_parameter_index_ = VariableIndex(-reserved_var_slot_count);
      first_local_index =
          VariableIndex(first_parameter_index_.value() - num_params);
    }
  }

  // Allocate parameters and local variables, either in the local frame or
  // in the context(s).
  bool found_captured_variables = false;
  VariableIndex next_free_index = scope->AllocateVariables(
      function(), first_parameter_index_, num_params, first_local_index,
      nullptr, &found_captured_variables);

  num_stack_locals_ = -next_free_index.value();
}

void ParsedFunction::SetGenericCovariantImplParameters(
    const BitVector* generic_covariant_impl_parameters) {
  ASSERT(generic_covariant_impl_parameters_ == nullptr);
  ASSERT(generic_covariant_impl_parameters->length() ==
         function_.NumParameters());
  generic_covariant_impl_parameters_ = generic_covariant_impl_parameters;
}

bool ParsedFunction::IsGenericCovariantImplParameter(intptr_t i) const {
  ASSERT(generic_covariant_impl_parameters_ != nullptr);
  ASSERT((i >= 0) && (i < function_.NumParameters()));
  return generic_covariant_impl_parameters_->Contains(i);
}

ParsedFunction::DynamicClosureCallVars*
ParsedFunction::EnsureDynamicClosureCallVars() {
  ASSERT(function().IsDynamicClosureCallDispatcher());
  if (dynamic_closure_call_vars_ != nullptr) return dynamic_closure_call_vars_;
  const auto& saved_args_desc =
      Array::Handle(zone(), function().saved_args_desc());
  const ArgumentsDescriptor descriptor(saved_args_desc);

  dynamic_closure_call_vars_ =
      new (zone()) DynamicClosureCallVars(zone(), descriptor.NamedCount());

  auto const pos = function().token_pos();
  const auto& type_Dynamic = Object::dynamic_type();
  const auto& type_Function =
      Type::ZoneHandle(zone(), Type::DartFunctionType());
  const auto& type_Smi = Type::ZoneHandle(zone(), Type::SmiType());
#define INIT_FIELD(Name, TypeName, Symbol)                                     \
  dynamic_closure_call_vars_->Name = new (zone()) LocalVariable(               \
      pos, pos, Symbols::DynamicCall##Symbol##Var(), type_##TypeName);
  FOR_EACH_DYNAMIC_CLOSURE_CALL_VARIABLE(INIT_FIELD);
#undef INIT_FIELD

  for (intptr_t i = 0; i < descriptor.NamedCount(); i++) {
    auto const name = OS::SCreate(
        zone(), ":dyn_call_named_argument_%" Pd "_parameter_index", i);
    auto const var = new (zone()) LocalVariable(
        pos, pos, String::ZoneHandle(zone(), Symbols::New(thread(), name)),
        type_Smi);
    dynamic_closure_call_vars_->named_argument_parameter_indices.Add(var);
  }

  return dynamic_closure_call_vars_;
}

}  // namespace dart

#endif  // !defined(DART_PRECOMPILED_RUNTIME)
