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

#include "vm/globals.h"
#if defined(TARGET_ARCH_IA32)

#include "vm/debugger.h"

#include "vm/code_patcher.h"
#include "vm/compiler/assembler/disassembler.h"
#include "vm/cpu.h"
#include "vm/object.h"
#include "vm/os.h"
#include "vm/stack_frame.h"
#include "vm/stub_code.h"

namespace dart {

#ifndef PRODUCT

CodePtr CodeBreakpoint::OrigStubAddress() const {
  return saved_value_;
}

void CodeBreakpoint::PatchCode() {
  ASSERT(!IsEnabled());
  auto thread = Thread::Current();
  auto zone = thread->zone();
  const Code& code = Code::Handle(zone, code_);
  const Instructions& instrs = Instructions::Handle(zone, code.instructions());
  Code& stub_target = Code::Handle(zone);
  thread->isolate_group()->RunWithStoppedMutators([&]() {
    WritableInstructionsScope writable(instrs.WritablePayloadStart(),
                                       instrs.Size());
    switch (breakpoint_kind_) {
      case UntaggedPcDescriptors::kIcCall: {
        stub_target = StubCode::ICCallBreakpoint().ptr();
        break;
      }
      case UntaggedPcDescriptors::kUnoptStaticCall: {
        stub_target = StubCode::UnoptStaticCallBreakpoint().ptr();
        break;
      }
      case UntaggedPcDescriptors::kRuntimeCall: {
        saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
        stub_target = StubCode::RuntimeCallBreakpoint().ptr();
        break;
      }
      default:
        UNREACHABLE();
    }
    saved_value_ = CodePatcher::GetStaticCallTargetAt(pc_, code);
    CodePatcher::PatchStaticCallAt(pc_, code, stub_target);
  });
}

void CodeBreakpoint::RestoreCode() {
  ASSERT(IsEnabled());
  auto thread = Thread::Current();
  auto zone = thread->zone();
  const Code& code = Code::Handle(zone, code_);
  const Instructions& instrs = Instructions::Handle(zone, code.instructions());
  thread->isolate_group()->RunWithStoppedMutators([&]() {
    WritableInstructionsScope writable(instrs.WritablePayloadStart(),
                                       instrs.Size());
    switch (breakpoint_kind_) {
      case UntaggedPcDescriptors::kIcCall:
      case UntaggedPcDescriptors::kUnoptStaticCall:
      case UntaggedPcDescriptors::kRuntimeCall: {
        CodePatcher::PatchStaticCallAt(pc_, code, Code::Handle(saved_value_));
        break;
      }
      default:
        UNREACHABLE();
    }
  });
}

#endif  // !PRODUCT

}  // namespace dart

#endif  // defined TARGET_ARCH_IA32
