// Copyright (c) 1997, 2004  Per M.A. Bothner.
// This is free software;  for terms and warranty disclaimer see ./COPYING.

package gnu.bytecode;
import java.util.HashMap;
public class Scope
{
  /** The enclosing scope. */
  Scope parent;
  Scope nextSibling;
  Scope firstChild, lastChild;

  /** If true, don't call freeLocal on our variables (yet). */
  boolean preserved;

    boolean autoPop;

  Label start;
  Label end;
  private Variable vars;
  Variable last_var;

  public Scope()
  {
  }

  public Scope (Label start, Label end)
  {
    this.start = start;
    this.end = end;
  }

  //  Variable lookup (String name);
  public final Variable firstVar () { return vars; }

  public VarEnumerator allVars () { return new VarEnumerator (this); }

    public Label getStartLabel() { return start; }
    public Label getEndLabel() { return end; }

  /** Link this scope as the next child of its parent scope. */
  public void linkChild (Scope parent)
  {
    this.parent = parent;
    if (parent == null)
      return;
    if (parent.lastChild == null)
      parent.firstChild = this;
    else
      parent.lastChild.nextSibling = this;
    parent.lastChild = this;
  }

  public Variable addVariable (CodeAttr code, Type type, String name)
  {
    Variable var = new Variable(name, type);
    addVariable (code, var);
    return var;
   }

  public void addVariable (Variable var)
  {
    if (last_var == null)
      vars = var;
    else
      last_var.next = var;
    last_var = var;
    var.setScope(this);
  }

  /* Add a new Variable, linking it in after a given Variable, */
  public void addVariableAfter (Variable prev, Variable var)
  {
    if (prev == null)
      { // Put first
	var.next = vars;
	vars = var;
      }
    else
      {
	var.next = prev.next;
	prev.next = var;
      }
    if (last_var == prev)
      last_var = var;
    if (var.next == var)
      throw new Error("cycle");
    var.setScope(this);
  }

  public void addVariable (CodeAttr code, Variable var)
  {
    addVariable (var);
    if (var.isSimple() && code != null)
      var.allocateLocal(code);
  }

  /**
   * Return a variable the scope, by numerical index.
   * @param index the number of the variable
   */
  public Variable getVariable(int index) {
    Variable var = vars;
    while (--index >= 0)
      var = var.next;
    return var;
  }

    /** Fix duplicate names.
     * This is needed for Android, since otherwise dex complains.
     */
    public void fixParamNames(HashMap<String,Variable> map) {
        for (Variable var = firstVar();  var != null;
             var = var.nextVar()) {
            String name = var.getName();
            if (name != null) {
                String xname = name;
                Variable old;
                for (int i = 0; (old = map.get(xname)) != null;  i++)
                    xname = name + '$' + i;
                if (name != xname)
                    var.setName(xname);
                map.put(xname, var);
            }
        }
    }


  static boolean equals (byte[] name1, byte[] name2) {
    if (name1.length != name2.length)
      return false;
    if (name1 == name2)
      return true;
    for (int i = name1.length; --i >= 0; )
      if (name1[i] != name2[i])
	return false;
    return true;
  }

  public void setStartPC(CodeAttr code)
  {
    if (start == null)
      start = new Label();
    boolean reachable = code.reachableHere();
    start.define(code);
    code.setReachable(reachable);
  }

  /** Should be called at the start of a logical function - inlined or not. */
  public void noteStartFunction(CodeAttr code)
  {
    setStartPC(code);
  }

  /**
   * Search by name for a Variable in this Scope (only).
   * @param name name to search for
   * @return the Variable, or null if not found (in this scope).
   */
  public Variable lookup (String name) {
    for (Variable var = vars;  var != null;  var = var.next) {
      if (name.equals(var.name))
	return var;
    }
    return null;
  }

  /** Make local variable slots of this scope available for re-use.
   * However, if the 'preserved' flag is set, defer doing so until
   * we exit a non-preserved Scope. */
  void freeLocals (CodeAttr code)
  {
    if (preserved)
      return;
    for (Variable var = vars; var != null; var = var.next)
      {
	if (var.isSimple () && ! var.dead ())
	  var.freeLocal(code);
      }
    for (Scope child = firstChild;  child != null; child = child.nextSibling)
      {
	if (child.preserved)
	  {
	    child.preserved = false;
	    child.freeLocals(code);
	  }
      }
  }

    /* DEBUG
    static int counter; int id=++counter;
    public String toString() { return "Scope#"+id; }
    */
}
