// File RTCG4B.java --- the power example
// sestoft@dina.kvl.dk * 2002-09-17

// Using BCEL from http://jakarta.apache.org/bcel/

import org.apache.bcel.*;		// Constants
import org.apache.bcel.classfile.*;	// JavaClass
import org.apache.bcel.generic.*;	// ClassGen, MethodGen, instructions
import java.io.*;

public class RTCG4B {
  public static void main(String[] args) 
    throws IOException, NoSuchMethodException, IllegalAccessException, 
           java.lang.reflect.InvocationTargetException {
    int count = Integer.parseInt(args[0]);
    int n = 16;
    ClassGen cg = new ClassGen("MyClass", "java.lang.Object",
			       "<generated>", 
			       Constants.ACC_PUBLIC | Constants.ACC_SUPER, 
			       null);
    ConstantPoolGen cp = cg.getConstantPool();
    {
      // Build: public static int MyPower(int x) { ... }
      InstructionList il = new InstructionList();
      MethodGen mg = new MethodGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
				   Type.INT,
				   new Type[] { Type.INT },
				   new String[] { "x" },
				   "MyPower",
				   "MyClass",
				   il, cp);
      PowerGen(il, mg, n);
      mg.setMaxStack();
      cg.addMethod(mg.getMethod());
    }
    // Get the generated class
    JavaClass clazz = cg.getJavaClass();
    // Output class in human-readable format:
    System.out.println(clazz);
    // Output method body in human-readable format:
    System.out.println(clazz.getMethods()[0].getCode());
    // Output class file to class file on disk:
    clazz.dump("MyClass.class");
    // Output class file to array:
    byte[] classFile = clazz.getBytes();
    // Load the class file from byte array into the JVM 
    Class ty = new ArrayClassLoader().loadClass("MyClass", classFile);
    // Get the MyMethod(int):
    java.lang.reflect.Method m = 
      ty.getMethod("MyPower", new Class[] { int.class }); 
    // Call the method:    
    System.out.println(m.invoke(null, new Object[] { new Integer(3) }));
  }

  public static int Power(int n, int x) {
    int p;
    p = 1;
    while (n > 0) {
      if (n % 2 == 0) 
        { x = x * x; n = n / 2; }
      else 
        { p = p * x; n = n - 1; }
    }
    return p;
  }

  public static void PowerGen(InstructionList il, MethodGen mg, int n) {
    final int x = 0;			// x is local var 0
    LocalVariableGen varp = mg.addLocalVariable("p", Type.INT, null, null);
    final int p = varp.getIndex();
    varp.setStart(il.append(new ICONST(1)));
    il.append(new ISTORE(p));           // p = 1;
    while (n > 0) {
      if (n % 2 == 0) { 
	il.append(new ILOAD(x));        // load x
	il.append(new ILOAD(x));        // load x
	il.append(new IMUL());
	il.append(new ISTORE(x));       // x = x * x
        n = n / 2; 
      } else { 
	il.append(new ILOAD(p));        // load p
	il.append(new ILOAD(x));        // load x
	il.append(new IMUL());
	il.append(new ISTORE(p));	// p = p * x
        n = n - 1; 
      }
    }
    il.append(new ILOAD(p));
    il.append(new IRETURN());           // return p;
  }
}

// This is needed because defineClass is protected in java.lang.ClassLoader:

class ArrayClassLoader extends ClassLoader {
  public Class loadClass(String name, byte[] classFile) {
    return defineClass(name, classFile, 0, classFile.length);
  }
}

