/*
 * Copyright (c) 2023, 2024, 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 SHARE_OOPS_RESOLVEDMETHODENTRY_HPP
#define SHARE_OOPS_RESOLVEDMETHODENTRY_HPP

#include "interpreter/bytecodes.hpp"
#include "runtime/atomic.hpp"
#include "utilities/sizes.hpp"

// ResolvedMethodEntry contains the resolution information for the invoke bytecodes
// invokestatic, invokespecial, invokeinterface, invokevirtual, and invokehandle but
// NOT invokedynamic (see resolvedIndyEntry.hpp). A member of this class can be initialized
// with the constant pool index associated with the bytecode before any resolution is done,
// where "resolution" refers to populating the bytecode1 and bytecode2 fields and other
// relevant information. These entries are contained within the ConstantPoolCache and are
// accessed with indices added to the bytecode after rewriting.

// Invoke bytecodes start with a constant pool index as their operand, which is then
// rewritten to a "method index", which is an index into the array of ResolvedMethodEntry.
// This structure has fields for every type of invoke bytecode but each entry may only
// use some of the fields. All entries have a TOS state, number of parameters, flags,
// and a constant pool index.

// Types of invokes
// invokestatic
// invokespecial
//   Method*
// invokehandle
//   Method*
//   resolved references index
// invokevirtual
//   Method* (if vfinal is true)
//   vtable/itable index
// invokeinterface
//   Klass*
//   Method*

// Note: invokevirtual & invokespecial bytecodes can share the same constant
//       pool entry and thus the same resolved method entry.
// The is_vfinal flag indicates method pointer for a final method or an index.

class InstanceKlass;
class ResolvedMethodEntry {
  friend class VMStructs;

  Method* _method;                   // Method for non virtual calls, adapter method for invokevirtual, final method for virtual
  union {                            // These fields are mutually exclusive and are only used by some invoke codes
    InstanceKlass* _interface_klass; // for interface and static
    u2 _resolved_references_index;   // Index of resolved references array that holds the appendix oop for invokehandle
    u2 _table_index;                 // vtable/itable index for virtual and interface calls
  } _entry_specific;

  u2 _cpool_index;                   // Constant pool index
  u2 _number_of_parameters;          // Number of arguments for method
  u1 _tos_state;                     // TOS state
  u1 _flags;                         // Flags: [00|has_resolved_ref_index|has_local_signature|has_appendix|forced_virtual|final|virtual_final]
  u1 _bytecode1, _bytecode2;         // Resolved invoke codes
#ifdef ASSERT
  bool _has_interface_klass;
  bool _has_table_index;
#endif

  void copy_from(const ResolvedMethodEntry& other) {
    _method = other._method;
    _entry_specific = other._entry_specific;
    _cpool_index = other._cpool_index;
    _number_of_parameters = other._number_of_parameters;
    _tos_state = other._tos_state;
    _flags = other._flags;
    _bytecode1 = other._bytecode1;
    _bytecode2 = other._bytecode2;
#ifdef ASSERT
    _has_interface_klass = other._has_interface_klass;
    _has_table_index = other._has_table_index;
#endif
  }

  // Constructors
  public:
    ResolvedMethodEntry(u2 cpi) :
      _method(nullptr),
      _cpool_index(cpi),
      _number_of_parameters(0),
      _tos_state(0),
      _flags(0),
      _bytecode1(0),
      _bytecode2(0) {
        _entry_specific._interface_klass = nullptr;
        DEBUG_ONLY(_has_interface_klass = false;)
        DEBUG_ONLY(_has_table_index = false;)
      }
    ResolvedMethodEntry() :
      ResolvedMethodEntry(0) {}

    ResolvedMethodEntry(const ResolvedMethodEntry& other) {
      copy_from(other);
    }

    ResolvedMethodEntry& operator=(const ResolvedMethodEntry& other) {
      copy_from(other);
      return *this;
    }


  // Bit shift to get flags
  enum {
      is_vfinal_shift           = 0,
      is_final_shift            = 1,
      is_forced_virtual_shift   = 2,
      has_appendix_shift        = 3,
      has_local_signature_shift = 4,
      has_resolved_ref_shift    = 5
  };

  // Flags
  bool is_vfinal()                     const { return (_flags & (1 << is_vfinal_shift))           != 0; }
  bool is_final()                      const { return (_flags & (1 << is_final_shift))            != 0; }
  bool is_forced_virtual()             const { return (_flags & (1 << is_forced_virtual_shift))   != 0; }
  bool has_appendix()                  const { return (_flags & (1 << has_appendix_shift))        != 0; }
  bool has_local_signature()           const { return (_flags & (1 << has_local_signature_shift)) != 0; }
  bool has_resolved_references_index() const { return (_flags & (1 << has_resolved_ref_shift))    != 0; }

  // Getters
  Method* method() const { return Atomic::load_acquire(&_method); }
  InstanceKlass* interface_klass() const {
    assert(_bytecode1 == Bytecodes::_invokeinterface, "Only invokeinterface has a klass %d", _bytecode1);
    assert(_has_interface_klass, "sanity");
    return _entry_specific._interface_klass;
  }
  u2 resolved_references_index() const {
    // This index may be read before resolution completes
    assert(has_resolved_references_index(), "sanity");
    return _entry_specific._resolved_references_index;
  }
  u2 table_index() const {
    assert(_bytecode2 == Bytecodes::_invokevirtual, "Only invokevirtual has a vtable/itable index %d", _bytecode2);
    assert(_has_table_index, "sanity");
    return _entry_specific._table_index;
  }
  u2 constant_pool_index() const { return _cpool_index; }
  u1 tos_state() const { return _tos_state; }
  u2 number_of_parameters() const { return _number_of_parameters; }
  u1 bytecode1() const { return Atomic::load_acquire(&_bytecode1); }
  u1 bytecode2() const { return Atomic::load_acquire(&_bytecode2); }

  bool is_resolved(Bytecodes::Code code) const {
    switch(code) {
      case Bytecodes::_invokeinterface:
      case Bytecodes::_invokehandle:
      case Bytecodes::_invokespecial:
      case Bytecodes::_invokestatic:
        return (bytecode1() == code);
      case Bytecodes::_invokevirtual:
        return (bytecode2() == code);
    default:
      ShouldNotReachHere();
      return false;
    }
  }

  void adjust_method_entry(Method* new_method) {
    // this is done during the redefinition safepoint
    _method = new_method;
  }
  bool check_no_old_or_obsolete_entry();

  // Printing
  void print_on(outputStream* st) const;

  // Setters
  void set_flags(u1 flags) { _flags |= flags; }

  inline void set_bytecode(u1* code, u1 new_code) {
  #ifdef ASSERT
    // Read once.
    volatile Bytecodes::Code c = (Bytecodes::Code)*code;
    assert(c == 0 || c == new_code || new_code == 0, "update must be consistent old: %d, new: %d", c, new_code);
  #endif
    Atomic::release_store(code, new_code);
  }

  void set_bytecode1(u1 b1) {
    set_bytecode(&_bytecode1, b1);
  }

  void set_bytecode2(u1 b2) {
    set_bytecode(&_bytecode2, b2);
  }

  void set_method(Method* m) {
    Atomic::release_store(&_method, m);
  }

  void set_klass(InstanceKlass* klass) {
    assert(!has_resolved_references_index() &&
           !_has_table_index,
           "Mutually exclusive fields %d %d %d", has_resolved_references_index(), _has_interface_klass, _has_table_index);
    DEBUG_ONLY(_has_interface_klass = true;)
    _entry_specific._interface_klass = klass;
  }

  void set_resolved_references_index(u2 ref_index) {
    assert(!_has_interface_klass &&
           !_has_table_index,
           "Mutually exclusive fields %d %d %d", has_resolved_references_index(), _has_interface_klass, _has_table_index);
    set_flags(1 << has_resolved_ref_shift);
    _entry_specific._resolved_references_index = ref_index;
  }

  void set_table_index(u2 table_index) {
    assert(!has_resolved_references_index() &&
           !_has_interface_klass,
           "Mutually exclusive fields %d %d %d", has_resolved_references_index(), _has_interface_klass, _has_table_index);
    DEBUG_ONLY(_has_table_index = true;)
    _entry_specific._table_index = table_index;
  }

  void set_num_parameters(u2 num_params) {
    _number_of_parameters = num_params;
  }

  void fill_in(u1 tos_state, u2 num_params) {
    _tos_state = tos_state;
    _number_of_parameters = num_params;
  }

  void reset_entry();

  // CDS
#if INCLUDE_CDS
  void remove_unshareable_info();
  void mark_and_relocate(ConstantPool* src_cp);
#endif

  // Offsets
  static ByteSize klass_offset()                     { return byte_offset_of(ResolvedMethodEntry, _entry_specific._interface_klass); }
  static ByteSize method_offset()                    { return byte_offset_of(ResolvedMethodEntry, _method);       }
  static ByteSize resolved_references_index_offset() { return byte_offset_of(ResolvedMethodEntry, _entry_specific._resolved_references_index); }
  static ByteSize table_index_offset()               { return byte_offset_of(ResolvedMethodEntry, _entry_specific._table_index);       }
  static ByteSize num_parameters_offset()            { return byte_offset_of(ResolvedMethodEntry, _number_of_parameters);      }
  static ByteSize type_offset()                      { return byte_offset_of(ResolvedMethodEntry, _tos_state); }
  static ByteSize flags_offset()                     { return byte_offset_of(ResolvedMethodEntry, _flags);        }
  static ByteSize bytecode1_offset()                 { return byte_offset_of(ResolvedMethodEntry, _bytecode1);        }
  static ByteSize bytecode2_offset()                 { return byte_offset_of(ResolvedMethodEntry, _bytecode2);        }

};

#endif //SHARE_OOPS_RESOLVEDMETHODENTRY_HPP
