// 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 VM_STUB_CODE_H_
#define VM_STUB_CODE_H_

#include "vm/allocation.h"
#include "vm/assembler.h"

namespace dart {

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


// List of stubs created in the VM isolate, these stubs are shared by different
// isolates running in this dart process.
#define VM_STUB_CODE_LIST(V)                                                   \
  V(DartCallToRuntime)                                                         \
  V(StubCallToRuntime)                                                         \
  V(CallNativeCFunction)                                                       \
  V(AllocateArray)                                                             \
  V(CallNoSuchMethodFunction)                                                  \
  V(MegamorphicLookup)                                                         \
  V(CallInstanceFunction)                                                      \
  V(CallStaticFunction)                                                        \
  V(CallClosureFunction)                                                       \
  V(StackOverflow)                                                             \
  V(OptimizeInvokedFunction)                                                   \
  V(FixCallersTarget)                                                          \
  V(Deoptimize)                                                                \

// TODO(regis): Is it OK for the stubs above to refer to Object::null()?

// List of stubs created per isolate, these stubs could potentially contain
// embedded objects and hence cannot be shared across isolates.
#define STUB_CODE_LIST(V)                                                      \
  V(InvokeDartCode)                                                            \
  V(AllocateContext)                                                           \


// class StubEntry is used to describe stub methods generated in dart to
// abstract out common code executed from generated dart code.
class StubEntry {
 public:
  StubEntry(const char* name, const Code& code);
  ~StubEntry() {}

  const ExternalLabel& label() const { return label_; }
  uword EntryPoint() const { return entry_point_; }
  intptr_t Size() const { return size_; }

  // Visit all object pointers.
  void VisitObjectPointers(ObjectPointerVisitor* visitor);

 private:
  RawCode* code_;
  uword entry_point_;
  intptr_t size_;
  ExternalLabel label_;

  DISALLOW_COPY_AND_ASSIGN(StubEntry);
};


// class StubCode is used to maintain the lifecycle of stubs.
class StubCode {
 public:
  StubCode()
    :
#define STUB_CODE_INITIALIZER(name) \
        name##_entry_(NULL),
  STUB_CODE_LIST(STUB_CODE_INITIALIZER)
        dummy_(NULL) {}
  ~StubCode();

  void GenerateFor(Isolate* isolate);

  // 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 InitOnce();

  // Generate all stubs which are generated on a per isolate basis as they
  // have embedded objects which are isolate specific.
  static void Init(Isolate* isolate);

  static void VisitObjectPointers(ObjectPointerVisitor* visitor);

  // Check if specified pc is in the dart invocation stub used for
  // transitioning into dart code.
  static bool InInvocationStub(uword pc);
  // Check if specified pc is in CallInstanceFunction stub.
  static bool InCallInstanceFunctionStubCode(uword pc);
  // Check if specified pc is in StubCallToRuntime stub.
  static bool InStubCallToRuntimeStubCode(uword pc);

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

  // Define the shared stub code accessors.
#define STUB_CODE_ACCESSOR(name)                                               \
  static StubEntry* name##_entry() {                                           \
    return name##_entry_;                                                      \
  }                                                                            \
  static const ExternalLabel& name##Label() {                                  \
    return name##_entry()->label();                                            \
  }                                                                            \
  static uword name##EntryPoint() {                                            \
    return name##_entry()->EntryPoint();                                       \
  }                                                                            \
  static intptr_t name##Size() {                                               \
    return name##_entry()->Size();                                             \
  }
  VM_STUB_CODE_LIST(STUB_CODE_ACCESSOR);
#undef STUB_CODE_ACCESSOR

  // Define the per-isolate stub code accessors.
#define STUB_CODE_ACCESSOR(name)                                               \
  static StubEntry* name##_entry() {                                           \
    return Isolate::Current()->stub_code()->name##_entry_;                     \
  }                                                                            \
  static uword name##EntryPoint() {                                            \
    return name##_entry()->EntryPoint();                                       \
  }                                                                            \
  static intptr_t name##Size() {                                               \
    return name##_entry()->Size();                                             \
  }
  STUB_CODE_LIST(STUB_CODE_ACCESSOR);
#undef STUB_CODE_ACCESSOR

  static RawCode* GetAllocationStubForClass(const Class& cls);
  static RawCode* GetAllocationStubForClosure(const Function& func);
  static RawCode* GetAllocationStubForStaticImplicitClosure(
      const Function& func);

 private:
  static const int kStubCodeSize = 4 * KB;

#define STUB_CODE_GENERATE(name)                                               \
  static void Generate##name##Stub(Assembler* assembler);
  VM_STUB_CODE_LIST(STUB_CODE_GENERATE);
  STUB_CODE_LIST(STUB_CODE_GENERATE);
#undef STUB_CODE_GENERATE

#define STUB_CODE_ENTRY(name)                                                  \
  static StubEntry* name##_entry_;
  VM_STUB_CODE_LIST(STUB_CODE_ENTRY);
#undef STUB_CODE_ENTRY

#define STUB_CODE_ENTRY(name)                                                  \
  StubEntry* name##_entry_;
  STUB_CODE_LIST(STUB_CODE_ENTRY);
#undef STUB_CODE_ENTRY
  // This dummy field is needed so that we can intialize the stubs from a macro.
  void* dummy_;

  // Generate the stub and finalize the generated code into the stub
  // code executable area.
  static RawCode* Generate(const char* name,
                           void (*GenerateStub)(Assembler* assembler));

  static void GenerateAllocationStubForClass(Assembler* assembler,
                                             const Class& cls);
  static void GenerateAllocationStubForClosure(Assembler* assembler,
                                               const Function& func);
  static void GenerateAllocationStubForStaticImplicitClosure(
      Assembler* assembler, const Function& func);
};

}  // namespace dart

#endif  // VM_STUB_CODE_H_
