/*******************************************************************************
 * 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.ipa.summaries;

import java.util.Map;

import com.ibm.wala.cfg.AbstractCFG;
import com.ibm.wala.cfg.InducedCFG;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.ipa.callgraph.Context;
import com.ibm.wala.ssa.ConstantValue;
import com.ibm.wala.ssa.IR;
import com.ibm.wala.ssa.SSACFG;
import com.ibm.wala.ssa.SSAIndirectionData;
import com.ibm.wala.ssa.SSAIndirectionData.Name;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAOptions;
import com.ibm.wala.ssa.SSAPhiInstruction;
import com.ibm.wala.ssa.SSAPiInstruction;
import com.ibm.wala.ssa.SymbolTable;
import com.ibm.wala.util.debug.Assertions;

public class SyntheticIR extends IR {

  private final static boolean PARANOID = true;

  /**
   * Create an SSA form, induced over a list of instructions provided externally. This entrypoint is often used for, e.g., native
   * method models
   * 
   * @param method the method to construct SSA form for
   * @param context the governing context
   * @param instructions the SSA instructions which define the body of the method
   * @param constants a Map giving information on constant values for the symbol table
   * @throws AssertionError if method is null
   */
  public SyntheticIR(IMethod method, Context context, AbstractCFG cfg, SSAInstruction[] instructions, SSAOptions options,
      Map<Integer, ConstantValue> constants) throws AssertionError {
    super(method, instructions, makeSymbolTable(method, instructions, constants, cfg), new SSACFG(method, cfg, instructions),
        options);
    if (PARANOID) {
      repOK(instructions);
    }

    setupLocationMap();
  }

  /**
   * throw an assertion if the instruction array contains a phi instruction
   */
  private void repOK(SSAInstruction[] instructions) {
    for (SSAInstruction s : instructions) {
      if (s instanceof SSAPhiInstruction) {
        Assertions.UNREACHABLE();
      }
      if (s instanceof SSAPiInstruction) {
        Assertions.UNREACHABLE();
      }
    }
  }

  /**
   * Set up the symbol table according to statements in the IR
   * 
   * @param constants Map: value number (Integer) -> ConstantValue
   */
  private static SymbolTable makeSymbolTable(IMethod method, SSAInstruction[] instructions, Map<Integer, ConstantValue> constants,
      AbstractCFG cfg) {
    if (method == null) {
      throw new IllegalArgumentException("null method");
    }
    SymbolTable symbolTable = new SymbolTable(method.getNumberOfParameters());

    // simulate allocation of value numbers
    for (int i = 0; i < instructions.length; i++) {
      if (instructions[i] != null) {
        SSAInstruction s = instructions[i];
        updateForInstruction(constants, symbolTable, s);
      }
    }

    /**
     * In InducedCFGs, we have nulled out phi instructions from the instruction array ... so go back and retrieve them now.
     */
    if (cfg instanceof InducedCFG) {
      InducedCFG icfg = (InducedCFG) cfg;
      for (SSAPhiInstruction phi : icfg.getAllPhiInstructions()) {
        updateForInstruction(constants, symbolTable, phi);
      }
    }

    return symbolTable;
  }

  private static void updateForInstruction(Map<Integer, ConstantValue> constants, SymbolTable symbolTable, SSAInstruction s) {
    for (int j = 0; j < s.getNumberOfDefs(); j++) {
      symbolTable.ensureSymbol(s.getDef(j));
    }
    for (int j = 0; j < s.getNumberOfUses(); j++) {
      int vn = s.getUse(j);
      symbolTable.ensureSymbol(vn);
      if (constants != null && constants.containsKey(new Integer(vn)))
        symbolTable.setConstantValue(vn, constants.get(new Integer(vn)));
    }
  }

  /**
   * This returns "", as synthetic IRs have no line numbers right now.
   */
  @Override
  protected String instructionPosition(int instructionIndex) {
    return "";
  }

  /**
   * This returns null, as synthetic IRs have no local names right now.
   */
  @Override
  public SSA2LocalMap getLocalMap() {
    return null;
  }

  @Override
  protected SSAIndirectionData<Name> getIndirectionData() {
     return null;
  }
}
