//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PLEASE DO NOT EDIT WITHOUT THE EXPLICIT CONSENT OF THE AUTHOR! \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\

package hlt.language.design.instructions;

/**
 * @version     Last modified on Wed Jun 20 14:29:51 2012 by hak
 * @author      <a href="mailto:hak@acm.org">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; <a href="http://www.hassan-ait-kaci.net/">by the author</a>
 */

import hlt.language.design.types.CodeEntry;
import hlt.language.design.backend.Closure;
import hlt.language.design.backend.Runtime;

/**
 * A <tt>PushClosure</tt> instruction is to a <a href="PushScope.html"><tt>PushScope</tt></a>
 * instruction what an <a href="../kernel/Abstraction.html"><tt>Abstraction</tt></a> expression
 * is to an <a href="../kernel/Scope.html"><tt>Scope</tt></a> expression. In other words, it is
 * a <tt>PushScope</tt> with the added frame machinery.
 */
public class PushClosure extends PushScope
{
  private int _intFrameSize;
  private int _realFrameSize;
  private int _objectFrameSize;

  protected boolean _isExitable = true;

  public final boolean isExitable ()
    {
      return _isExitable;
    }

  public final PushClosure setIsExitable (boolean flag)
    {
      _isExitable = flag;
      return this;
    }

  public final PushClosure setNonExitable ()
    {
      _isExitable = false;
      return this;
    }

  public PushClosure (int voidArity, int intArity, int realArity, int objectArity,
                      int intFrameSize, int realFrameSize, int objectFrameSize)
    {
      super(voidArity,intArity,realArity,objectArity);
      _intFrameSize = intFrameSize;
      _realFrameSize = realFrameSize;
      _objectFrameSize = objectFrameSize;
      setName("PUSH_CLOSURE");
    }

  public PushClosure (int address, int voidArity, int intArity, int realArity, int objectArity,
                      int intFrameSize, int realFrameSize, int objectFrameSize)
    {
      this(voidArity,intArity,realArity,objectArity,intFrameSize,realFrameSize,objectFrameSize);
      setAddress(address);
    }

  final int intFrameSize ()
    {
      return _intFrameSize;
    }
  
  final int realFrameSize ()
    {
      return _realFrameSize;
    }
  
  final int objectFrameSize ()
    {
      return _objectFrameSize;
    }
  
  public final Instruction apply ()
    {
      return new Apply(_voidArity,_intArity,_realArity,_objectArity);
    }

  public final void execute (Runtime r)
    {
      r.pushObject(new Closure(_isExitable,
                               _code,
                               _address,
                               _voidArity,
                               _intArity,
                               _realArity,
                               _objectArity,
                               r.copyIntEnv(_intFrameSize),
                               r.copyRealEnv(_realFrameSize),
                               r.copyObjectEnv(_objectFrameSize)));
      r.incIP();
    }

  public final boolean equals (Object object)
    {
      if (this == object)
        return true;

      if (!(object instanceof PushClosure))
        return false;

      PushClosure pushClosure = (PushClosure)object;

      return _name            == pushClosure.name()
          && _address         == pushClosure.address()
          && _voidArity       == pushClosure.voidArity()
          && _intArity        == pushClosure.intArity()
          && _realArity       == pushClosure.realArity()
          && _objectArity     == pushClosure.objectArity()
          && _intFrameSize    == pushClosure.intFrameSize()
          && _realFrameSize   == pushClosure.realFrameSize()
          && _objectFrameSize == pushClosure.objectFrameSize()
          && _isExitable      == pushClosure.isExitable()
          && (_code == null ? pushClosure.code() == null : _code == pushClosure.code());
    }

  public final int hashCode ()
    {
      return name().hashCode() + 2*_address + (_code == null ? 0 : 3*_code.length)
           + 5*_voidArity + 7*_intArity + 11*_realArity + 13*_objectArity
           + 17*_intFrameSize + 19*_realFrameSize + 23*_objectFrameSize;           
    }

  public final String toString ()
    {
      return name()
           + "("+ CodeEntry.getId(_code) + "," + _address + ","
           + "<" + _voidArity + "," + _intArity + "," + _realArity + "," + _objectArity + ">,"
           + "<" + _intFrameSize + "," + _realFrameSize + "," + _objectFrameSize + ">)";
    }

}

