Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

report roc_panic to the user in the web repl #5921

Merged
merged 3 commits into from
Oct 25, 2023
Merged

Conversation

folkertdev
Copy link
Contributor

I don't know JS very well so this works but might be unidiomatic?

it now looks lilke this

Screenshot from 2023-10-20 14-29-14

Comment on lines 176 to 177
const strPtr = rocStrBytes[0] | (rocStrBytes[1] << 8) | (rocStrBytes[2] << 16) | (rocStrBytes[3] << 24);
const strLen = rocStrBytes[4] | (rocStrBytes[5] << 8) | (rocStrBytes[6] << 16) | (rocStrBytes[7] << 24);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

based on some searches there is no better way to achieve this?!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do something like this (please test for bugs!)

    const rocStrWords = new Uint32Array(memory.buffer, rocstr_ptr, 3);
    const [ptr, len, cap] = rocStrWords;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main idea being to use Uint32Array rather than Uint8Array
These classes are "views" onto an ArrayBuffer, but interpret the buffer as either u32 or u8 respectively.
The ArrayBuffer itself can't really do much.

Docs here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array/Uint32Array

The second argument to the constructor is called byteOffset in the docs. Third argument is length, which I think is the length of the final Uint32Array but test it!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also memoryBuffer appears to be unused?
You might have un-pushed changes because I see you marked one of my comments as resolved but I don't see the changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

huh, I tried the "transmute" from bytes before and then it did not work. maybe I used the wrong length. Anyway that does in fact work now

www/public/repl/repl.js Outdated Show resolved Hide resolved
www/public/repl/repl.js Outdated Show resolved Hide resolved
Comment on lines 176 to 177
const strPtr = rocStrBytes[0] | (rocStrBytes[1] << 8) | (rocStrBytes[2] << 16) | (rocStrBytes[3] << 24);
const strLen = rocStrBytes[4] | (rocStrBytes[5] << 8) | (rocStrBytes[6] << 16) | (rocStrBytes[7] << 24);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do something like this (please test for bugs!)

    const rocStrWords = new Uint32Array(memory.buffer, rocstr_ptr, 3);
    const [ptr, len, cap] = rocStrWords;

Comment on lines 176 to 177
const strPtr = rocStrBytes[0] | (rocStrBytes[1] << 8) | (rocStrBytes[2] << 16) | (rocStrBytes[3] << 24);
const strLen = rocStrBytes[4] | (rocStrBytes[5] << 8) | (rocStrBytes[6] << 16) | (rocStrBytes[7] << 24);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main idea being to use Uint32Array rather than Uint8Array
These classes are "views" onto an ArrayBuffer, but interpret the buffer as either u32 or u8 respectively.
The ArrayBuffer itself can't really do much.

Docs here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint32Array/Uint32Array

The second argument to the constructor is called byteOffset in the docs. Third argument is length, which I think is the length of the final Uint32Array but test it!

www/public/repl/repl.js Outdated Show resolved Hide resolved
Comment on lines 176 to 177
const strPtr = rocStrBytes[0] | (rocStrBytes[1] << 8) | (rocStrBytes[2] << 16) | (rocStrBytes[3] << 24);
const strLen = rocStrBytes[4] | (rocStrBytes[5] << 8) | (rocStrBytes[6] << 16) | (rocStrBytes[7] << 24);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also memoryBuffer appears to be unused?
You might have un-pushed changes because I see you marked one of my comments as resolved but I don't see the changes.

Comment on lines 176 to 190
let stringBytes = "";
if (finalByte < 0) {
// small string
const length = finalByte ^ 0b1000_0000;
stringBytes = new Uint8Array(memory.buffer, rocstr_ptr, length);
} else {
// big string
const rocStrWords = new Uint32Array(memory.buffer, rocstr_ptr, 3);
const [ptr, len, _cap] = rocStrWords;

const SEAMLESS_SLICE_BIT = 1 << 31;
const length = len & (~SEAMLESS_SLICE_BIT);

stringBytes = new Uint8Array(memory.buffer, ptr, length);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in practice I think only big (non-slice) strings can end up here right now. But it's good to just get this logic right, there is a good chance it'll get copied over time.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good thinking!

let stringBytes = "";
if (finalByte < 0) {
// small string
const length = finalByte ^ 0b1000_0000;
Copy link
Contributor

@brian-carroll brian-carroll Oct 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a bug here!

I did the following test in my browser console.
Let's test this logic for a Roc string of length 5, where the last byte is 0x85.
Instead of building the whole string, let's put that number into an Int8Array and take it out again.

a = new Int8Array([0x85])
finalByte = a[0] // -123
length = finalByte ^ 0b1000_0000 // -251
correctLength = finalByte + 128 // 5

Once you take it out of the Int8Array as a single number it is no longer 8 bits. It's converted to a JavaScript number. Bitwise ops in JS are effectively signed 32 bit. Bitwise ops on negative JS numbers are annoying, but addition just does what you'd expect. So finalByte + 128 will work correctly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

love JS numbers... fixed now

const [ptr, len, _cap] = rocStrWords;

const SEAMLESS_SLICE_BIT = 1 << 31;
const length = len & (~SEAMLESS_SLICE_BIT);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This bitwise op should work fine. It can't be a negative number because we read it out of an unsigned array. 👍

Comment on lines 176 to 190
let stringBytes = "";
if (finalByte < 0) {
// small string
const length = finalByte ^ 0b1000_0000;
stringBytes = new Uint8Array(memory.buffer, rocstr_ptr, length);
} else {
// big string
const rocStrWords = new Uint32Array(memory.buffer, rocstr_ptr, 3);
const [ptr, len, _cap] = rocStrWords;

const SEAMLESS_SLICE_BIT = 1 << 31;
const length = len & (~SEAMLESS_SLICE_BIT);

stringBytes = new Uint8Array(memory.buffer, ptr, length);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good thinking!

Copy link
Contributor

@brian-carroll brian-carroll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this!

@brian-carroll brian-carroll merged commit c509252 into main Oct 25, 2023
14 checks passed
@brian-carroll brian-carroll deleted the wasm-repl-crash branch October 25, 2023 18:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants