// Copyright (c) 2011, 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_STUB_CODE_H_
#define RUNTIME_VM_STUB_CODE_H_

#include <functional>

#include "vm/allocation.h"
#include "vm/compiler/runtime_api.h"
#include "vm/object.h"
#include "vm/stub_code_list.h"

#if !defined(DART_PRECOMPILED_RUNTIME)
#include "vm/compiler/assembler/assembler.h"
#include "vm/compiler/stub_code_compiler.h"
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

namespace dart {

// Forward declarations.
class Code;
class Isolate;
class ObjectPointerVisitor;
class Thread;

// Is it permitted for the stubs above to refer to Object::null(), which is
// allocated in the VM isolate and shared across all isolates.
// However, in cases where a simple GC-safe placeholder is needed on the stack,
// using Smi 0 instead of Object::null() is slightly more efficient, since a Smi
// does not require relocation.

// class StubCode is used to maintain the lifecycle of stubs.
class StubCode : public AllStatic {
 public:
  // Generate all stubs which are shared across all isolates, this is done
  // only once and the stub code resides in the vm_isolate heap.
  static void Init();

  static void Cleanup();

  // Check if specified pc is in the dart invocation stub used for
  // transitioning into dart code.
  static bool InInvocationStub(Thread* T,
                               uword pc,
                               bool is_interpreted_frame = false);

  // Check if the specified pc is in the jump to frame stub.
  static bool InJumpToFrameStub(Thread* T, uword pc);

  // Returns nullptr if no stub found.
  static const char* NameOfStub(uword entry_point);

  // Callback is called for each stub until it returns false.
  static void ForEachStub(
      const std::function<bool(const char*, uword)>& callback);

  static constexpr const char* StubNames[] = {
#define STUB_CODE_NAME(name) #name,
      VM_STUB_CODE_LIST(STUB_CODE_NAME)
#undef STUB_CODE_NAME
  };

// Define the shared stub code accessors.
#define STUB_CODE_ACCESSOR(name)                                               \
  static const Code& name() { return Roots::stub_handle(k##name##Index); }
  VM_STUB_CODE_LIST(STUB_CODE_ACCESSOR);
#undef STUB_CODE_ACCESSOR

#if !defined(DART_PRECOMPILED_RUNTIME)
  static const Code& SubtypeTestCacheStubForUsedInputs(intptr_t i) {
    switch (i) {
      case 1:
        return StubCode::Subtype1TestCache();
      case 2:
        return StubCode::Subtype2TestCache();
      case 3:
        return StubCode::Subtype3TestCache();
      case 4:
        return StubCode::Subtype4TestCache();
      case 6:
        return StubCode::Subtype6TestCache();
      case 7:
        return StubCode::Subtype7TestCache();
      default:
        UNREACHABLE();
        return StubCode::Subtype7TestCache();
    }
  }
  static CodePtr GetAllocationStubForClass(const Class& cls);
  static CodePtr GetAllocationStubForTypedData(classid_t class_id);
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

#if !defined(DART_PRECOMPILED_RUNTIME)
  // Generate the stub and finalize the generated code into the stub
  // code executable area.
  static CodePtr Generate(const char* name,
                          compiler::ObjectPoolBuilder* object_pool_builder,
                          void (compiler::StubCodeCompiler::*GenerateStub)());
#endif  // !defined(DART_PRECOMPILED_RUNTIME)

  static const Code& UnoptimizedStaticCallEntry(intptr_t num_args_tested);

  static const char* NameAt(intptr_t index) { return StubNames[index]; }

  static const Code& EntryAt(intptr_t index) {
    return Roots::stub_handle(index);
  }
  static void EntryAtPut(intptr_t index, CodePtr code) {
    Roots::stub_handle(index).initRO(code);
  }
  static intptr_t NumEntries() { return kNumStubEntries; }

 private:
  friend class MegamorphicCacheTable;

  enum {
#define STUB_CODE_ENTRY(name) k##name##Index,
    VM_STUB_CODE_LIST(STUB_CODE_ENTRY)
#undef STUB_CODE_ENTRY
        kNumStubEntries
  };
};

}  // namespace dart

#endif  // RUNTIME_VM_STUB_CODE_H_
