WebAssemblyでJavaVMを動かす

最近環境が整いつつあるWebAssemblyを触ってみます。
題材としてJavaを実行できる環境を作ってみたいと思います。そうです、Javaアプレットのような、ブラウザで動くJavaインタプリタ(JavaVM)です。

> 最初に、お断りしておきます。
> Javaのバイトコードを解釈するJavaインタプリタではありますが、最低限のバイトコードしか実装していません。また、java/lang配下のほんの一部のクラスしか実装していないので、実用的には使い物にならないです。個人的に、Javaインタプリタの勉強のために作ってみたものです。
> 期待してこられた方にはすみません。

以下の通り進めていきます。

* WebAssemblyコードを生成するコンパイラをインストールする。
* プロジェクトフォルダを作成する
* WebAssemblyに埋め込むJavaソースコードをコンパイルする。
* JavaインタプリタのC言語ソースコードをコンパイルする。
* Webに配置してWebAssemblyに埋め込んだJavaバイトコードの動作確認する。
* WebAssemblyに埋め込まなかったJavaソースコードをコンパイルする。
* WebAssemblyに埋め込まなかったJavaバイトコードの動作確認する。

JavaインタプリタのC言語ソースコードとして、「waba」を参考にさせていただきました。非常に勉強になりました。ありがとうございました。

wabasoft
　http://wabasoft.com/

WebAssemblyコンパイラには、「emscripten」を利用しました。
　https://emscripten.org/index.html

以下、続編です。
　[WebAssemblyでJavaVMを動かす：補足](https://qiita.com/poruruba/items/bbabdd55f02e3a884605)

# emscriptenのインストール

Windows10のWSL環境にインストールしてみました。
ですので、Python2.7を使うのですが、インストール済みでした。

```console
> python --version
Python 2.7.15rc1
```

```console
> git clone https://github.com/juj/emsdk.git
> cd emsdk
> ./emsdk install latest
> ./emsdk activate latest
```

これで、WebAssemblyコンパイラである「emcc」がインストールされました。
使うときには、パスを通す必要があるため、以下を実行します。

```console
> source ./emsdk_env.sh --build=Release
```

#プロジェクトフォルダの作成

適当なフォルダを作って作業します。

C言語のソースファイル一式を同フォルダに配置します。

以下のGitHubに上げておきました。

　https://github.com/poruruba/javaemu

```console
> git clone https://github.com/poruruba/javaemu.git
> cd javaemu
```

#WebAssemblyに埋め込むJavaソースコードをコンパイルする

Javaソースコードをコンパイルします。
Javaソースコードは2種類に分けて、配置の方法を変えます。
一つ目は、WebAssemblyに埋め込むJavaソースで、もう一つは、WebAssemblyに埋め込まないJavaソースです。
後者は、通常のユーザ作成のJavaソースコードです。一方、java/langなどは通常のユーザは利用するだけで、変更することのないシステムのJavaソースが前者です。また、インタプリタとの連携やC言語でしか処理できないネイティブ関数があるJavaクラスも前者になります。

ソースコードは、java_srcのフォルダ配下に作っていくことを前提としています。
コンパイルしたクラスファイルを置くフォルダを作ります。pre_classesとします。

```console
> mkdir pre_classes
```

コンパイルは以下の通りです。

```console
>javac -encoding UTF8 -d pre_classes -sourcepath java_src java_src/base/framework/*.java java_src/java/lang/*.java java_src/test/*.java
```

base/frameworkとjava/lang配下のJavaソースファイルをシステムのJavaソースとしています。java_src/test/*.java は、後でテスト用に使うものですので、テストが終わったら抜いてください。
これで、pre_classesフォルダ配下に、Javaクラスファイルができあがりました。

Windows上でOracle JDKを使いました。OpenJDKの場合、実行環境上のクラスファイルとバッティングしてしまって、排他する方法がわかりませんでした。。。

#C言語ソースコードをコンパイルする

さあ、コンパイルしましょう

```console
> emcc *.c -s WASM=1 -o javaemu.js -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'UTF8ArrayToString', 'stringToUTF8Array']" --preload-file pre_classes
```

(参考)コンパイル時オプション
　https://emscripten.org/docs/tools_reference/emcc.html#emccdoc

-s WASM=1 はWASMファイルを生成することを示すおまじないです。

-o javaemu.js は、WASMファイル(javaemu.wasm)とユーティリティのJavascript(javaemu.js)とWebAssemblyに埋め込むJavaクラスファイルをまとめたファイル(javaemu.data)を生成するためのものです。
javaemuという名前は適当です。好きな名前にしてください。

-s EXTRA_EXPORTED_RUNTIME_METHODS は、javaemu.jsが提供する関数のうち、Webに配置するHTMLページのJavascriptから呼び出すことを許可する関数名です。ccallは必須ですが、それ以外の2つは有用なので設定しました。

--preload-file は、WebAssemblyに埋め込むファイルが置かれたフォルダ名を指定します。指定したフォルダ名配下のファイルが、javaemu.dataというファイルにまとめられます。

出来上がったファイルのうちWebの配置するのは、「javaemu.wasm」と「javaemu.js」と「javaemu.data」です。
javaemu.wasmが、まさしくC言語をコンパイルしたWebAssemblyファイルです。

WebページのJavascriptから呼び出される関数は、main.cに定義しています。

```main.c
int EMSCRIPTEN_KEEPALIVE setInoutBuffer(const unsigned char *buffer, long bufferSize );
int EMSCRIPTEN_KEEPALIVE setRomImage(unsigned char *romImage, long romSize );
int EMSCRIPTEN_KEEPALIVE callStaticMain(char *className, char *param );
```

# Webに配置してWebAssemblyに埋め込んだJavaバイトコードの動作確認する。

GitHubから落としてきたファイルのうちのhtmlをWebサーバにアップします。
動作確認のためのページも用意しておきました。
さきほど生成した3つのファイルも、htmlフォルダに配置します。

```console
> cp javaemu.wasm javaemu.js javaemu.data html/

または

> cd html
> ln -s ../javaemu.wasm ../javaemu.js ../javaemu.data .
> cd ..
```

（HTMLのルートフォルダにおいてください。そうしないと、javaemu.jsがうまくwasmやdataファイルを読んでくれないです）

ありがたいことに、emscriptenには簡易Webサーバ機能があるので、それを使います。ポート番号は8080にしました。

> emrun --serve_root html --no_browser --port 8080 .

それでは、ブラウザからアクセスしてみます。
ブラウザとして、Windows上のChromeで動かしてみました。

　http://localhost:8080

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/b044b787-f50a-2ad1-f6a3-63df2ece979d.png)


わかりやすいように、F12を押して開発者コンソールを表示しています。

Class Nameのこところに、テスト用に埋め込んだJavaクラス名を入力します。「test/TestFunc」です。

もととなったソースコードは以下の感じです。

```TestFunc.java
package test;

import base.framework.System;
import base.framework.Convert;
import base.framework.Util;

public class TestFunc{
  public static void main( String[] args ){
  	try
  	{
  		System.println("Hello test/TestFunc");

  		if( args.length >= 1 )
  			System.println("args[0]=" + args[0]);
  		
  		String[] input = System.getInput(-1);
  		for( int i = 0 ; i < input.length ; i++ )
	  		System.println("params[" + i + "]=" + input[i]);

  		System.setOutput(new String[]{ "World", "こんばんは" });
  	}catch( Exception ex )
  	{
		System.print( ex.toString() );
  		ex.printStackTrace();
  	}
  }
}
```

Input ArgやInput Paramsにも適当な文字列をいれます。
さっそく「start wasm」ボタンを押下します。

以下の感じで、C言語で実装した関数を呼び出します。
WebAssemblyファイルをロードして実行するには、手間がかかるのですが、そこはjavaemu.jsがうまくやってくれています。

```start.js
            this.output_return = Module.ccall('callStaticMain', // name of C function 
                                    "number", // return type
                                    ["string", "string"], // argument types
                                    [this.class_name, this.input_arg]); // arguments
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/075c546d-d4f1-5d87-b029-83c706732104.png)

すると、ブラウザのConsoleにごちゃごちゃデバッグ文字が表示された中に、以下のような文字列が表示されたかと思います。JavaインタプリタによるJavaクラスファイルの実行完了です。

> Hello test/TestFunc
> args[0]=ABCD
> params[]=あいうえお
> params[1]=かきくけこ

また、ページにも以下が表示されたかと思います。

> Output Return 0 
> Output Params [ "World", "こんばんは" ]

# WebAssemblyに埋め込まなかったJavaソースコードをコンパイルする。

今度は、いろいろなJavaソースを作成して動かしてみましょう。

java_src 配下に作っていきます。
今回は、test2フォルダ以下に作っていきます。

コンパイルは以下の通りです。

```console
> mkdir classes
> javac -encoding UTF8 -d classes -sourcepath java_src -classpath pre_classes java_src/test2/*.java
```

クラスファイル格納用に、フォルダclassesを作っています。
pre_classesはWebAssemblyファイルに埋め込まれている前提なので、すでにあるクラスファイルとみなしてコンパイルします。

次に、作ったクラスファイルをJarファイルにします。

```console
> jar cvf classes.jar -C classes .
```

これで、classesフォルダ配下のファイルが、classes.jarファイルにまとめられました。test2のクラスファイル以外のファイルも作られてましたが、実害はないので無視してもいいですし、Jarファイル対象から除いてもよいです。

「ファイルを選択」ボタンを押下し、先ほど作成した「classes.jar」を指定します。
そうするとその下に、Jarファイルに含まれるクラスファイル一覧が表示されます。

Jarファイルの展開、すなわち、ZIPファイルの展開には、zlib.jsのunzip.min.jsを使わせていただきました。

zlib.js
　https://github.com/imaya/zlib.js

それではもう一度、ブラウザから実行します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261826/ca62ece6-db64-7188-1844-22809b571c99.png)

Class Nameに、今度はJarファイルに含まれるtest2/TestFuncを指定して、「start wasm」ボタンを押下して実行します。

コンソールに以下のように表示されましたでしょうか。test2/TestFuncとなって、該当ソースが実行されているのがわかります。

> Hello test2/TestFunc
> args[0]=ABCD
> []=あいうえお
> [1]=かきくけこ

# 補足

Javaインタプリタは、wabaからかなりカスタマイズしていまして、バグだらけですので、それだけお断りしておきます。。。

解説は次回の記事で。まずはここまで。

以下、続編です。
　[WebAssemblyでJavaVMを動かす：補足](https://qiita.com/poruruba/items/bbabdd55f02e3a884605)

以上。