/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Common Public License (CPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/cpl1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.compilers.common;

import org.jikesrvm.VM;
import org.jikesrvm.adaptive.measurements.RuntimeMeasurements;
import org.jikesrvm.classloader.RVMMethod;
import org.jikesrvm.classloader.NormalMethod;

/**
 * This class enables an external driver to block recompile all
 * methods that have been dynamically compiled since the VM began
 * execution.  This support can be used to eliminate the effects
 * of early vs. late compilation by removing all dynamic linking
 * and "bad" class hierarchy based optimizations.
 * <p>
 */
public final class RecompilationManager {

  private static final boolean DEBUG = false;

  /**
   * Use the runtime compiler to forcibly recompile all dynamically
   * loaded methods
   */
  public static void recompileAllDynamicallyLoadedMethods(boolean report) {
    int numMethods = CompiledMethods.numCompiledMethods();
    for (int cmid = 1; cmid < numMethods; cmid++) {
      // To avoid the assertion for unused cmids
      CompiledMethod cpMeth = CompiledMethods.getCompiledMethodUnchecked(cmid);
      if (cpMeth == null) {
        if (DEBUG) {
          VM.sysWrite("Not recompiling method ID ", cmid, " because it has no compiledMethod\n");
        }
      } else {
        RVMMethod meth = cpMeth.getMethod();
        if (DEBUG) {
          VM.sysWrite("numMethods: " +
                      numMethods +
                      ", Inspecting cpMethod " +
                      cpMeth +
                      ", method: " +
                      cpMeth.getMethod() +
                      "(" +
                      cmid +
                      ")\n");
        }
        if (cpMeth.getCompilerType() == CompiledMethod.TRAP) {
          if (DEBUG) {
            VM.sysWrite("Not recompiling compiled method " +
                        cpMeth +
                        "(" +
                        cmid +
                        ") because it a TRAP, i.e. has no source code\n");
          }
        } else {
          if (meth.getDeclaringClass().isResolved()) {
            if (meth.getDeclaringClass().isInBootImage()) {
              if (DEBUG) {
                VM.sysWrite("Not recompiling bootimage method " + meth + "(" + cmid + ")\n");
              }
            } else {
              if (meth.isAbstract()) {
                if (DEBUG) VM.sysWrite("Not recompiling abstract method " + meth + "(" + cmid + ")\n");
              } else if (meth.isNative()) {
                if (DEBUG) VM.sysWrite("Not recompiling native method " + meth + "(" + cmid + ")\n");
              } else {
                if (DEBUG || report) VM.sysWrite("Recompiling " + meth + "(" + cmid + ") ");
                recompile((NormalMethod) meth);
                if (DEBUG || report) VM.sysWrite("...done\n");
              }
            }
          } else {
            if (DEBUG) VM.sysWrite("Class not resolved" + meth + "(" + cmid + ")\n");
          }
        }
      }
    }

    if (VM.BuildForAdaptiveSystem) {
      // clear profiling counter
      if (DEBUG || report) { VM.sysWrite("Reseting profiling information\n"); }
      RuntimeMeasurements.resetReportableObjects();
    }
  }

  /**
   * recompile and replace the argument method by invoking the runtime compiler
   */
  public static void recompile(NormalMethod meth) {
    try {
      CompiledMethod cm = RuntimeCompiler.compile(meth);
      meth.replaceCompiledMethod(cm);
    } catch (Throwable e) {
      VM.sysWrite("Failure while recompiling \"" + meth + "\" : " + e + "\n");
    }
  }
}

