/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef vm_GeckoProfiler_inl_h
#define vm_GeckoProfiler_inl_h

#include "vm/GeckoProfiler.h"

#include "js/ProfilingStack.h"
#include "vm/JSContext.h"
#include "vm/Realm.h"
#include "vm/Runtime.h"

namespace js {

inline void GeckoProfilerThread::updatePC(JSContext* cx, JSScript* script,
                                          jsbytecode* pc) {
  if (!cx->runtime()->geckoProfiler().enabled()) {
    return;
  }

  uint32_t sp = profilingStack_->stackPointer;
  if (sp - 1 < profilingStack_->stackCapacity()) {
    MOZ_ASSERT(sp > 0);
    MOZ_ASSERT(profilingStack_->frames[sp - 1].rawScript() == script);
    profilingStack_->frames[sp - 1].setPC(pc);
  }
}

MOZ_ALWAYS_INLINE
GeckoProfilerEntryMarker::GeckoProfilerEntryMarker(JSContext* cx,
                                                   JSScript* script)
    : profiler_(&cx->geckoProfiler()) {
  if (MOZ_LIKELY(!profiler_->infraInstalled())) {
    profiler_ = nullptr;
#ifdef DEBUG
    spBefore_ = 0;
#endif
    return;
  }
#ifdef DEBUG
  spBefore_ = profiler_->stackPointer();
#endif

  // Push an sp marker frame so the profiler can correctly order JS and native
  // stacks.
  profiler_->profilingStack_->pushSpMarkerFrame(this);

  profiler_->profilingStack_->pushJsFrame(
      "js::RunScript",
      /* dynamicString = */ nullptr, script, script->code(),
      script->realm()->creationOptions().profilerRealmID());
}

MOZ_ALWAYS_INLINE
GeckoProfilerEntryMarker::~GeckoProfilerEntryMarker() {
  if (MOZ_LIKELY(profiler_ == nullptr)) {
    return;
  }

  profiler_->profilingStack_->pop();  // the JS frame
  profiler_->profilingStack_->pop();  // the SP_MARKER frame
  MOZ_ASSERT(spBefore_ == profiler_->stackPointer());
}

MOZ_ALWAYS_INLINE
AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(
    JSContext* cx, const char* label, const char* dynamicString,
    JS::ProfilingCategoryPair categoryPair, uint32_t flags) {
  profilingStack_ = GetContextProfilingStackIfEnabled(cx);
  if (MOZ_LIKELY(!profilingStack_)) {
#ifdef DEBUG
    profiler_ = nullptr;
    spBefore_ = 0;
#endif
    return;
  }

#ifdef DEBUG
  profiler_ = &cx->geckoProfiler();
  spBefore_ = profiler_->stackPointer();
#endif

  profilingStack_->pushLabelFrame(label, dynamicString,
                                  /* sp = */ this, categoryPair, flags);
}

MOZ_ALWAYS_INLINE
AutoGeckoProfilerEntry::~AutoGeckoProfilerEntry() {
  if (MOZ_LIKELY(!profilingStack_)) {
    return;
  }

  profilingStack_->pop();
  MOZ_ASSERT(spBefore_ == profiler_->stackPointer());
}

MOZ_ALWAYS_INLINE
AutoGeckoProfilerEntry::AutoGeckoProfilerEntry(
    JSContext* cx, const char* label, JS::ProfilingCategoryPair categoryPair,
    uint32_t flags)
    : AutoGeckoProfilerEntry(cx, label, /* dynamicString */ nullptr,
                             categoryPair, flags) {}

MOZ_ALWAYS_INLINE
AutoJSMethodProfilerEntry::AutoJSMethodProfilerEntry(JSContext* cx,
                                                     const char* label,
                                                     const char* dynamicString)
    : AutoGeckoProfilerEntry(
          cx, label, dynamicString, JS::ProfilingCategoryPair::JS_Builtin,
          uint32_t(ProfilingStackFrame::Flags::RELEVANT_FOR_JS) |
              uint32_t(ProfilingStackFrame::Flags::STRING_TEMPLATE_METHOD)) {}

MOZ_ALWAYS_INLINE
AutoJSConstructorProfilerEntry::AutoJSConstructorProfilerEntry(
    JSContext* cx, const char* label)
    : AutoGeckoProfilerEntry(
          cx, label, "constructor", JS::ProfilingCategoryPair::JS_Builtin,
          uint32_t(ProfilingStackFrame::Flags::RELEVANT_FOR_JS)) {}

}  // namespace js

#endif  // vm_GeckoProfiler_inl_h
