/* Copyright 1994 - 1996, LongView Technologies L.L.C. $Revision: 1.22 $ */
/* Copyright (c) 2006, Sun Microsystems, Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the 
following conditions are met:

    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
	  disclaimer in the documentation and/or other materials provided with the distribution.
    * Neither the name of Sun Microsystems nor the names of its contributors may be used to endorse or promote products derived 
	  from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 
NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 
THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE


*/

#ifdef DELTA_COMPILER

// UnpackClosure is used for iteration over a string of nameDescs with different pc values.

class UnpackClosure: public StackObj {
 public:
  virtual void nameDescAt(NameDesc* nameDesc, char* pc) = 0;
};


// nmethodScopes represent the compressed nmethod source-level debugging information generated by the scopeDescRecorder.
// Whenever the debugging information is needed, it is uncompressed into ScopeDescs.
// Compression works mainly by putting all "large" data (e.g., oops) into an array and
// using small (8-bit) indices to refer to this array from the actual scope data structures.

# define FOR_EACH_SCOPE(SCOPES, VAR)\
   for(ScopeDesc* VAR = SCOPES->getNext(NULL); \
       VAR != NULL; VAR =  SCOPES->getNext(VAR) )

class nmethodScopes : public ValueObj {
 private:
  int         _nmethod_offset; 
  uint16      _length;
  // Implements the compact version of nmethod scopes
  // with byte codes, shared oop array, and value array.
  uint16 _oops_offset;    // word offset to the oops array
  uint16 _value_offset;   // word offset to the value array
  uint16 _pcs_offset;     // word offset to PcDesc array
  int    _dependants_end; // size of dependants

 private:
  static uint16 pack_word_aligned(int value) {
    assert(value %  BytesPerWord == 0, "value should be word aligned");
    assert(value >> BytesPerWord <= nthMask(BitsPerByte*sizeof(uint16)),
	   "value exceeds limit");
    return value >> LogBytesPerWord;
  }

  static int unpack_word_aligned(uint16 v) { return v << LogBytesPerWord; }

  int oops_offset()  const { return unpack_word_aligned(_oops_offset); }
  int value_offset() const { return unpack_word_aligned(_value_offset); }
  int pcs_offset()   const { return unpack_word_aligned(_pcs_offset); }

  // Return the address after the struct header
  u_char* start() const { return (u_char*) (this+1); }

 public: // for debugging
  oop* oops()      const  { return (oop*) (start() + oops_offset()); } 
  int  oops_size() const  { return (value_offset() - oops_offset())/sizeof(oop); }
  oop  oop_at(int index) const { 
    assert( index < oops_size(), "oops index out of range");
    return oops()[index]; 
  }
 private:
  int* values()     const { return (int*) (start() + value_offset()); } 
  int  value_size() const { return (pcs_offset() - value_offset())/sizeof(int); }
  int  value_at(int index) const {
    assert( index < value_size(), "oops index out of range");
    return values()[index]; 
  }

  inline u_char getIndexAt(int& offset) const;
  inline oop    unpackOopFromIndex(u_char index, int& offset) const;
  inline int  unpackValueFromIndex(u_char index, int& offset) const;

 private:
  friend class ScopeDescRecorder;
  void set_nmethod_offset(int v) { _nmethod_offset  = v; }
  void set_length(int v)         { _length          = pack_word_aligned(v); }
  void set_oops_offset(int v)    { _oops_offset     = pack_word_aligned(v); }
  void set_value_offset(int v)   { _value_offset    = pack_word_aligned(v); }
  void set_pcs_offset(int v)     { _pcs_offset      = pack_word_aligned(v); }
  void set_dependants_end(int v) { _dependants_end  = v; }

 public:
  klassOop dependant_at(int index) const {
    assert(index >= 0 && index < dependent_length(), "must be within bounds");
    oop result = oop_at(index);
    assert(result->is_klass(), "must be klass");
    return klassOop(result);
  }
  int dependent_length() const { return _dependants_end; }

  void* pcs()    const { return (void*) (start() + pcs_offset()); }
  void* pcsEnd() const { return (void*) end(); }

  int length()   const { return unpack_word_aligned(_length); }

  nmethod* my_nmethod() const { return (nmethod*) (((char*) this) - _nmethod_offset); };

  // returns the address following this nmethodScopes.
  ScopeDesc* end() const {  return (ScopeDesc*) (start()  + length()); }

  bool includes(ScopeDesc* d) const {  return this == d->_scopes; }

  // Returns the root scope without pc specific information.
  // The returned scope cannot be used for retrieving name desc information. 
  ScopeDesc *root() const { return at(0, ScopeDesc::invalid_pc); }

  int size() const { return sizeof(nmethodScopes) + length(); }

  // Returns a scope located at offset.
  ScopeDesc* at(int offset, char* pc) const;

  NonInlinedBlockScopeDesc* noninlined_block_scope_at(int offset) const ;

  // used in iterator macro FOR_EACH_SCOPE
  ScopeDesc *getNext(ScopeDesc *s) const {
    if (!s) return root();
    int offset = s->next_offset();

    if (offset + (sizeof(int) - (offset%sizeof(int))) % sizeof(int)
        >= (_oops_offset)*sizeof(oop)) return NULL;
    return at(offset, ScopeDesc::invalid_pc);
  }

  u_char get_next_char(int& offset) const { return *(start() + offset++); }

  int16  get_next_half(int& offset) const;

  u_char peek_next_char(int offset) const  { return *(start() + offset); }

  oop   unpackOopAt(int& offset) const;
  int unpackValueAt(int& offset) const;

  void iterate(int& offset, UnpackClosure* closure) const;	// iterates over a string of NameDescs (iterator is not called at termination)
  NameDesc* unpackNameDescAt(int& offset, char* pc) const;	// Unpacks a string of name descs and returns one matching the pc

 private:
  NameDesc* unpackNameDescAt(int& offset, bool& is_last, char* pc) const;	// Unpacks a single name desc at offset
 public:

  // Support for garbage collection.
  void oops_do(void f(oop*));
  void scavenge_contents();
  void switch_pointers(oop from, oop to, GrowableArray<nmethod*>* nmethods_to_invalidate);

  bool is_new() const;

  void relocate();

  void verify();
  void print();

  // Prints (dep d% oops %d bytes %d pcs %d)
  void print_partition();
};


#endif
