/*
 * Copyright (c) 2002 - 2006 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 */
package com.ibm.wala.ssa;

import com.ibm.wala.classLoader.NewSiteReference;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeReference;

/**
 * An allocation instruction ("new") for SSA form.
 *
 * <p>This includes allocations of both scalars and arrays.
 */
public abstract class SSANewInstruction extends SSAInstruction {
  private final int result;

  private final NewSiteReference site;

  /**
   * The value numbers of the arguments passed to the call. If params == null, this should be a
   * static this statement allocates a scalar. if params != null, then params[i] is the size of the
   * ith dimension of the array.
   */
  private final int[] params;

  /** Create a new instruction to allocate a scalar. */
  protected SSANewInstruction(int iindex, int result, NewSiteReference site)
      throws IllegalArgumentException {
    super(iindex);
    if (site == null) {
      throw new IllegalArgumentException("site cannot be null");
    }
    this.result = result;
    this.site = site;
    this.params = null;
  }

  /**
   * Create a new instruction to allocate an array.
   *
   * @throws IllegalArgumentException if site is null
   * @throws IllegalArgumentException if params is null
   */
  protected SSANewInstruction(int iindex, int result, NewSiteReference site, int[] params) {
    super(iindex);
    if (params == null) {
      throw new IllegalArgumentException("params is null");
    }
    if (site == null) {
      throw new IllegalArgumentException("site is null");
    }
    assert site.getDeclaredType().isArrayType()
        || site.getDeclaredType().getClassLoader().getLanguage() != ClassLoaderReference.Java;
    this.result = result;
    this.site = site;
    this.params = params.clone();
  }

  @Override
  public SSAInstruction copyForSSA(SSAInstructionFactory insts, int[] defs, int[] uses) {
    if (params == null) {
      return insts.NewInstruction(iIndex(), defs == null ? result : defs[0], site);
    } else {
      return insts.NewInstruction(
          iIndex(), defs == null ? result : defs[0], site, uses == null ? params : uses);
    }
  }

  @Override
  public String toString(SymbolTable symbolTable) {
    return getValueString(symbolTable, result)
        + " = new "
        + site.getDeclaredType()
        + '@'
        + site.getProgramCounter()
        + (params == null ? "" : array2String(params, symbolTable));
  }

  private String array2String(int[] params, SymbolTable symbolTable) {
    StringBuilder result = new StringBuilder();
    for (int param : params) {
      result.append(getValueString(symbolTable, param));
      result.append(' ');
    }
    return result.toString();
  }

  @Override
  public void visit(IVisitor v) {
    if (v == null) {
      throw new IllegalArgumentException("v is null");
    }
    v.visitNew(this);
  }

  /** @see com.ibm.wala.ssa.SSAInstruction#getDef() */
  @Override
  public boolean hasDef() {
    return true;
  }

  @Override
  public int getDef() {
    return result;
  }

  @Override
  public int getDef(int i) {
    assert i == 0;
    return result;
  }

  @Override
  public int getNumberOfDefs() {
    return 1;
  }

  /** @return TypeReference */
  public TypeReference getConcreteType() {
    return site.getDeclaredType();
  }

  public NewSiteReference getNewSite() {
    return site;
  }

  @Override
  public int hashCode() {
    return result * 7529 + site.getDeclaredType().hashCode();
  }

  @Override
  public int getNumberOfUses() {
    return params == null ? 0 : params.length;
  }

  @Override
  public int getUse(int j) {
    assert params != null : "expected params but got null in " + this;
    assert params.length > j : "found too few parameters";
    return params[j];
  }

  @Override
  public boolean isPEI() {
    return true;
  }

  @Override
  public boolean isFallThrough() {
    return true;
  }
}
