/*
 * Copyright (c) 2021, 2023, 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.
 *
 * 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.
 *
 */

#ifndef SHARED_CDS_SHAREDCLASSINFO_HPP
#define SHARED_CDS_SHAREDCLASSINFO_HPP

#include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp"
#include "cds/cds_globals.hpp"
#include "cds/metaspaceShared.hpp"
#include "classfile/compactHashtable.hpp"
#include "classfile/javaClasses.hpp"
#include "memory/metaspaceClosure.hpp"
#include "oops/instanceKlass.hpp"
#include "prims/jvmtiExport.hpp"
#include "utilities/growableArray.hpp"

class DumpTimeClassInfo;
class Method;
class Symbol;

class RunTimeClassInfo {
public:
  struct CrcInfo {
    int _clsfile_size;
    int _clsfile_crc32;
  };

  // This is different than DumpTimeClassInfo::DTVerifierConstraint. We use
  // u4 instead of Symbol* to save space on 64-bit CPU.
  struct RTVerifierConstraint {
    u4 _name;
    u4 _from_name;
    Symbol* name() { return (Symbol*)(SharedBaseAddress + _name);}
    Symbol* from_name() { return (Symbol*)(SharedBaseAddress + _from_name); }
  };

  struct RTLoaderConstraint {
    u4   _name;
    char _loader_type1;
    char _loader_type2;
    Symbol* constraint_name() {
      return (Symbol*)(SharedBaseAddress + _name);
    }
  };
  struct RTEnumKlassStaticFields {
    int _num;
    int _root_indices[1];
  };

  InstanceKlass* _klass;
  int _num_verifier_constraints;
  int _num_loader_constraints;

  // optional CrcInfo                 _crc;  (only for UNREGISTERED classes)
  // optional InstanceKlass*          _nest_host
  // optional RTLoaderConstraint      _loader_constraint_types[_num_loader_constraints]
  // optional RTVerifierConstraint    _verifier_constraints[_num_verifier_constraints]
  // optional char                    _verifier_constraint_flags[_num_verifier_constraints]
  // optional RTEnumKlassStaticFields _enum_klass_static_fields;

private:
  static size_t header_size_size() {
    return align_up(sizeof(RunTimeClassInfo), wordSize);
  }
  static size_t verifier_constraints_size(int num_verifier_constraints) {
    return align_up(sizeof(RTVerifierConstraint) * num_verifier_constraints, wordSize);
  }
  static size_t verifier_constraint_flags_size(int num_verifier_constraints) {
    return align_up(sizeof(char) * num_verifier_constraints, wordSize);
  }
  static size_t loader_constraints_size(int num_loader_constraints) {
    return align_up(sizeof(RTLoaderConstraint) * num_loader_constraints, wordSize);
  }
  static size_t enum_klass_static_fields_size(int num_fields) {
    size_t size = num_fields <= 0 ? 0 : sizeof(RTEnumKlassStaticFields) + (num_fields - 1) * sizeof(int);
    return align_up(size, wordSize);
  }

  static size_t nest_host_size(InstanceKlass* klass) {
    if (klass->is_hidden()) {
      return sizeof(InstanceKlass*);
    } else {
      return 0;
    }
  }

  static size_t crc_size(InstanceKlass* klass);
public:
  static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints,
                          int num_enum_klass_static_fields) {
    return header_size_size() +
           crc_size(klass) +
           nest_host_size(klass) +
           loader_constraints_size(num_loader_constraints) +
           verifier_constraints_size(num_verifier_constraints) +
           verifier_constraint_flags_size(num_verifier_constraints) +
           enum_klass_static_fields_size(num_enum_klass_static_fields);
  }

private:
  size_t crc_offset() const {
    return header_size_size();
  }

  size_t nest_host_offset() const {
    return crc_offset() + crc_size(_klass);
  }

  size_t loader_constraints_offset() const  {
    return nest_host_offset() + nest_host_size(_klass);
  }
  size_t verifier_constraints_offset() const {
    return loader_constraints_offset() + loader_constraints_size(_num_loader_constraints);
  }
  size_t verifier_constraint_flags_offset() const {
    return verifier_constraints_offset() + verifier_constraints_size(_num_verifier_constraints);
  }
  size_t enum_klass_static_fields_offset() const {
    return verifier_constraint_flags_offset() + verifier_constraint_flags_size(_num_verifier_constraints);
  }

  void check_verifier_constraint_offset(int i) const {
    assert(0 <= i && i < _num_verifier_constraints, "sanity");
  }

  void check_loader_constraint_offset(int i) const {
    assert(0 <= i && i < _num_loader_constraints, "sanity");
  }

  RTEnumKlassStaticFields* enum_klass_static_fields_addr() const {
    assert(_klass->has_archived_enum_objs(), "sanity");
    return (RTEnumKlassStaticFields*)(address(this) + enum_klass_static_fields_offset());
  }

public:
  CrcInfo* crc() const {
    assert(crc_size(_klass) > 0, "must be");
    return (CrcInfo*)(address(this) + crc_offset());
  }
  RTVerifierConstraint* verifier_constraints() {
    assert(_num_verifier_constraints > 0, "sanity");
    return (RTVerifierConstraint*)(address(this) + verifier_constraints_offset());
  }
  RTVerifierConstraint* verifier_constraint_at(int i) {
    check_verifier_constraint_offset(i);
    return verifier_constraints() + i;
  }

  char* verifier_constraint_flags() {
    assert(_num_verifier_constraints > 0, "sanity");
    return (char*)(address(this) + verifier_constraint_flags_offset());
  }

  InstanceKlass** nest_host_addr() {
    assert(_klass->is_hidden(), "sanity");
    return (InstanceKlass**)(address(this) + nest_host_offset());
  }
  InstanceKlass* nest_host() {
    return *nest_host_addr();
  }

  RTLoaderConstraint* loader_constraints() {
    assert(_num_loader_constraints > 0, "sanity");
    return (RTLoaderConstraint*)(address(this) + loader_constraints_offset());
  }

  RTLoaderConstraint* loader_constraint_at(int i) {
    check_loader_constraint_offset(i);
    return loader_constraints() + i;
  }

  void init(DumpTimeClassInfo& info);

  bool matches(int clsfile_size, int clsfile_crc32) const {
    return crc()->_clsfile_size  == clsfile_size &&
           crc()->_clsfile_crc32 == clsfile_crc32;
  }

  char verifier_constraint_flag(int i) {
    check_verifier_constraint_offset(i);
    return verifier_constraint_flags()[i];
  }

  int num_enum_klass_static_fields(int i) const {
    return enum_klass_static_fields_addr()->_num;
  }

  void set_num_enum_klass_static_fields(int num) {
    enum_klass_static_fields_addr()->_num = num;
  }

  int enum_klass_static_field_root_index_at(int i) const {
    assert(0 <= i && i < enum_klass_static_fields_addr()->_num, "must be");
    return enum_klass_static_fields_addr()->_root_indices[i];
  }

  void set_enum_klass_static_field_root_index_at(int i, int root_index) {
    assert(0 <= i && i < enum_klass_static_fields_addr()->_num, "must be");
    enum_klass_static_fields_addr()->_root_indices[i] = root_index;
  }
private:
  // ArchiveBuilder::make_shallow_copy() has reserved a pointer immediately
  // before archived InstanceKlasses. We can use this slot to do a quick
  // lookup of InstanceKlass* -> RunTimeClassInfo* without
  // building a new hashtable.
  //
  //  info_pointer_addr(klass) --> 0x0100   RunTimeClassInfo*
  //  InstanceKlass* klass     --> 0x0108   <C++ vtbl>
  //                               0x0110   fields from Klass ...
  static RunTimeClassInfo** info_pointer_addr(InstanceKlass* klass) {
    return &((RunTimeClassInfo**)klass)[-1];
  }

public:
  static RunTimeClassInfo* get_for(InstanceKlass* klass) {
    assert(klass->is_shared(), "don't call for non-shared class");
    return *info_pointer_addr(klass);
  }
  static void set_for(InstanceKlass* klass, RunTimeClassInfo* record) {
    assert(ArchiveBuilder::current()->is_in_buffer_space(klass), "must be");
    assert(ArchiveBuilder::current()->is_in_buffer_space(record), "must be");
    *info_pointer_addr(klass) = record;
    ArchivePtrMarker::mark_pointer(info_pointer_addr(klass));
  }

  // Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS
  static inline bool EQUALS(
       const RunTimeClassInfo* value, Symbol* key, int len_unused) {
    return (value->_klass->name() == key);
  }
};

class RunTimeSharedDictionary : public OffsetCompactHashtable<
  Symbol*,
  const RunTimeClassInfo*,
  RunTimeClassInfo::EQUALS> {};
#endif // SHARED_CDS_SHAREDCLASSINFO_HPP
