/*
 * 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.shrike.shrikeBT.shrikeCT.tools;

import com.ibm.wala.shrike.shrikeBT.Decoder.InvalidBytecodeException;
import com.ibm.wala.shrike.shrikeBT.IInstruction;
import com.ibm.wala.shrike.shrikeBT.InvokeDynamicInstruction;
import com.ibm.wala.shrike.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrike.shrikeBT.shrikeCT.ClassInstrumenter;
import com.ibm.wala.shrike.shrikeBT.shrikeCT.OfflineInstrumenter;
import com.ibm.wala.shrike.shrikeCT.ClassReader;
import com.ibm.wala.shrike.shrikeCT.CodeReader;
import com.ibm.wala.shrike.shrikeCT.InvalidClassFileException;
import java.io.BufferedWriter;
import java.io.File;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.lang.invoke.CallSite;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;

public class BootstrapDumper {
  private final PrintWriter w;

  /** Get ready to print a class to the given output stream. */
  public BootstrapDumper(PrintWriter w) {
    this.w = w;
  }

  public static void main(String[] args) throws Exception {
    OfflineInstrumenter oi = new OfflineInstrumenter();
    String[] classpathEntries = oi.parseStandardArgs(args);

    PrintWriter w = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));

    BootstrapDumper p = new BootstrapDumper(w);

    URL[] urls = new URL[classpathEntries.length - 1];
    for (int i = 1; i < classpathEntries.length; i++) {
      System.err.println(classpathEntries[i]);
      File f = new File(classpathEntries[i]);
      assert f.exists();
      urls[i - 1] = f.toURI().toURL();
    }
    try (final URLClassLoader image =
        URLClassLoader.newInstance(urls, BootstrapDumper.class.getClassLoader().getParent())) {
      System.err.println(image);

      ClassInstrumenter ci;
      oi.beginTraversal();
      while ((ci = oi.nextClass()) != null) {
        try {
          p.doClass(image, ci.getReader());
        } finally {
          w.flush();
        }
      }
    }

    oi.close();
  }

  private void dumpAttributes(Class<?> cl, ClassReader.AttrIterator attrs)
      throws InvalidClassFileException, InvalidBytecodeException, ClassNotFoundException,
          NoSuchMethodException, SecurityException, IllegalAccessException,
          IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
    for (; attrs.isValid(); attrs.advance()) {
      String name = attrs.getName();
      if (name.equals("Code")) {
        CodeReader code = new CodeReader(attrs);

        CTDecoder decoder = new CTDecoder(code);
        decoder.decode();
        IInstruction[] insts = decoder.getInstructions();
        for (IInstruction inst : insts) {
          if (inst instanceof InvokeDynamicInstruction) {
            CallSite target = ((InvokeDynamicInstruction) inst).bootstrap(cl);
            w.println(target.dynamicInvoker());
            w.println(target.getTarget());
            /*
             * only in Java 8.  Uncomment when we mandate Java 8.
            try {
              w.println(MethodHandles.reflectAs(Method.class, target.dynamicInvoker()));
            } catch (Throwable e) {
              System.out.println(e);
            }
            */
          }
        }
      }
    }
  }

  /**
   * Print a class.
   *
   * @throws IllegalArgumentException if cr is null
   */
  public void doClass(ClassLoader image, final ClassReader cr)
      throws InvalidClassFileException, InvalidBytecodeException, ClassNotFoundException,
          NoSuchMethodException, SecurityException, IllegalAccessException,
          IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
    if (cr == null) {
      throw new IllegalArgumentException("cr is null");
    }

    ClassReader.AttrIterator attrs = new ClassReader.AttrIterator();
    cr.initClassAttributeIterator(attrs);
    int methodCount = cr.getMethodCount();

    for (int i = 0; i < methodCount; i++) {
      cr.initMethodAttributeIterator(i, attrs);
      dumpAttributes(Class.forName(cr.getName().replace('/', '.'), false, image), attrs);
    }
  }
}
