plasma-umass / doppio

Breaks the browser language barrier (includes a plugin-free JVM).
http://plasma-umass.github.io/doppio-demo
MIT License
2.16k stars 174 forks source link

Uint8Array not converted to byte[] #457

Closed ianopolous closed 8 years ago

ianopolous commented 8 years ago

I'm returning what I believe is a Uint8Array from a "native" JS method, but it is throwing this exception in AsyncReturn().

assert.ts:8 Uncaught Error: Assertion failed: Invalid array object: 216,129,120,126,226,157,61,141,153,225,159,227,200,40,203,202,249,120,249,143,248,65,209,240,79,45,29,238,15,59,146,112,194,49,135,45,14,166,46,84,12,195,4,170,223,204,115,60,246,226,50,36,148,28,15,128,141,7,169,143,59,116,203,90,218,149,20,27,181,163,27,74,14,223,128,57,64,149,244,252,217,77,73,182,225,165,78,30,144,113,32,215,124,81,210,170

Code is at: https://github.com/ianopolous/Peergos/blob/doppio/ui/doppio/natives/peergos_crypto_hash_ScryptJS.js

jvilk commented 8 years ago

There are two issues with what you are trying to do.

First: DoppioJVM does not directly map JVM arrays to JS arrays, since JVM arrays are also JVM objects -- so they have a class, methods, etc. DoppioJVM wraps JS arrays and typed arrays in a JVM object with an array property, which you need to construct like other JVM objects.

Second: a byte[] array is an Int8Array -- they are signed integers. You need to convert the Uint8Array into an Int8Array first.

Once you have the Int8Array, you can do the following to construct a byte array with no copying:

Doppio.VM.Util.newArrayFromDataWithClass(thread, thread.getBsCl().getInitializedClass(thread, '[B'), i8Array);
jvilk commented 8 years ago

Also, if you have assertion checking enabled, you are using a development build of DoppioJVM (which is super useful for debugging). Hopefully that is intentional. :)

If it is not, you should use a much faster release build of DoppioJVM that strips assertions from the JVM once you have something shippable.

ianopolous commented 8 years ago

Excellent, thank you! That fixed it. For the reference of others I had to do:

    var hashedBytes = some UInt8Array....;
    var i8Array = new Int8Array(hashedBytes);
    var javaByteArray = Doppio.VM.Util.newArrayFromDataWithClass(thread, thread.getBsCl().getInitializedClass(thread, '[B'), i8Array);
    thread.asyncReturn(javaByteArray);
jvilk commented 8 years ago

Note that your solution is actually copying the Uint8Array.

From MDN:

When called with a typedArray argument, which can be an object of any of the typed array types (such as Int32Array), the typedArray gets copied into a new typed array. Each value in typedArray is converted to the corresponding type of the constructor before being copied into the new array.

To do it zero-copy, you need to pass in the raw ArrayBuffer, byteOffset, and byteLength:

When called with a buffer, and optionally a byteOffset and a length argument, a new typed array view is created that views the specified ArrayBuffer. The byteOffset and length parameters specify the memory range that will be exposed by the typed array view. If both are omitted, all of buffer is viewed; if only length is omitted, the remainder of buffer is viewed.

Example:

var i8Array = new Int8Array(hashedBytes.buffer, hashedBytes.byteOffset, hashedBytes.byteLength);
ianopolous commented 8 years ago

Thanks, John. In this case it's only 96 bytes, but I'll need that later on for 5MiB arrays after erasure decoding.