konsoletyper / teavm

Compiles Java bytecode to JavaScript, WebAssembly and C
https://teavm.org
Apache License 2.0
2.55k stars 260 forks source link

Replacing Adler32 #925

Open xupwup opened 3 weeks ago

xupwup commented 3 weeks ago

Hi,

I'm running into some performance issues with my application, which mostly seem to be caused by operations on longs. One big culprit is Adler32, which I'd like to replace by my own version. So I've attempted to replace com.jcraft.jzlib.Adler32, but that crashes at runtime with error -3 (Z_DATA_ERROR). Even if the replacement is a literal copy-paste from the original.

So:

  1. Is there a way to use my own Adler32 implementation?
  2. Is there a way to get faster long operations? Mainly Long.hashCode

(I am using the javascript backend by the way)

Stacktrace: Caused by: Error: Error occurred: -3 JavaError http://localhost:8080/VikingWebVisualisationClient.js:981 $rt_exception http://localhost:8080/VikingWebVisualisationClient.js:947 $rt_throw http://localhost:8080/VikingWebVisualisationClient.js:938 juz_Inflater_inflate http://localhost:8080/VikingWebVisualisationClient.js:18927 juz_InflaterInputStream_read http://localhost:8080/VikingWebVisualisationClient.js:16369 rt_wrapFunction3 http://localhost:8080/VikingWebVisualisationClient.js:23 ji_DataInputStream_readToBuff http://localhost:8080/VikingWebVisualisationClient.js:17597 ji_DataInputStream_readInt****

konsoletyper commented 3 weeks ago

As for long operations, I don't know if there's a way to do anything with it. JavaScript does not support 64 bit integers, so longs are emulated with BigInt, which is more or less same to java.math.BigInteger. WebAssembly supports 64 bit integers, but the whole WebAssembly setup it quite unstable and difficult to use. If you know how to implement longs efficiently in JavaScript BE, you can post your suggestions.

As for replacing com.zcraft.jzlib.Adler32 class, I don't know why it does not work.

JS stacktraces are mostly useless, unless you provide me with the instructions how to build the same JS or deobfuscate it before posting.

xupwup commented 3 weeks ago

adler32demo.zip

I've created a small demo project that shows the problem. You'll notice that renaming TAdler32 to TAdler32a makes the compressed bytearray that's printed to stdout different.

Note: my implementation of Adler32 is an exact copy of the original

Ihromant commented 3 weeks ago

Hi. In my project I successfully got rid of long usage. My goal was to get rid of long emulation at all. I understand that you have different purpose, but this could be helpful https://github.com/konsoletyper/teavm/discussions/719#discussioncomment-7190006

xupwup commented 3 weeks ago

My goal isn't exactly to get rid of longs, I just want to get rid of longs in a few hotspots.

A slightly different question about wasm, are there any plans to support the JSBody annotation for wasm?

konsoletyper commented 3 weeks ago

@xupwup current WebAssembly implementation can't support @JSBody even in theory, because WebAssembly has no direct access to JavaScript objects. You need to use WebAssembly specific interop to call imported functions. And you can import any JavaScript code to WebAssembly module you like.

There's new GC spec in WebAssembly that makes it possible. However, it's so big game changer, that's it's not a question of just slightly improving existing Wasm backend. Instead, it should be 99% rewritten. Also, Wasm GC spec is still unsupported by Safari or any WASI runtimes.

xupwup commented 3 weeks ago

Can't you replace @JsBody by @Import, and create a function in a separate js file automatically?

konsoletyper commented 3 weeks ago

@xupwup no, because WebAssembly backend does not generate any JS file, it only produces wasm file. Also, @JSBody method can take any data, whereas WebAssembly imports only take numbers. Also, in WebAssembly Java objects are not really objects - they are just sequence of numbers in a large array.

xupwup commented 3 weeks ago

Well, the wasm backend currently does not generate any JS files. It could make a file consisting of only code from JSBody annotations which have been processed a little.

I think you can make things work with org.teavm.interop.Address and a bit of glue code in the generated javascript. The only types I'm interested in passing are primitives, byte[] and String anyway, so that should be fine. And I'm interested in passing callbacks, but those would probably take a non trivial amount of hacky code on the js and java side to handle (exposing a function like callCallback(int id, Object... args) or something).

But anyway, that would probably take a lot of work.

konsoletyper commented 3 weeks ago

@xupwup it's you interested, and in general support for @JSBody is impossible, so it would be a source of confusion for users. Passing callbacks is also impossible, even in theory, no glue code would help. And I don't have any motivation to do all this stuff. But luckily, TeaVM is extensible, so you can just write a plugin which does what you need (please, note that I'm also not motivated to help and perhaps won't answer any questions). Alternatively, you can use any sort of code/bytecode generation on your side.

xupwup commented 3 weeks ago

Ok, fair enough. Did you have a look at my example project already where replacing Adler32 doesn't work properly?

konsoletyper commented 3 weeks ago

@xupwup have no idea. I suggest that you don't use this property-based mechanism to replace one class from jzlib. At least, this mechanism was not designed to replace single classes, but whole package. The only potential issue on TeaVM side is why it completes normally instead of generating lots of compile errors.

There are plenty of ways you can handle this. For example

  1. avoid working with zip archives in your client code, HTTP already can handle compression for you
  2. use some kind of package relocation on Java bytecode level instead of relying on TeaVM
  3. write TeaVM plugin that would replace a single class

or what you invent.