/* * * * * *_* * * * * * * * * * * * * * * * * * * * * * * *\
 *         |_)                           _                 *
 *  _  _  _ _ _   _ _  _  _ _____  ___ _| |_ _____  ____   *
 * | ||_|| | ( \ / ) ||_|| (____ |/___|_   _) ___ |/ ___)  *
 * | |   | | |) X (| |   | / ___ |___ | | |_| ____| |      *
 * |_|   |_|_(_/ \_)_|   |_\_____(___/   \__)_____)_|      *
 *                                                         *
 *                         See LICENSE for license details *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

package xt.to.cs.mixmaster;

import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Set;

import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;
import org.objectweb.asm.commons.EmptyVisitor;
import org.objectweb.asm.commons.RemappingMethodAdapter;
import org.objectweb.asm.commons.SimpleRemapper;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;

/**
 * A tool that allows you to generate a new class by mixing together code from multiple classes.
 * @author Andy Scott
 */
public class MixMaster {
  
  protected static final EmptyVisitor EMPTY_VISITOR = new EmptyVisitor();
  
  protected int version = Opcodes.V1_6;
  protected int access = Opcodes.ACC_PUBLIC;
  protected String name;
  protected String signature = null;
  protected String superName = "java/lang/Object";
  protected Set<String> interfaces = new HashSet<>();
  protected ClassVisitor cv;
  protected Set<ClassNode> nodes;
  protected MixAdapter mixAdapter;
  protected Set<String> methods = new HashSet<>();
  protected ClassNode extendsNode;
  protected StripSupercallAdapter noSuperInitVisitor;
  protected MethodNode initNode;
  protected LinkedList<String> initMethods;
  
  protected class MixAdapter extends ClassAdapter {
    
    protected String className;
    
    @Override
    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
      this.className = name;
      System.out.println("C " + className);
    }
    
    @Override
    public void visitInnerClass(String name, String outerName, String innerName, int access) {
      System.out.println("INNER " + innerName);
      super.visitInnerClass(name, outerName, innerName, access);
    }

    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
      System.out.println("GRR " + name);
      if ("<init>".equals(name)) {
        
        if (initNode == null) {
          initNode = new MethodNode(access, name, desc, signature, exceptions);
          return new RemappingMethodAdapter(
              access, desc,
              initNode,
              new SimpleRemapper(className, MixMaster.this.name));
          }
        
        
        String newName = "#init#" + className.hashCode();
        initMethods.push(newName);
        MethodVisitor mv = super.visitMethod(Opcodes.ACC_PRIVATE, newName, desc, signature, exceptions);
        return new RemappingMethodAdapter(
            access, desc,
            new StripSupercallAdapter(mv, access, name, desc),
            new SimpleRemapper(className, MixMaster.this.name));

      }
      
      
      if (methods.add(name + desc)) {
        return new RemappingMethodAdapter(
            access, desc,
            super.visitMethod(access, name, desc, signature, exceptions),
            new SimpleRemapper(className, MixMaster.this.name));
      }
      return EMPTY_VISITOR;
    }

    @Override
    public void visitEnd() {
    }

    public MixAdapter(ClassVisitor cv) {
      super(cv);
    }
    
  }
  
  public MixMaster(String name, ClassVisitor cv) {
    this.name = name;
    this.cv = cv;
    this.mixAdapter = new MixAdapter(cv);
    this.nodes = new LinkedHashSet<>();
    this.initMethods = new LinkedList<>();
  }
  
  @SuppressWarnings("unchecked")
  public void mix(ClassNode node) {
    nodes.add(node);
    interfaces.addAll(node.interfaces);
  }
  
  public void extend(ClassNode node) {
    this.superName = node.name;
    this.extendsNode = node;
  }
  
  @SuppressWarnings("unchecked")
  public void generate() {
    cv.visit(version, access, name, signature, superName, interfaces.toArray(new String[]{}));
    
    for (ClassNode node : nodes)
      node.accept(mixAdapter);
    
    String[] exceptions = new String[initNode.exceptions.size()];
    initNode.exceptions.toArray(exceptions);
    MethodVisitor mv = cv.visitMethod(access,
            initNode.name,
            initNode.desc,
            initNode.signature,
            exceptions);
    
    initNode.accept(
        new AdviceAdapter(mv, initNode.access, initNode.name, initNode.desc) {
          @Override
          protected void onMethodEnter() {
            for (String initMethod : initMethods) {
              visitIntInsn(ALOAD, 0);
              visitMethodInsn(Opcodes.INVOKESPECIAL, MixMaster.this.name, initMethod, initNode.desc);
            }
          }
    });
    
    cv.visitEnd();
  }

}

@@ -2064,32 +2064,105 @@
 assName;%0D%0A    %0D%0A
+    public MixAdapter(ClassVisitor cv) %7B%0D%0A      super(cv);%0D%0A    %7D%0D%0A    %0D%0A
     @Override%0D%0A 
@@ -2314,292 +2314,8 @@
 e;%0D%0A
-      System.out.println(%22C %22 + className);%0D%0A    %7D%0D%0A    %0D%0A    @Override%0D%0A    public void visitInnerClass(String name, String outerName, String innerName, int access) %7B%0D%0A      System.out.println(%22INNER %22 + innerName);%0D%0A      super.visitInnerClass(name, outerName, innerName, access);%0D%0A
     
@@ -2455,50 +2455,8 @@
  %7B%0D%0A
-      System.out.println(%22GRR %22 + name);%0D%0A
     
@@ -3586,90 +3586,10 @@
 () %7B
-%0D%0A    %7D%0D%0A%0D%0A    public MixAdapter(ClassVisitor cv) %7B%0D%0A      super(cv);%0D%0A    %7D%0D%0A   
  
+%7D
 %0D%0A  
@@ -4477,22 +4477,16 @@
 ccess,%0D%0A
-      
       in
@@ -4497,38 +4497,32 @@
 de.name,%0D%0A      
-    
-  
 initNode.desc,%0D%0A
@@ -4517,30 +4517,24 @@
 Node.desc,%0D%0A
-      
       initNo
@@ -4554,22 +4554,16 @@
 %0D%0A      
-  
-    
 exceptio
@@ -4596,18 +4596,16 @@
 ccept(%0D%0A
-  
       ne
@@ -4682,18 +4682,16 @@
         
-  
 @Overrid
@@ -4693,18 +4693,16 @@
 erride%0D%0A
-  
         
@@ -4745,18 +4745,16 @@
         
-  
 for (Str
@@ -4794,26 +4794,24 @@
             
-  
 visitIntInsn
@@ -4819,26 +4819,24 @@
 ALOAD, 0);%0D%0A
-  
             
@@ -4930,31 +4930,27 @@
 %0D%0A          
-  
 %7D%0D%0A
-  
         %7D%0D%0A 

remove silly log messages