/* Copyright (C) 2017 The Android Open Source Project
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This file implements interfaces from the file jvmti.h. This implementation
 * is licensed under the same terms as the file jvmti.h.  The
 * copyright and license information for the file jvmti.h follows.
 *
 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "ti_object.h"

#include "art_jvmti.h"
#include "mirror/object-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
#include "ti_thread.h"

namespace openjdkjvmti {

jvmtiError ObjectUtil::GetObjectSize(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                     jobject jobject,
                                     jlong* size_ptr) {
  if (jobject == nullptr) {
    return ERR(INVALID_OBJECT);
  }
  if (size_ptr == nullptr) {
    return ERR(NULL_POINTER);
  }

  art::ScopedObjectAccess soa(art::Thread::Current());
  art::ObjPtr<art::mirror::Object> object = soa.Decode<art::mirror::Object>(jobject);

  *size_ptr = object->SizeOf();
  return ERR(NONE);
}

jvmtiError ObjectUtil::GetObjectHashCode(jvmtiEnv* env ATTRIBUTE_UNUSED,
                                         jobject jobject,
                                         jint* hash_code_ptr) {
  if (jobject == nullptr) {
    return ERR(INVALID_OBJECT);
  }
  if (hash_code_ptr == nullptr) {
    return ERR(NULL_POINTER);
  }

  art::ScopedObjectAccess soa(art::Thread::Current());
  art::ObjPtr<art::mirror::Object> object = soa.Decode<art::mirror::Object>(jobject);

  *hash_code_ptr = object->IdentityHashCode();

  return ERR(NONE);
}

jvmtiError ObjectUtil::GetObjectMonitorUsage(
    jvmtiEnv* baseenv, jobject obj, jvmtiMonitorUsage* usage) {
  ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(baseenv);
  if (obj == nullptr) {
    return ERR(INVALID_OBJECT);
  }
  if (usage == nullptr) {
    return ERR(NULL_POINTER);
  }
  art::Thread* self = art::Thread::Current();
  ThreadUtil::SuspendCheck(self);
  art::JNIEnvExt* jni = self->GetJniEnv();
  std::vector<jthread> wait;
  std::vector<jthread> notify_wait;
  {
    art::ScopedObjectAccess soa(self);      // Now we know we have the shared lock.
    art::ScopedThreadSuspension sts(self, art::kNative);
    art::ScopedSuspendAll ssa("GetObjectMonitorUsage", /*long_suspend=*/false);
    art::ObjPtr<art::mirror::Object> target(self->DecodeJObject(obj));
    // This gets the list of threads trying to lock or wait on the monitor.
    art::MonitorInfo info(target.Ptr());
    usage->owner = info.owner_ != nullptr ?
        jni->AddLocalReference<jthread>(info.owner_->GetPeerFromOtherThread()) : nullptr;
    usage->entry_count = info.entry_count_;
    for (art::Thread* thd : info.waiters_) {
      // RI seems to consider waiting for notify to be included in those waiting to acquire the
      // monitor. We will match this behavior.
      notify_wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
      wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
    }
    {
      // Scan all threads to see which are waiting on this particular monitor.
      art::MutexLock tll(self, *art::Locks::thread_list_lock_);
      for (art::Thread* thd : art::Runtime::Current()->GetThreadList()->GetList()) {
        if (thd != info.owner_ && target.Ptr() == thd->GetMonitorEnterObject()) {
          wait.push_back(jni->AddLocalReference<jthread>(thd->GetPeerFromOtherThread()));
        }
      }
    }
  }
  usage->waiter_count = wait.size();
  usage->notify_waiter_count = notify_wait.size();
  jvmtiError ret = CopyDataIntoJvmtiBuffer(env,
                                           reinterpret_cast<const unsigned char*>(wait.data()),
                                           wait.size() * sizeof(jthread),
                                           reinterpret_cast<unsigned char**>(&usage->waiters));
  if (ret != OK) {
    return ret;
  }
  return CopyDataIntoJvmtiBuffer(env,
                                 reinterpret_cast<const unsigned char*>(notify_wait.data()),
                                 notify_wait.size() * sizeof(jthread),
                                 reinterpret_cast<unsigned char**>(&usage->notify_waiters));
}

}  // namespace openjdkjvmti
