// 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.

#ifndef RUNTIME_VM_NATIVE_ARGUMENTS_H_
#define RUNTIME_VM_NATIVE_ARGUMENTS_H_

#include "platform/assert.h"
#include "platform/memory_sanitizer.h"
#include "vm/globals.h"
#include "vm/simulator.h"
#include "vm/stub_code.h"

namespace dart {

// Forward declarations.
class BootstrapNatives;
class Object;
class Simulator;
class Thread;

#if defined(TESTING) || defined(DEBUG)

#if defined(DART_INCLUDE_SIMULATOR)
// The simulator/isolate might not exist yet for an async FFI callback, so do
// not check. An equivalent check exists in the simulator itself.
#define CHECK_STACK_ALIGNMENT                                                  \
  {                                                                            \
  }
#elif defined(DART_HOST_OS_WINDOWS)
// The compiler may dynamically align the stack on Windows, so do not check.
#define CHECK_STACK_ALIGNMENT                                                  \
  {                                                                            \
  }
#else
#define CHECK_STACK_ALIGNMENT                                                  \
  {                                                                            \
    ASSERT(Utils::IsAligned(OSThread::GetCurrentStackPointer(),                \
                            OS::ActivationFrameAlignment()));                  \
  }
#endif

#define DEOPTIMIZE_ALOT                                                        \
  if (FLAG_deoptimize_alot) {                                                  \
    DeoptimizeFunctionsOnStack();                                              \
  }

#else

#define CHECK_STACK_ALIGNMENT                                                  \
  {                                                                            \
  }
#define DEOPTIMIZE_ALOT                                                        \
  {                                                                            \
  }

#endif

// Class NativeArguments is used to access arguments passed in from
// generated dart code to a runtime function or a dart library native
// function. It is also used to set the return value if any at the slot
// reserved for return values.
// All runtime function/dart library native functions have the
// following signature:
//   void function_name(NativeArguments arguments);
// Inside the function, arguments are accessed as follows:
//   const Instance& arg0 = Instance::CheckedHandle(arguments.NativeArgAt(0));
//   const Smi& arg1 = Smi::CheckedHandle(arguments.NativeArgAt(1));
// If the function is generic, type arguments are accessed as follows:
//   const TypeArguments& type_args =
//       TypeArguments::Handle(arguments.NativeTypeArgs());
// The return value is set as follows:
//   arguments.SetReturn(result);
// NOTE: Since we pass 'this' as a pass-by-value argument in the stubs we don't
// have DISALLOW_COPY_AND_ASSIGN in the class definition and do not make it a
// subclass of ValueObject.
class NativeArguments {
 private:
  using ArgcBits = BitField<intptr_t, int32_t, 0, 24>;
  using GenericFunctionBit = BitField<intptr_t, bool, ArgcBits::kNextBit>;
  using ReverseArgOrderBit =
      BitField<intptr_t, bool, GenericFunctionBit::kNextBit>;

 public:
  Thread* thread() const { return thread_; }

  // Includes type arguments vector.
  int ArgCount() const { return ArgcBits::decode(argc_tag_); }

  ObjectPtr ArgAt(int index) const {
    ASSERT((index >= 0) && (index < ArgCount()));
    ObjectPtr* arg_ptr =
        &(argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index]);
    // Tell MemorySanitizer the ObjectPtr was initialized (by generated code).
    MSAN_UNPOISON(arg_ptr, kWordSize);
    return *arg_ptr;
  }

  void SetArgAt(int index, const Object& value) const {
    ASSERT(thread_->execution_state() == Thread::kThreadInVM);
    ASSERT((index >= 0) && (index < ArgCount()));
    argv_[ReverseArgOrderBit::decode(argc_tag_) ? index : -index] = value.ptr();
  }

  // Does not include hidden type arguments vector.
  int NativeArgCount() const { return ArgCount() - NumHiddenArgs(); }

  ObjectPtr NativeArg0() const { return ArgAt(NumHiddenArgs()); }

  ObjectPtr NativeArgAt(int index) const {
    ASSERT((index >= 0) && (index < NativeArgCount()));
    return ArgAt(index + NumHiddenArgs());
  }

  TypeArgumentsPtr NativeTypeArgs() const {
    ASSERT(ToGenericFunction());
    return TypeArguments::RawCast(ArgAt(0));
  }

  int NativeTypeArgCount() const {
    if (ToGenericFunction()) {
      TypeArguments& type_args = TypeArguments::Handle(NativeTypeArgs());
      if (type_args.IsNull()) {
        // null vector represents infinite list of dynamics
        return INT_MAX;
      }
      return type_args.Length();
    }
    return 0;
  }

  AbstractTypePtr NativeTypeArgAt(int index) const {
    ASSERT((index >= 0) && (index < NativeTypeArgCount()));
    TypeArguments& type_args = TypeArguments::Handle(NativeTypeArgs());
    if (type_args.IsNull()) {
      // null vector represents infinite list of dynamics
      return Type::dynamic_type().ptr();
    }
    return type_args.TypeAt(index);
  }

  void SetReturn(const Object& value) const {
    ASSERT(thread_->execution_state() == Thread::kThreadInVM);
    *retval_ = value.ptr();
  }

  ObjectPtr ReturnValue() const {
    // Tell MemorySanitizer the retval_ was initialized (by generated code).
    MSAN_UNPOISON(retval_, kWordSize);
    return *retval_;
  }

  uword GetCallerSP() const {
    return reinterpret_cast<uword>(
        retval_ + (ReverseArgOrderBit::decode(argc_tag_) ? 0 : 1));
  }

  static intptr_t thread_offset() {
    return OFFSET_OF(NativeArguments, thread_);
  }
  static intptr_t argc_tag_offset() {
    return OFFSET_OF(NativeArguments, argc_tag_);
  }
  static intptr_t argv_offset() { return OFFSET_OF(NativeArguments, argv_); }
  static intptr_t retval_offset() {
    return OFFSET_OF(NativeArguments, retval_);
  }

  static intptr_t ParameterCountForResolution(const Function& function) {
    ASSERT(function.is_old_native());
    ASSERT(!function.IsGenerativeConstructor());  // Not supported.
    ASSERT(!function.IsClosureFunction());        // Not supported.
    return function.NumParameters();
  }

  static int ComputeArgcTag(const Function& function) {
    ASSERT(function.is_old_native());
    ASSERT(!function.IsGenerativeConstructor());  // Not supported.
    ASSERT(!function.IsClosureFunction());        // Not supported.
    bool is_generic = function.IsGeneric();
    return ArgcBits::encode(function.NumParameters() + (is_generic ? 1 : 0)) |
           GenericFunctionBit::encode(is_generic);
  }

 private:
  friend class Api;
  friend class Interpreter;
  friend class NativeEntry;
  friend class Simulator;

#if defined(DART_DYNAMIC_MODULES)
  NativeArguments(Thread* thread,
                  int argc_tag,
                  ObjectPtr* argv,
                  ObjectPtr* retval)
      : thread_(thread),
        argc_tag_(ReverseArgOrderBit::update(true, argc_tag)),
        argv_(argv),
        retval_(retval) {}
  NativeArguments() = default;
#endif  // defined(DART_DYNAMIC_MODULES)

  // Since this function is passed an ObjectPtr directly, we need to be
  // exceedingly careful when we use it.  If there are any other side
  // effects in the statement that may cause GC, it could lead to
  // bugs.
  void SetReturnUnsafe(ObjectPtr value) const {
    ASSERT(thread_->execution_state() == Thread::kThreadInVM);
    *retval_ = value;
  }

  // Returns true if the arguments are those of a generic function call.
  bool ToGenericFunction() const {
    return GenericFunctionBit::decode(argc_tag_);
  }

  int NumHiddenArgs() const {
    return GenericFunctionBit::decode(argc_tag_) ? 1 : 0;
  }

  Thread* thread_;     // Current thread pointer.
  intptr_t argc_tag_;  // Encodes argument count and invoked native call type.
  ObjectPtr* argv_;    // Pointer to an array of arguments to runtime call.
  ObjectPtr* retval_;  // Pointer to the return value area.
};

}  // namespace dart

#endif  // RUNTIME_VM_NATIVE_ARGUMENTS_H_
