Running Java VM with WebAssembly: Supplement

In the previous article Running JavaVM with WebAssembly, I tried to make a Java interpreter (JavaVM) using WebAssembly, but for later, a little ingenuity. Or, keep a memorandum for reference when expanding. By the way, the WebAssembly compiler assumes "Emscripten".

I have uploaded a set of C language source files of Java interpreter (Java VM) to the following GitHub.  https://github.com/poruruba/javaemu

Cooperation between Javascript and C language

We are using the following linkages.

Calling C function from Javascript

First is the function definition on the C language side that is called. Add EMSCRIPTEN_KEEPALIVE to the function definition. Only this.

main.c


#include <emscripten/emscripten.h>

#ifdef __cplusplus
extern "C" {
#endif

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

#ifdef __cplusplus
}
#endif

(However, since the expressiveness of the return value from C language to Javascript is poor, parameters are exchanged using the shared memory that will be explained later.)

The call from the Javascript side looks like the following. The utility "Module" defined in javaemu.js generated at compile time by emcc is useful.

main.js


var ret = Module.ccall('setRomImage', "number", ["number", "number"], [g_pointer, bin.length]);

The second argument indicates the return type of the C language function. The third argument shows the type of the list of arguments for the C language function. The fourth argument is the value you actually pass to the C language function argument.

The function called ccall is one of the functions specified at compile time.

s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'UTF8ArrayToString', 'stringToUTF8Array']"

Memory sharing between Javascript and C

I am using it as follows.

(Reference information)  https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#access-memory-from-javascript

//For example, var bin= { array: [1, 2, 3, 4] };
g_pointer = Module._malloc(bin.array.length);
Module.HEAPU8.set(bin.array, g_pointer);
var ret = Module.ccall('setRomImage', "number", ["number", "number"], [g_pointer, bin.array.length]);

Allocate the memory you want to share by calling Module._malloc. This is fine for an array of unsigned chars, but for shorts and longs you need to specify the area size accordingly. By calling Module.HEAPU8.set, the value of the array that Javascript wants to set is set in the memory. Since it is an array of unsigned chars, a macro called HEAPU8 is used when accessing memory.

You can also set it as follows.

Module.HEAPU8[g_pointer + ptr] = 123;

On the C language side, in the setRomImage function, remember the address of the shared memory, and then refer to or set it.

main.c


int EMSCRIPTEN_KEEPALIVE setRomImage(const unsigned char *romImage, long romSize )
{
	FUNC_CALL();
	
	classRom_ext = (unsigned char*)romImage;
	classRomSize_ext = romSize;

	FUNC_RETURN();
	
	return FT_ERR_OK;
}

The function setRomImage is used for sharing the class file in the Jar file, and the function setInoutBuffer is used for exchanging the arguments when calling the function callStaticMain and the return value from the C language side.

fopen pseudo behavior

C language fopen is also available, but the browser operates in Sandbox, so you cannot access local files on your PC. Instead, you can take the compile-time folder structure and files as a snapshot as an image file (javaemu.data) and read it with fopen at run time.

(Reference information)  https://emscripten.org/docs/porting/files/index.html

The following is an excerpt of that part.

alloc_class.c


#define PRECLASS_DIR	"pre_classes"

char *baseClassDir = PRECLASS_DIR;

static long file_read(const char *p_fname, unsigned char **pp_bin, long *p_size)
{
	FILE *fp;
	long fsize;
	char path[255];

	strcpy(path, baseClassDir);
	strcat(path, "/");
	strcat(path, p_fname);
	strcat(path, ".class");

	fp = fopen(path, "rb");
	if (fp == NULL)
		return FT_ERR_NOTFOUND;
//・ ・ ・

pre_classes is that that was specified at compile time in emcc.

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

Java interpreter supplement

It is an implementation specification of the implemented Java interpreter, not WebAssembly.

Java function call

Use the function callStaticMain.

main.c


int EMSCRIPTEN_KEEPALIVE callStaticMain(char *className, char *param )

In this, the Java VM, which is a Java interpreter, is initialized, and the Java class functions are called in the following parts.

main.c


	ret = startStaticMain(className, param, &retType, &retVar);

In the argument, set the class name to call and the argument you want to pass. The function name and type on the Java side called by this are fixed.

  public static void main( String[] args ){

Therefore, it is necessary to implement the above function name in the Java source of the target class. Implement the logic you want to process in this Java function. Argument exchange is

String[] input = System.getInput(-1);

Then, get the argument from Javascript side and

System.setOutput(new String[]{ "World", "Good evening" });

Returns the return value to the Javascript side.

Native extension of Java interpreter

You can do some things with Java implementation, but I still want to implement native processing using C language. To do this, define the following function in your Java source. The function name can be anything. In short, add the qualifier native static.

public native static final String[] getInput(int max);

Then, if you haven't implemented it natively yet, the interpreter will exit with an error similar to the following in the browser's developer console.

** Native Method Missing:
javaemu.js:2178 // base/framework/System_getInput_(I)[Ljava/lang/String;
javaemu.js:2178 { 320190814UL, func },

It means that there is no definition of a native function. Implement the native function in C language and set the pointer of the implemented C language function in the following part of waba_native.c.

waba_native.c


NativeMethod nativeMethods[] = {

//・ ・ ・ ・ ・ ・ ・
	// base/framework/System_printStackTrace_()V
	{ 320187986UL, FCSystem_printStackTrace },
	// base/framework/System_getInput_(I)[Ljava/lang/String; //★ Addition
	{ 320190814UL, FCSystem_getInput },  //★ Addition
	// base/framework/System_sleep_(I)I
	{ 320192265UL, FCSystem_sleep },

//・ ・ ・ ・ ・ ・ ・

};

NativeMethod nativeMethods [] has C language native function implementations. They are arranged in ascending order of hash values, so insert them in the appropriate places. (Otherwise the interpreter won't find it)

The implementation of a native function in C language looks like the following, for example.

waba_native.c


// base/framework/System_getInput_(I)[Ljava/lang/String;
long FCSystem_getInput(Var stack[]) {
	Var v;
	unsigned char num;
	unsigned char i;
	WObject strArray;
	WObject *obj;
	unsigned long ptr;
	long max;

	if (inoutBuff_ext == NULL)
		return ERR_CondNotSatisfied;

	max = stack[0].intValue;
	num = inoutBuff_ext[0];
	if (max >= 0 && num > max)
		num = (unsigned char)max;
	strArray = createArrayObject(TYPE_OBJECT, num);
	obj = (WObject *)WOBJ_arrayStart(strArray);

	if (pushObject(strArray) != FT_ERR_OK)
		return ERR_OutOfObjectMem;

	ptr = 1;
	for (i = 0; i < num; i++) {
		obj[i] = createString((const char*)&inoutBuff_ext[ptr]);
		if (obj[i] == WOBJECT_NULL) {
			popObject();
			return ERR_OutOfObjectMem;
		}
		ptr += strlen((const char*)&inoutBuff_ext[ptr]) + 1;
	}

	popObject();

	v.obj = strArray;
	stack[0] = v;

	return 0;
}

Please refer to other native functions. The input arguments are on the Var stack []. Set the response to stack [0]. (Because it is ←, it is not possible to create a native function without arguments) If there is no problem, return 0; is returned.

Java Interpreter (Java VM) Limitations

JavaVM has limited functionality. For example, bytecode using float, double and long is not supported. As you can see by looking at the function below, all the above bytecodes are commented out.

waba.c


long executeMethod(WClass *wclass, WClassMethod *method, Var params[], unsigned short numParams, unsigned char *retType, Var* retValue) {
//・ ・ ・
		// NOTE: this is the full list of unsupported opcodes. Adding all
		// these cases here does not cause the VM executable code to be any
		// larger, it just makes sure that the compiler uses a jump table
		// with no spaces in it to make sure performance is as good as we
		// can get (tested under Codewarrior for PalmOS).
/*
		case OP_lconst_0:
		case OP_lconst_1:
		case OP_dconst_0:
//・ ・ ・

that's all

Recommended Posts

Running Java VM with WebAssembly: Supplement
Run Java VM with WebAssembly
Profiling with Java Visual VM ~ Basic usage ~
Install java with Homebrew
Change seats with java
Install Java with Ansible
Compile with Java 6 and test with Java 11 while running Maven on Java 8
Comfortable download with JAVA
Switch java with direnv
Let's scrape with Java! !!
Build Java with Wercker
Endian conversion with JAVA
Java multi-project creation with Gradle
Getting Started with Java Collection
Basic Authentication with Java 11 HttpClient
Let's experiment with Java inlining
Run batch with docker-compose with Java batch
[Template] MySQL connection with Java
Rewrite Java try-catch with Optional
Install Java 7 with Homebrew (cask)
[Java] JSON communication with jackson
Java to play with Function
Try DB connection with Java
Enable Java EE with NetBeans 9
[Java] JavaConfig with Static InnerClass
Try gRPC with Java, Maven
Let's operate Excel with Java! !!
Version control Java with SDKMAN
RSA encryption / decryption with java 8
Paging PDF with Java + PDFBox.jar
Sort strings functionally with java
Object-oriented (java) with Strike Gundam
[Java] Content acquisition with HttpCliient
Troubleshooting with Java Flight Recorder
Streamline Java testing with Spock
Connect to DB with Java
Error when playing with java
Using Mapper with Java (Spring)
Java study memo 2 with Progate
Getting Started with Java Basics
Seasonal display with Java switch
Use SpatiaLite with Java / JDBC
Study Java with Progate Note 1
Compare Java 8 Optional with Swift
HTML parsing with JAVA (scraping)
Screen transition with swing, java
Java unit tests with Mockito