So, now that we've had a quick look at the history, let's look at what these class files actually look like. So, if we go back to the simple compilation example, what does this hello.class file contain? A binary mess. We can see the names of some other class names, but, yeah, that's not very useful. So, class files themselves are packaged into jar files, and each class file looks something a bit like this, where you've got this constant pool, you've got some metadata, you've got some fields, and you've got the actual code itself.
The methods each have a sort of method descriptor which describes the types of the inputs and the outputs. So, we've got this sort of the types of parameters in the brackets and then the return type afterwards. And the primitive Java types map to these little letters, so, like, byte is B, char is C, long is J, of course, and so on.
So, now that we can parse these class files, let's talk about compiling and calling functions with them. So, we've got the method descriptors, we've got the byte code. So, we can start transforming it to WebAssembly. To do this, we can just pattern match on the descriptors that we've parsed earlier and convert them to WebAssembly types, and similarly for instructions, we can just take the JVM instructions and convert them to WebAssembly. This is going to be really easy. And that's kind of all we need for basic compilation. When you've got this, to run it, you can just wrap it with a WebAssembly module, and then you can export it, and then we've got this text format which we can't actually run, so we convert it to the binary format, and we get this Node.js code, which is super nice, because we just have to read the file, we can compile that into a WebAssembly module, instantiate it, and call its exports.
So, we can also call the function from within WebAssembly, which is nice, but hang on a minute, we've got a first problem. Java lets you do method overloads, where you can have the same method, the same name, but just, like, different parameters instead. And we can kind of implement this in WebAssembly. We can just stick the method descriptors on the function names and treat them as separate functions. That works, that's fine. But there's a little subtlety here. In, even though we're adding two of the same things, in the integer example, the second integer is loading from index one, and in the double example, the second double is loading from index two. This is kind of a quirk of how Java's storing these locals. So, Java treats locals as four byte word slots, and ints are one word each, doubles are two words, and the problem is that WebAssembly expects single typed values for locals. So, when you create a local in WebAssembly, you have to define its type ahead of time, and it can only ever be that type. This gets even more complicated, because if we take this, like, example here, the local slots get reused if the variables go out of scope. So, a single word is referenced as an int, and the first half of a double in the same function. This is definitely not allowed in WebAssembly.
Comments