/***
 * ASM: a very small and fast Java bytecode manipulation framework
 * Copyright (c) 2000-2011 INRIA, France Telecom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.objectweb.asm;

import gnu.bytecode.Access;
import gnu.bytecode.ClassType;
import gnu.bytecode.CodeAttr;
import gnu.bytecode.Field;
import gnu.bytecode.Method;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.Arrays;

import jbet.Descriptor;
import jbet.Instruction;
import jbet.Snippit;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.PUSH;
import org.cojen.classfile.MethodInfo;
import org.cojen.classfile.Modifiers;
import org.cojen.classfile.TypeDesc;
import org.gjt.jclasslib.bytecode.ImmediateByteInstruction;
import org.gjt.jclasslib.bytecode.ImmediateShortInstruction;
import org.gjt.jclasslib.bytecode.SimpleInstruction;
import org.gjt.jclasslib.io.ByteCodeWriter;
import org.gjt.jclasslib.structures.AccessFlags;
import org.gjt.jclasslib.structures.AttributeInfo;
import org.gjt.jclasslib.structures.CPInfo;
import org.gjt.jclasslib.structures.ConstantPoolUtil;
import org.gjt.jclasslib.structures.InvalidByteCodeException;
import org.gjt.jclasslib.structures.attributes.CodeAttribute;
import org.gjt.jclasslib.structures.attributes.SourceFileAttribute;
import org.gjt.jclasslib.structures.constants.ConstantStringInfo;
import org.mozilla.classfile.ByteCode;
import org.mozilla.classfile.ClassFileWriter;

import alt.jiapi.reflect.InstructionFactory;
import alt.jiapi.reflect.InstructionList;
import alt.jiapi.reflect.JiapiClass;
import alt.jiapi.reflect.JiapiMethod;
import alt.jiapi.reflect.MethodExistsException;
import alt.jiapi.reflect.Signature;

import com.claritysys.jvm.builder.CodeBuilder;
import com.claritysys.jvm.classfile.CfMethod;
import com.claritysys.jvm.classfile.ClassFile;
import com.claritysys.jvm.classfile.ConstantPool;
import com.claritysys.jvm.classfile.JVM;

/**
 * Performance tests for frameworks that can only do bytecode generation.
 * 
 * @author Eric Bruneton
 */
public class GenPerfTest {

    final static int N = 100000;

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; ++i) {
            asmTest();
        }
        for (int i = 0; i < 5; ++i) {
            gnuByteCodeTest();
        }
        for (int i = 0; i < 5; ++i) {
            csgBytecodeTest();
        }
        for (int i = 0; i < 5; ++i) {
            cojenTest();
        }
        for (int i = 0; i < 5; ++i) {
            jbetTest();
        }
        for (int i = 0; i < 5; ++i) {
            jClassLibTest();
        }
        for (int i = 0; i < 5; ++i) {
            jiapiTest();
        }
        for (int i = 0; i < 5; ++i) {
            mozillaClassFileTest();
        }
        for (int i = 0; i < 5; ++i) {
            bcelTest();
        }
        for (int i = 0; i < 5; ++i) {
            aspectjBcelTest();
        }
    }

    static void asmTest() {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            asmHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("ASM generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void gnuByteCodeTest() {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            gnuByteCodeHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("gnu.bytecode generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void csgBytecodeTest() {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            csgBytecodeHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("CSG bytecode generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void cojenTest() throws IOException {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            cojenHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("Cojen generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void jbetTest() throws IOException {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            jbetHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("JBET generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void jClassLibTest() throws IOException, InvalidByteCodeException {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            jClassLibHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("JClassLib generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void jiapiTest() throws MethodExistsException {
        int N = 1000;
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            jiapiHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("Jiapi generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void mozillaClassFileTest() {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            mozillaClassFileHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("Mozilla Class File generation time: " + ((float) t)
                / N + " ms/class");
    }

    static void bcelTest() {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            bcelHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("BCEL generation time: " + ((float) t) / N
                + " ms/class");
    }

    static void aspectjBcelTest() {
        long t = System.currentTimeMillis();
        for (int i = 0; i < N; ++i) {
            aspectjBcelHelloWorld();
        }
        t = System.currentTimeMillis() - t;
        System.out.println("AspectJ BCEL generation time: " + ((float) t) / N
                + " ms/class");
    }

    static byte[] asmHelloWorld() {
        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

        cw.visit(Opcodes.V1_1, Opcodes.ACC_PUBLIC, "HelloWorld", null,
                "java/lang/Object", null);
        cw.visitSource("HelloWorld.java", null);

        MethodVisitor mw = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V",
                null, null);
        mw.visitVarInsn(Opcodes.ALOAD, 0);
        mw.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>",
                "()V", false);
        mw.visitInsn(Opcodes.RETURN);
        mw.visitMaxs(0, 0);
        mw.visitEnd();

        mw = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "main",
                "([Ljava/lang/String;)V", null, null);
        mw.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out",
                "Ljava/io/PrintStream;");
        mw.visitLdcInsn("Hello world!");
        mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream",
                "println", "(Ljava/lang/String;)V", false);
        mw.visitInsn(Opcodes.RETURN);
        mw.visitMaxs(0, 0);
        mw.visitEnd();

        return cw.toByteArray();
    }

    static Method objectCtor = gnu.bytecode.Type.pointer_type
            .getDeclaredMethod("<init>", 0);

    static Field outField = ClassType.make("java.lang.System").getField("out");

    static Method printlnMethod = ClassType.make("java.io.PrintStream")
            .getDeclaredMethod("println",
                    new gnu.bytecode.Type[] { gnu.bytecode.Type.string_type });

    static byte[] gnuByteCodeHelloWorld() {
        ClassType c = new ClassType("HelloWorld");
        c.setSuper("java.lang.Object");
        c.setModifiers(Access.PUBLIC);
        c.setSourceFile("HelloWorld.java");

        Method m = c.addMethod("<init>", "()V", Access.PUBLIC);
        CodeAttr code = m.startCode();
        code.pushScope();
        code.emitPushThis();
        code.emitInvokeSpecial(objectCtor);
        code.emitReturn();
        code.popScope();

        m = c.addMethod("main", "([Ljava/lang/String;)V", Access.PUBLIC
                | Access.STATIC);
        code = m.startCode();
        code.pushScope();
        code.emitGetStatic(outField);
        code.emitPushString("Hello world!");
        code.emitInvokeVirtual(printlnMethod);
        code.emitReturn();
        code.popScope();

        return c.writeToArray();
    }

    static byte[] csgBytecodeHelloWorld() {
        ClassFile cf = new ClassFile("HelloWorld", "java/lang/Object",
                "HelloWorld.java");
        ConstantPool cp = cf.getConstantPool();

        CfMethod method = cf.addMethod(JVM.ACC_PUBLIC, "<init>", "()V");
        CodeBuilder code = new CodeBuilder(method);
        code.add(JVM.ALOAD_0);
        code.add(JVM.INVOKESPECIAL,
                cp.addMethodRef(false, "java/lang/Object", "<init>", "()V"));
        code.add(JVM.RETURN);
        code.flush();

        method = cf.addMethod(JVM.ACC_PUBLIC + JVM.ACC_STATIC, "main",
                "([Ljava/lang/String;)V");
        code = new CodeBuilder(method);
        code.add(JVM.GETSTATIC, cp.addFieldRef("java/lang/System", "out",
                "Ljava/io/PrintStream;"));
        code.add(JVM.LDC, "Hello world!");
        code.add(JVM.INVOKEVIRTUAL, cp.addMethodRef(false,
                "java/io/PrintStream", "println", "(Ljava/lang/String;)V"));
        code.add(JVM.RETURN);
        code.flush();

        return cf.writeToArray();
    }

    static TypeDesc printStream = TypeDesc.forClass("java.io.PrintStream");

    static byte[] cojenHelloWorld() throws IOException {
        org.cojen.classfile.ClassFile cf = new org.cojen.classfile.ClassFile(
                "HelloWorld");

        cf.setSourceFile("HelloWorld.java");

        cf.addDefaultConstructor();

        TypeDesc[] params = new TypeDesc[] { TypeDesc.STRING.toArrayType() };
        MethodInfo mi = cf.addMethod(Modifiers.PUBLIC_STATIC, "main", null,
                params);
        org.cojen.classfile.CodeBuilder b = new org.cojen.classfile.CodeBuilder(
                mi);
        b.loadStaticField("java.lang.System", "out", printStream);
        b.loadConstant("Hello world!");
        b.invokeVirtual(printStream, "println", null,
                new TypeDesc[] { TypeDesc.STRING });
        b.returnVoid();

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        cf.writeTo(bos);

        return bos.toByteArray();
    }

    static Descriptor emptyDesc = new Descriptor("()V");

    static Descriptor mainDesc = new Descriptor("([Ljava/lang/String;)V");

    static Descriptor printlnDesc = new Descriptor("(Ljava/lang/String;)V");

    static jbet.Type printStreamType = new jbet.Type("Ljava/io/PrintStream;");

    static byte[] jbetHelloWorld() throws IOException {
        jbet.ClassInfo ci = new jbet.ClassInfo(null, "HelloWorld");

        ci.sourceFile = "HelloWorld.java";

        jbet.MethodInfo mi = new jbet.MethodInfo("<init>", emptyDesc,
                jbet.MethodInfo.ACC_PUBLIC);
        mi.code = new Snippit();
        mi.code.push(new Instruction().setAload(0));
        mi.code.push(new Instruction().setInvokeSpecial("java/lang/Object",
                "<init>", emptyDesc));
        mi.code.push(new Instruction().setReturn());
        mi.maxLocals = 1;
        mi.maxStack = 1;
        ci.addMethod(mi);

        mi = new jbet.MethodInfo("main", mainDesc, jbet.MethodInfo.ACC_PUBLIC
                | jbet.MethodInfo.ACC_STATIC);
        mi.code = new Snippit();
        mi.code.push(new Instruction().setGetstatic("java/lang/System", "out",
                printStreamType));
        mi.code.push(new Instruction().setSpush("Hello world!"));
        mi.code.push(new Instruction().setInvokeVirtual("java/io/PrintStream",
                "println", printlnDesc));
        mi.maxLocals = 1;
        mi.maxStack = 2;
        ci.addMethod(mi);

        ci.resolveConstants();

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ci.writeFile(bos);

        return bos.toByteArray();
    }

    static byte[] jClassLibHelloWorld() throws InvalidByteCodeException,
            IOException {
        org.gjt.jclasslib.structures.ClassFile cf = new org.gjt.jclasslib.structures.ClassFile();
        cf.setConstantPool(new CPInfo[0]);
        ConstantPoolUtil.addConstantUTF8Info(cf, "", 0); // dummy constant
        cf.setMajorVersion(45);
        cf.setMinorVersion(3);
        cf.setAccessFlags(AccessFlags.ACC_PUBLIC);
        cf.setThisClass(ConstantPoolUtil.addConstantClassInfo(cf, "HelloWorld",
                0));
        cf.setSuperClass(ConstantPoolUtil.addConstantClassInfo(cf,
                "java/lang/Object", 0));

        SourceFileAttribute sa = new SourceFileAttribute();
        sa.setAttributeNameIndex(ConstantPoolUtil.addConstantUTF8Info(cf,
                SourceFileAttribute.ATTRIBUTE_NAME, 0));
        sa.setSourcefileIndex(ConstantPoolUtil.addConstantUTF8Info(cf,
                "HelloWorld.java", 0));

        org.gjt.jclasslib.structures.MethodInfo mi1 = new org.gjt.jclasslib.structures.MethodInfo();
        mi1.setAccessFlags(AccessFlags.ACC_PUBLIC);
        mi1.setNameIndex(ConstantPoolUtil.addConstantUTF8Info(cf, "<init>", 0));
        mi1.setDescriptorIndex(ConstantPoolUtil.addConstantUTF8Info(cf, "()V",
                0));
        CodeAttribute ca1 = new CodeAttribute();
        ca1.setAttributeNameIndex(ConstantPoolUtil.addConstantUTF8Info(cf,
                CodeAttribute.ATTRIBUTE_NAME, 0));
        ca1.setCode(ByteCodeWriter.writeByteCode(Arrays
                .asList(new org.gjt.jclasslib.bytecode.AbstractInstruction[] {
                        new SimpleInstruction(
                                org.gjt.jclasslib.bytecode.Opcodes.OPCODE_ALOAD_0),
                        new ImmediateShortInstruction(
                                org.gjt.jclasslib.bytecode.Opcodes.OPCODE_INVOKESPECIAL,
                                ConstantPoolUtil.addConstantMethodrefInfo(cf,
                                        "java/lang/Object", "<init>", "()V", 0)),
                        new SimpleInstruction(
                                org.gjt.jclasslib.bytecode.Opcodes.OPCODE_RETURN) })));
        ca1.setMaxStack(1);
        ca1.setMaxLocals(1);
        mi1.setAttributes(new AttributeInfo[] { ca1 });

        ConstantStringInfo s = new ConstantStringInfo();
        s.setStringIndex(ConstantPoolUtil.addConstantUTF8Info(cf,
                "Hello world!", 0));

        org.gjt.jclasslib.structures.MethodInfo mi2 = new org.gjt.jclasslib.structures.MethodInfo();
        mi2.setAccessFlags(AccessFlags.ACC_PUBLIC | AccessFlags.ACC_STATIC);
        mi2.setNameIndex(ConstantPoolUtil.addConstantUTF8Info(cf, "main", 0));
        mi2.setDescriptorIndex(ConstantPoolUtil.addConstantUTF8Info(cf,
                "([Ljava/lang/String;)V", 0));
        CodeAttribute ca2 = new CodeAttribute();
        ca2.setAttributeNameIndex(ConstantPoolUtil.addConstantUTF8Info(cf,
                CodeAttribute.ATTRIBUTE_NAME, 0));
        ca2.setCode(ByteCodeWriter.writeByteCode(Arrays
                .asList(new org.gjt.jclasslib.bytecode.AbstractInstruction[] {
                        new ImmediateShortInstruction(
                                org.gjt.jclasslib.bytecode.Opcodes.OPCODE_GETSTATIC,
                                ConstantPoolUtil.addConstantFieldrefInfo(cf,
                                        "java/lang/System", "out",
                                        "Ljava/io/PrintStream;", 0)),
                        new ImmediateByteInstruction(
                                org.gjt.jclasslib.bytecode.Opcodes.OPCODE_LDC,
                                false, ConstantPoolUtil.addConstantPoolEntry(
                                        cf, s, 0)),
                        new ImmediateShortInstruction(
                                org.gjt.jclasslib.bytecode.Opcodes.OPCODE_INVOKEVIRTUAL,
                                ConstantPoolUtil.addConstantMethodrefInfo(cf,
                                        "java/io/PrintStream", "println",
                                        "(Ljava/lang/String;)V", 0)) })));
        ca2.setMaxStack(2);
        ca2.setMaxLocals(1);
        mi2.setAttributes(new AttributeInfo[] { ca2 });

        cf.setMethods(new org.gjt.jclasslib.structures.MethodInfo[] { mi1, mi2 });
        cf.setAttributes(new AttributeInfo[] { sa });

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        cf.write(dos);
        dos.close();

        return bos.toByteArray();
    }

    static Signature emptySig = new Signature("()V");

    static Signature mainSig = new Signature("([Ljava/lang/String;)V");

    static Signature printlnSig = new Signature("(Ljava/lang/String;)V");

    static byte[] jiapiHelloWorld() throws MethodExistsException {
        JiapiClass c = JiapiClass.createClass("HelloWorld");

        // No API to set SourceFile!

        JiapiMethod method = c.addMethod(Modifier.PUBLIC, "<init>", emptySig);
        InstructionList il = method.getInstructionList();
        InstructionFactory iFactory = il.getInstructionFactory();
        il.add(iFactory.aload(0));
        il.add(iFactory.invoke(0, "java/lang/Object", "<init>", emptySig));
        il.add(iFactory.returnMethod(method));

        method = c
                .addMethod(Modifier.PUBLIC | Modifier.STATIC, "main", mainSig);
        il = method.getInstructionList();
        iFactory = il.getInstructionFactory();
        il.add(iFactory.getField(Modifier.STATIC, "java/lang/System", "out",
                "Ljava/io/PrintStream;"));
        il.add(iFactory.pushConstant("Hello world!"));
        il.add(iFactory.invoke(0, "java/io/PrintStream", "println", printlnSig));
        il.add(iFactory.returnMethod(method));

        return c.getByteCode();
    }

    static byte[] mozillaClassFileHelloWorld() {
        ClassFileWriter c = new ClassFileWriter("HelloWorld",
                "java/lang/Object", "HelloWorld.java");

        c.startMethod("<init>", "()V", ClassFileWriter.ACC_PUBLIC);
        c.addLoadThis();
        c.addInvoke(ByteCode.INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
        c.add(ByteCode.RETURN);
        c.stopMethod((short) 1);

        c.startMethod(
                "main",
                "()V",
                (short) (ClassFileWriter.ACC_PUBLIC | ClassFileWriter.ACC_STATIC));
        c.add(ByteCode.GETSTATIC, "java/lang/System", "out",
                "Ljava/io/PrintStream;");
        c.addPush("Hello world!");
        c.addInvoke(ByteCode.INVOKEVIRTUAL, "java/io/PrintStream", "println",
                "(Ljava/lang/String;)V");
        c.add(ByteCode.RETURN);
        c.stopMethod((short) 1);

        return c.toByteArray();
    }

    static org.apache.bcel.generic.Type printStreamT = org.apache.bcel.generic.Type
            .getType("Ljava/io/PrintStream;");

    static byte[] bcelHelloWorld() {
        ClassGen cg = new ClassGen("HelloWorld", "java/lang/Object",
                "HelloWorld.java", Constants.ACC_PUBLIC, null);

        cg.addEmptyConstructor(Constants.ACC_PUBLIC);

        ConstantPoolGen cp = cg.getConstantPool();
        org.apache.bcel.generic.InstructionList il = new org.apache.bcel.generic.InstructionList();
        org.apache.bcel.generic.InstructionFactory factory = new org.apache.bcel.generic.InstructionFactory(
                cg);

        MethodGen mg = new MethodGen(Constants.ACC_STATIC
                | Constants.ACC_PUBLIC, org.apache.bcel.generic.Type.VOID,
                new org.apache.bcel.generic.Type[] { new ArrayType(
                        org.apache.bcel.generic.Type.STRING, 1) }, null,
                "main", "HelloWorld", il, cp);
        il.append(factory.createGetStatic("java/lang/System", "out",
                printStreamT));
        il.append(new PUSH(cp, "Hello world!"));
        il.append(factory
                .createInvoke(
                        "java.io.PrintStream",
                        "println",
                        org.apache.bcel.generic.Type.VOID,
                        new org.apache.bcel.generic.Type[] { org.apache.bcel.generic.Type.STRING },
                        Constants.INVOKESPECIAL));

        mg.setMaxStack();
        cg.addMethod(mg.getMethod());

        return cg.getJavaClass().getBytes();
    }

    static org.aspectj.apache.bcel.generic.Type printStreamAT = org.aspectj.apache.bcel.generic.Type
            .getType("Ljava/io/PrintStream;");

    static byte[] aspectjBcelHelloWorld() {
        org.aspectj.apache.bcel.generic.ClassGen cg = new org.aspectj.apache.bcel.generic.ClassGen(
                "HelloWorld", "java/lang/Object", "HelloWorld.java",
                Constants.ACC_PUBLIC, null);

        cg.addEmptyConstructor(Constants.ACC_PUBLIC);

        org.aspectj.apache.bcel.generic.ConstantPoolGen cp = cg
                .getConstantPool();
        org.aspectj.apache.bcel.generic.InstructionList il = new org.aspectj.apache.bcel.generic.InstructionList();
        org.aspectj.apache.bcel.generic.InstructionFactory factory = new org.aspectj.apache.bcel.generic.InstructionFactory(
                cg);

        org.aspectj.apache.bcel.generic.MethodGen mg = new org.aspectj.apache.bcel.generic.MethodGen(
                Constants.ACC_STATIC | Constants.ACC_PUBLIC,
                org.aspectj.apache.bcel.generic.Type.VOID,
                new org.aspectj.apache.bcel.generic.Type[] { new org.aspectj.apache.bcel.generic.ArrayType(
                        org.aspectj.apache.bcel.generic.Type.STRING, 1) },
                null, "main", "HelloWorld", il, cp);
        il.append(factory.createGetStatic("java/lang/System", "out",
                printStreamAT));
        il.append(new org.aspectj.apache.bcel.generic.PUSH(cp, "Hello world!"));
        il.append(factory
                .createInvoke(
                        "java.io.PrintStream",
                        "println",
                        org.aspectj.apache.bcel.generic.Type.VOID,
                        new org.aspectj.apache.bcel.generic.Type[] { org.aspectj.apache.bcel.generic.Type.STRING },
                        Constants.INVOKESPECIAL));

        mg.setMaxStack();
        cg.addMethod(mg.getMethod());

        return cg.getJavaClass().getBytes();
    }
}
