/* package whatever; // don't place package name! */

import java.util.*;
import java.lang.*;
import java.io.*;
import java.nio.*;
import java.lang.reflect.*;

/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
	
	public static void main (String[] args) throws java.lang.Exception
	{
		// your code goes here
		MyClass mc = new MyClass();
		
		method_info mi_cnst = new method_info(
			method_info.ACC_PUBLIC,
			mc.addCP(new CONSTANT_Utf8_info("<init>")),
			mc.addCP(new CONSTANT_Utf8_info("()V")));
		mc.addMethod(mi_cnst);
		Code_attribute ca_cnst = new Code_attribute(mc);
		mi_cnst.add(ca_cnst);
		ca_cnst.addCode(0x2A); // aload_0
		ca_cnst.addCode(0xB7); // invokespecial
		ca_cnst.addCodeShort(
			mc.addCP(new CONSTANT_Methodref_info(
				mc, "java/lang/Object",
					"<init>", "()V")));
		ca_cnst.addCode(0xB1); // return
		
		method_info mi_main = new method_info(
			method_info.ACC_PUBLIC | method_info.ACC_STATIC,
			mc.addCP(new CONSTANT_Utf8_info("main")),
			mc.addCP(new CONSTANT_Utf8_info("([Ljava/lang/String;)V")));
		mc.addMethod(mi_main);
		Code_attribute ca_main = new Code_attribute(mc);
		mi_main.add(ca_main);
		ca_main.max_stack = 2;
		ca_main.addCode(0xB2); // getstatic
		ca_main.addCodeShort(
			mc.addCP(new CONSTANT_Fieldref_info(
				mc, "java/lang/System",
					"out", "Ljava/io/PrintStream;")));
		ca_main.addCode(0x12); // ldc
		ca_main.addCode(
			mc.addCP(new CONSTANT_String_info(mc, "Hello World!")));
		ca_main.addCode(0xB6); // invokevirtual
		ca_main.addCodeShort(
			mc.addCP(new CONSTANT_Methodref_info(
				mc, "java/io/PrintStream", 
					"println", "(Ljava/lang/String;)V"))); 
		ca_main.addCode(0xB1); // return
		

		// バイトコード生成！
		byte[] buf = mc.toBytes();
		
		
		// 生成したバイトコードを試すとこ！
		try {
			MyClassLoader loader = new MyClassLoader();
			Object hoge = loader.getHoge(buf).newInstance();
			Method[] mm = hoge.getClass().getMethods();

			mm[0].invoke(null, new String[1]);
			String[] ss = new String[1];
			hoge.getClass().getMethod("main", ss.getClass()).invoke(null, ss);
			
		} catch (Throwable e) {
			System.out.println("ロード失敗！！");
			System.out.println(e.toString());
		}
		
		System.out.println("--生成したバイトコード--");
		for (int i = 0; i < buf.length; i++) {
			System.out.printf("%02X ", buf[i]);
			if ((i & 0x7) == 0x7) {
				System.out.print(" ");
			}
			if ((i & 0xF) == 0xF) {
				System.out.println();
			}
			
		}
		
		System.out.println();
		System.out.println("--Constant pool--");
		int c = 1;
		for (Iterator<cp_info> i = mc.constant_pool.iterator()
				; i.hasNext(); c++) {
			System.out.println(c + ":: " + i.next().toString());		
		}
		
	}
	
	static class MyClassLoader extends ClassLoader {
		MyClassLoader() {}
		public Class<?> getHoge(byte[] b) {
//			return defineClass(b, 0, b.length);
			return defineClass("Hoge", b, 0, b.length);
		}
	}
	
	
	static class MyClass {
		public static final int ACC_PUBLIC = 0x01;
		public static final int ACC_SUPER  = 0x20;
		
		byte[] 	magic;
		int  	minor_version;
		int  	major_version;
		ArrayList<cp_info> constant_pool;
		int 	access_flags;
		int		this_class;
		int		super_class;
		int		interfaces_count;
		IntBuffer interfaces;
		ArrayList<field_info> fields;
		ArrayList<method_info> methods;
		ArrayList<attribute_info> attributes;
		{
			magic = new byte[4];
			magic[0] = (byte)0xCA;
			magic[1] = (byte)0xFE;
			magic[2] = (byte)0xBA;
			magic[3] = (byte)0xBE;
			
			access_flags = ACC_PUBLIC | ACC_SUPER;
			
			minor_version = 0;
			major_version = 0x31;
			
			constant_pool = new ArrayList<cp_info>();
			interfaces_count = 0;
			interfaces = IntBuffer.allocate(5);
			fields = null;
			methods = null;
			attributes = null;
		}
		
		MyClass() {
			this_class = addCP(
				new CONSTANT_Class_info(this, "Hoge"));
			super_class = addCP(
				new CONSTANT_Class_info(this, "java/lang/Object"));
			/*
			addAttribute(
				new SourceFile_attribute(this, "Hoge.java"));			
			*/
		}
		
		int addCP(cp_info cp) {
			int i = constant_pool.indexOf(cp);
			if (i < 0) {
				constant_pool.add(cp);
				return constant_pool.size();
			}
			return i + 1;
		}
		
		void addMethod(method_info mt) {
			if (methods == null) {
				methods = new ArrayList<method_info>();
			}
			methods.add(mt);
		}

		void addAttribute(attribute_info ab) {
			if (attributes == null) {
				attributes = new ArrayList<attribute_info>();
			}
			attributes.add(ab);
		}
		
		byte[] toBytes() throws IOException {
			ByteArrayOutputStream buf = null;
			DataOutputStream out = null;
			try {
				buf = new ByteArrayOutputStream();
				out = new DataOutputStream(buf);
				out.write(magic, 0, magic.length);
				out.writeShort(minor_version);
				out.writeShort(major_version);
				out.writeShort(constant_pool.size() + 1);
				for (Iterator<cp_info> i = constant_pool.iterator()
						; i.hasNext(); ) {
					i.next().write(out);			
				}
				out.writeShort(access_flags);
				out.writeShort(this_class);
				out.writeShort(super_class);
				
				out.writeShort(interfaces_count);
				for (int i = 0; i < interfaces_count; i++) {
					out.writeShort(interfaces.get(i));
				}
				
				if (fields == null) {
					out.writeShort(0);
				} else {
					out.writeShort(fields.size());
					for (Iterator<field_info> i = fields.iterator()
							; i.hasNext(); ) {
						i.next().write(out);
					}
				}

				if (methods == null) {
					out.writeShort(0);
				} else {
					out.writeShort(methods.size());
					for (Iterator<method_info> i = methods.iterator()
							; i.hasNext(); ) {
						i.next().write(out);
					}
				}
				
				if (attributes == null) {
					out.writeShort(0);
				} else {
					out.writeShort(attributes.size());
					for (Iterator<attribute_info> i = attributes.iterator()
							; i.hasNext(); ) {
						i.next().write(out);
					}
				}
				
				return buf.toByteArray();
			} finally {
				if (out != null) out.close();
				if (buf != null) buf.close();
			}
		}
	}
	
	static interface Icp_info {
		byte CONSTANT_Class = 7;
		byte CONSTANT_Fieldref = 9;
		byte CONSTANT_Methodref = 10;
		byte CONSTANT_InterfaceMethodref = 11;
		byte CONSTANT_String = 8;
		byte CONSTANT_Integer = 3;
		byte CONSTANT_Float = 4;
		byte CONSTANT_Long = 5;
		byte CONSTANT_Double = 6;
		byte CONSTANT_NameAndType = 12;
		byte CONSTANT_Utf8 = 1;
		void write(DataOutputStream out) throws IOException;
	}
	
	static abstract class cp_info implements Icp_info {
		public final byte tag;
		protected cp_info(byte tag) { this.tag = tag; }
	}
	
	static class CONSTANT_Class_info extends cp_info {
		public final int name_index;
		CONSTANT_Class_info(int i) { super(CONSTANT_Class); name_index = i; }
		CONSTANT_Class_info(MyClass mc, String c) {
			super(CONSTANT_Class);
			name_index = mc.addCP(new CONSTANT_Utf8_info(c));
		}
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeShort(name_index);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_Class_info)) return false;
			CONSTANT_Class_info v = (CONSTANT_Class_info)obj;
			return name_index == v.name_index;
		}
		public String toString() {
			return "Class " + name_index;
		}
	}
	static class CONSTANT_ref_info extends cp_info {
		public final int class_index;
		public final int name_and_type_index;
		protected CONSTANT_ref_info(byte tag, int a, int b) { super(tag);
			class_index = a; name_and_type_index = b;}
		CONSTANT_ref_info(byte t, MyClass mc, String c, String m, String d) {
			this(t
				, mc.addCP(new CONSTANT_Class_info(mc, c))
				, mc.addCP(new CONSTANT_NameAndType_info(mc, m, d)));
		}
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeShort(class_index);
			out.writeShort(name_and_type_index);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_ref_info)) return false;
			CONSTANT_ref_info v = (CONSTANT_ref_info)obj;
			return (class_index == v.class_index) && 
				(name_and_type_index == v.name_and_type_index);
		}
		public String toString() {
			return "c:" + class_index + " nat:" + name_and_type_index;
		}
	}
	static class CONSTANT_Fieldref_info extends CONSTANT_ref_info {
		CONSTANT_Fieldref_info(int a, int b) { super(CONSTANT_Fieldref,a,b); }
		CONSTANT_Fieldref_info(MyClass mc, String c, String m, String d) {
			super(CONSTANT_Fieldref, mc, c, m, d);
		}
		public String toString() {
			return "Field " + super.toString();
		}
	}
	static class CONSTANT_Methodref_info extends CONSTANT_ref_info {
		CONSTANT_Methodref_info(int a, int b) { super(CONSTANT_Methodref,a,b); }
		CONSTANT_Methodref_info(MyClass mc, String c, String m, String d) {
			super(CONSTANT_Methodref, mc, c, m, d);
		}
		public String toString() {
			return "Method " + super.toString();
		}
	}
	static class CONSTANT_InterfaceMethodref_info extends CONSTANT_ref_info {
		CONSTANT_InterfaceMethodref_info(int a, int b) { super(CONSTANT_InterfaceMethodref,a,b); }
	}
	static class CONSTANT_String_info extends cp_info {
		public final int string_index;
		CONSTANT_String_info(int i) { super(CONSTANT_String); string_index = i; }
		CONSTANT_String_info(MyClass mc, String s) {
			super(CONSTANT_String);
			string_index = mc.addCP(new CONSTANT_Utf8_info(s));
		}
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeShort(string_index);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_String_info)) return false;
			CONSTANT_String_info v = (CONSTANT_String_info)obj;
			return string_index == v.string_index;
		}
		public String toString() {
			return "String " + string_index;
		}
	}
	static class CONSTANT_Integer_info extends cp_info {
		public final int bytes;
		CONSTANT_Integer_info(int v) { super(CONSTANT_Integer); bytes = v; }
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeInt(bytes);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_Integer_info)) return false;
			CONSTANT_Integer_info v = (CONSTANT_Integer_info)obj;
			return bytes == v.bytes;
		}
	}
	static class CONSTANT_Float_info extends cp_info {
		public final float bytes;
		CONSTANT_Float_info(float v) { super(CONSTANT_Float); bytes = v; }
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeFloat(bytes);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_Float_info)) return false;
			CONSTANT_Float_info v = (CONSTANT_Float_info)obj;
			return bytes == v.bytes;
		}
	}
	static class CONSTANT_Long_info extends cp_info {
		public final long bytes;
		CONSTANT_Long_info(long v) { super(CONSTANT_Long); bytes = v; }
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeLong(bytes);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_Long_info)) return false;
			CONSTANT_Long_info v = (CONSTANT_Long_info)obj;
			return bytes == v.bytes;
		}
	}
	static class CONSTANT_Double_info extends cp_info {
		public final double bytes;
		CONSTANT_Double_info(double d) { super(CONSTANT_Double); bytes = d; }
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeDouble(bytes);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_Double_info)) return false;
			CONSTANT_Double_info v = (CONSTANT_Double_info)obj;
			return bytes == v.bytes;
		}
	}
	static class CONSTANT_NameAndType_info extends cp_info {
		public int name_index = 0;
		public int descriptor_index = 0;
		CONSTANT_NameAndType_info(int n, int d) {
			super(CONSTANT_NameAndType); name_index = n; descriptor_index=d; }
		CONSTANT_NameAndType_info(MyClass mc, String n, String d) {
			super(CONSTANT_NameAndType);
			name_index = mc.addCP(new CONSTANT_Utf8_info(n));
			descriptor_index = mc.addCP(new CONSTANT_Utf8_info(d));
		}
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeShort(name_index);
			out.writeShort(descriptor_index);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_NameAndType_info)) return false;
			CONSTANT_NameAndType_info v = (CONSTANT_NameAndType_info)obj;
			return (name_index == v.name_index) && 
				(descriptor_index == v.descriptor_index);
		}
		public String toString() {
			return "NameAndType n:" + name_index + " d:" + descriptor_index;
		}
	}
	static class CONSTANT_Utf8_info extends cp_info {
		public final String bytes;
		CONSTANT_Utf8_info(String str) { super(CONSTANT_Utf8); bytes = str; }
		public void write(DataOutputStream out) throws IOException {
			out.writeByte(tag);
			out.writeShort(bytes.length());
			byte[] b = bytes.getBytes();
			out.write(b, 0, b.length);
		}
		public boolean equals(Object obj) {
			if (obj == null) return false; if (this == obj) return true;
			if (!(obj instanceof CONSTANT_Utf8_info)) return false;
			CONSTANT_Utf8_info v = (CONSTANT_Utf8_info)obj;
			if (bytes == null) return v.bytes == null;
			return bytes.equals(v.bytes);
		}
		public String toString() {
			return "\"" + bytes + "\"";
		}
	}
	
	static abstract class attribute_info {
		public final int attribute_name_index;
		protected attribute_info(int i) { attribute_name_index = i; }
		public abstract void write(DataOutputStream out) throws IOException;
	}
	
	static class exception_info {
		public int start_pc = 0;
		public int end_pc = 0;
		public int handler_pc = 0;
		public int catch_type = 0;
		public void write(DataOutputStream out) throws IOException {
			out.writeShort(start_pc);
			out.writeShort(end_pc);
			out.writeShort(handler_pc);
			out.writeShort(catch_type);
		}
		
	}
	
	static class SourceFile_attribute extends attribute_info {
		public final int sourcefile_index;
		SourceFile_attribute(MyClass mc, String s) {
			super(mc.addCP(new CONSTANT_Utf8_info("SourceFile")));
			sourcefile_index = mc.addCP(
				new CONSTANT_Utf8_info(s));
		}
		public void write(DataOutputStream out) throws IOException {
			out.writeShort(attribute_name_index);
			out.writeInt(2);
			out.writeShort(sourcefile_index);
		}
	}
	
	
	static class Code_attribute extends attribute_info {
		public int max_stack = 1;
		public int max_locals = 1;
		public ByteArrayOutputStream code = null;;
		public DataOutputStream data = null;
		public ArrayList<exception_info> exception_table = null;
		public ArrayList<attribute_info> attributes = null;
		Code_attribute(MyClass mc) {
			super(mc.addCP(new CONSTANT_Utf8_info("Code")));
			code = new ByteArrayOutputStream();
			data = new DataOutputStream(code);
		}
		public void addCode(int c) throws IOException {
			data.write(c);
		}
		public void addCodeShort(int c) throws IOException {
			data.writeShort(c);
		}
		public void write(DataOutputStream out) throws IOException {
			out.writeShort(attribute_name_index);
			ByteArrayOutputStream buf  = null;
			DataOutputStream tmp = null;
			try {
				buf = new ByteArrayOutputStream();
				tmp = new DataOutputStream(buf);
				tmp.writeShort(max_stack);
				tmp.writeShort(max_locals);
				tmp.writeInt(code.size());
				tmp.write(code.toByteArray(), 0, code.size());
				if (exception_table == null) {
					tmp.writeShort(0);
				} else {
					tmp.writeShort(exception_table.size());
					for (Iterator<exception_info>
							i = exception_table.iterator()
							; i.hasNext(); ) {
						i.next().write(tmp);
					}
				}
				if (attributes == null) {
					tmp.writeShort(0);
				} else {
					tmp.writeShort(attributes.size());
					for (Iterator<attribute_info>
							i = attributes.iterator()
							; i.hasNext(); ) {
						i.next().write(tmp);
					}
				}
				byte[] b = buf.toByteArray();
				out.writeInt(b.length);
				out.write(b, 0, b.length);
			} finally {
				if (tmp != null) tmp.close();
				if (buf != null) buf.close();
			}
		}
	}
	
	static abstract class member_info {
		public int access_flags;
		public int name_index;
		public int descriptor_index;
		public ArrayList<attribute_info> attributes = null;
		protected member_info(int ac, int n, int d) {
			access_flags = ac;
			name_index = n;
			descriptor_index = d;
		}
		public void add(attribute_info a) {
			if (attributes == null) {
				attributes = new ArrayList<attribute_info>();
			}
			attributes.add(a);
		}
		public void write(DataOutputStream out) throws IOException {
			out.writeShort(access_flags);
			out.writeShort(name_index);
			out.writeShort(descriptor_index);
			if (attributes == null) {
				out.writeShort(0);
			} else {
				out.writeShort(attributes.size());
				for (Iterator<attribute_info> i = attributes.iterator()
						; i.hasNext(); ) {
					i.next().write(out);			
				}
			}
		}
	}
	
	static class field_info extends member_info {
		public static final int ACC_PUBLIC 		= 0x01;
		public static final int ACC_PRIVATE 	= 0x02;
		public static final int ACC_PROTECTED 	= 0x04;
		public static final int ACC_STATIC 		= 0x08;
		public static final int ACC_FINAL 		= 0x10;
		field_info(int ac, int n, int d) { super(ac, n, d); }
	}
	
	static class method_info extends member_info {
		public static final int ACC_PUBLIC 		= 0x01;
		public static final int ACC_PRIVATE 	= 0x02;
		public static final int ACC_PROTECTED 	= 0x04;
		public static final int ACC_STATIC 		= 0x08;
		public static final int ACC_FINAL 		= 0x10;
		method_info(int ac, int n, int d) { super(ac,n,d); }
	}
}