読者です 読者をやめる 読者になる 読者になる

変態的HelloWorld:「お巡りさんこっちです」と標準出力に出力するだけのアプリ

変態アドベントカレンダーの4日目です。
http://atnd.org/events/22020

遅くなりました。ごめんなさい。
せっかく変態アドベントカレンダーなのに体調が悪くて手抜きでごめんなさい。
もうちょっとネタを探したかったんだけど。。。


今日はずっと体調が悪くて、変態なネタも思いつかずに
変態アドベント4日目にして終了かと思ったそのときに、
@daiksyさんが、、、


「@tan_go238 お巡りさんこっちです、とだけ書いていただければw」


と言ってくれたので


「お巡りさんこっちです」と標準出力に出力するだけのアプリを作った。


普通に書いても面白くないので、
1.「お巡りさんこっちです」というだけのメソッドを持ったクラスを用意
2.コンパイル
3.コンパイル後のバイトコードを16進数に変換
4.それを.txtで書き出し
5.16進数で書かれたテキストファイルを読み込んで、クラスとしてロード
6.リフレクションでメソッドを実行
7.「お巡りさんこっちです」と出力される
という感じにしてみた。



まず1
hentai/Main.java

package hentai;

public class Main {
	public void call110() {
		System.out.println("「お巡りさんこっちです」");
	}
}


4でできたテキストファイルはこんな感じ
tsuuhou.txt

CAFEBABE00000032001F07000201000B68656E7461692F4D61696E0700040100106A6176612F6C616E672F4F626A6563740100063C696E69743E010003282956010004436F64650A000300090C0005000601000F4C696E654E756D6265725461626C650100124C6F63616C5661726961626C655461626C650100047468697301000D4C68656E7461692F4D61696E3B01000763616C6C31313009001000120700110100106A6176612F6C616E672F53797374656D0C001300140100036F75740100154C6A6176612F696F2F5072696E7453747265616D3B080016010024E3808CE3818AE5B7A1E3828AE38195E38293E38193E381A3E381A1E381A7E38199E3808D0A0018001A0700190100136A6176612F696F2F5072696E7453747265616D0C001B001C0100077072696E746C6E010015284C6A6176612F6C616E672F537472696E673B295601000A536F7572636546696C650100094D61696E2E6A617661002100010003000000000002000100050006000100070000002F00010001000000052AB70008B100000002000A00000006000100000003000B0000000C000100000005000C000D00000001000E000600010007000000370002000100000009B2000F1215B60017B100000002000A0000000A00020000000500080006000B0000000C000100000009000C000D00000001001D00000002001E


昨日のやきに駆動の発表でも言いましたが、最初の4バイトの「CAFEBABE」がマジックナンバーだ。
デコンパイルJava」という本によると、「NeXTプラットフォームのマルチアーキテクチャバイナリファイルの最初の4バイトもこの値」らしい。ともかくこれがClassファイルを表す識別子ですよと。


このへんのClassファイルの読み方は昨日説明したので興味がある人は資料をみてもらったほうがいいかもしれない。
興味が無いひとはたまに表示される焼肉の写真を眺めるのがいいと思います。
http://www.slideshare.net/tanago3/inside-the-java-virtual-machine-10141075


んで、あとの5、6は独自のクラスローダ(HentaiClassLoader)を作って
それ経由でテキストファイルを読み込みクラスとしてロード、実行の流れ。
ソースは以下のようになった。

Main.java

import java.lang.reflect.Method;

public class Main {
	public static void main(String[] args) throws Exception {
		Main main = new Main();
		main.run();
	}

	public void run() throws Exception {
		HentaiClassLoader loader = new HentaiClassLoader();
		Class<?> clazz = loader.loadClass(Constant.PROJECT_DIR + "tsuuhou.txt");
		Object newInstance = clazz.newInstance();
		Method method = clazz.getDeclaredMethod("call110");
		method.invoke(newInstance);
	}
	
}

HentaiClassLoader.java

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class HentaiClassLoader extends ClassLoader {

	@Override
	public Class<?> loadClass(String className) throws ClassNotFoundException {
		if (className.startsWith("java")) {
			return Class.forName(className);
		}
		if (className.endsWith(".txt")) {
			Class<?> clazz = defineClass(className);
			if (clazz != null) {
				return clazz;
			}
		}
		return super.loadClass(className);
	}

	protected Class<?> defineClass(String path) {
		Class<?> clazz;
		byte[] bytes = getBytes(path);
		clazz = defineClass("hentai.Main", bytes, 0, bytes.length);
		resolveClass(clazz);
		return clazz;
	}

	protected byte[] getBytes(String path) {
		byte[] bytes = null;

		try {
			BufferedInputStream bis = new BufferedInputStream(
					new FileInputStream(new File(path)));
			byte[] buf = new byte[8192];
			StringBuilder sb = new StringBuilder();
                        while (bis.read(buf, 0, buf.length) != -1) {
                                sb.append(new String(buf));
                        }
			String source = sb.toString().trim().toLowerCase();
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			for (int i = 0; i < source.length(); i += 2) {
				int b = Integer.parseInt(source.substring(i, i + 2), 16);
				baos.write(b);
			}
			bytes = baos.toByteArray();
		} catch (IOException e) {
			e.printStackTrace();
		}

		return bytes;
	}
}

そんな感じでMain.javaを実行すればめでたく「お巡りさんこっちです」と標準出力に表示される。


なんか変態っぽくないけど、そんな感じだ。
このアプリのメリットはやっぱりクラスファイルを16進数にしたことで
Java仮想マシン仕様」を片手にクラスファイルを読み進めることができるところだと思う。


そんな訳で寝ます。。ごめんなさい。ごめんなさい。。



あ、ソースはGithubあげてます。ごめんなさい。
https://github.com/tango238/Hentai-Sample