/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "runtime_intrinsics.h"

#include "art_method-inl.h"
#include "class_linker.h"
#include "dex/invoke_type.h"
#include "intrinsics_enum.h"
#include "intrinsics_list.h"
#include "mirror/class.h"
#include "runtime.h"
#include "scoped_thread_state_change-inl.h"
#include "thread.h"

namespace art {

namespace {

ArtMethod* FindIntrinsicMethod(Thread* self,
                               const char* class_name,
                               const char* method_name,
                               const char* signature)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
  PointerSize image_size = class_linker->GetImagePointerSize();
  ObjPtr<mirror::Class> cls = class_linker->FindSystemClass(self, class_name);
  if (cls == nullptr) {
    LOG(FATAL) << "Could not find class of intrinsic " << class_name;
  }

  ArtMethod* method = cls->FindClassMethod(method_name, signature, image_size);
  if (method == nullptr || method->GetDeclaringClass() != cls) {
    LOG(FATAL) << "Could not find method of intrinsic "
               << class_name << " " << method_name << " " << signature;
  }
  return method;
}

// Initialize an intrinsic. Returns true if the intrinsic is already
// initialized, false otherwise.
bool InitializeIntrinsic(Thread* self,
                         Intrinsics intrinsic,
                         InvokeType invoke_type,
                         const char* class_name,
                         const char* method_name,
                         const char* signature)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ArtMethod* method = FindIntrinsicMethod(self, class_name, method_name, signature);

  CHECK_EQ(method->GetInvokeType(), invoke_type);
  if (method->IsIntrinsic()) {
    CHECK_EQ(method->GetIntrinsic(), static_cast<uint32_t>(intrinsic));
    return true;
  } else {
    method->SetIntrinsic(static_cast<uint32_t>(intrinsic));
    return false;
  }
}

// Returns true if the intrinsic is already initialized, false otherwise.
bool IsIntrinsicInitialized(Thread* self,
                            Intrinsics intrinsic,
                            InvokeType invoke_type,
                            const char* class_name,
                            const char* method_name,
                            const char* signature)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ArtMethod* method = FindIntrinsicMethod(self, class_name, method_name, signature);

  CHECK_EQ(method->GetInvokeType(), invoke_type);
  if (method->IsIntrinsic()) {
    CHECK_EQ(method->GetIntrinsic(), static_cast<uint32_t>(intrinsic));
    return true;
  } else {
    return false;
  }
}

bool AreAllIntrinsicsInitialized() {
  ScopedObjectAccess soa(Thread::Current());
#define IS_INTRINSIC_INITIALIZED(Name, InvokeType, _, __, ___, ClassName, MethodName, Signature) \
  IsIntrinsicInitialized(soa.Self(),                                                             \
                         Intrinsics::k##Name,                                                    \
                         InvokeType,                                                             \
                         ClassName,                                                              \
                         MethodName,                                                             \
                         Signature) &&
  bool result = INTRINSICS_LIST(IS_INTRINSIC_INITIALIZED) true;
#undef IS_INTRINSIC_INITIALIZED
  return result;
}

}  // namespace

void InitializeIntrinsics() {
  ScopedObjectAccess soa(Thread::Current());
  // Initialization here uses the short-circuit operator || to stop
  // initializing if there's an already initialized intrinsic.
#define INITIALIZE_INTRINSIC(Name, InvokeType, _, __, ___, ClassName, MethodName, Signature) \
  InitializeIntrinsic(soa.Self(),                                                            \
                      Intrinsics::k##Name,                                                   \
                      InvokeType,                                                            \
                      ClassName,                                                             \
                      MethodName,                                                            \
                      Signature) ||
  INTRINSICS_LIST(INITIALIZE_INTRINSIC) true;
#undef INITIALIZE_INTRINSIC
  DCHECK(AreAllIntrinsicsInitialized());
}

}  // namespace art
