Skip to content

Add BigInt64Array shim for Safari 14 #17103

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

Merged
merged 50 commits into from
Jun 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9e78b25
Add BigInt64Array shim for Safari 14
hoodmane May 30, 2022
65ea16c
Fix test
hoodmane May 30, 2022
bf504bc
Move polyfill to a separate file, add some comments
hoodmane May 31, 2022
7649692
Fix support for indented include in parseTools.js:preprocess
hoodmane Jun 1, 2022
c44bc67
Address review comments
hoodmane Jun 1, 2022
b2a401e
Remove extra closing brace, formatting fixes
hoodmane Jun 1, 2022
ab54574
Polyfill BigInt64Array and BigUint64Array
hoodmane Jun 1, 2022
76be232
Add newline at end of file
hoodmane Jun 1, 2022
1b83731
Unindent
hoodmane Jun 1, 2022
9b00c8b
Use var instead of globalThis
hoodmane Jun 1, 2022
6e5df7a
Trigger CI
hoodmane Jun 1, 2022
7b96688
Remove !POLYFILL guard
hoodmane Jun 1, 2022
7e3c36d
Revert "Remove !POLYFILL guard"
hoodmane Jun 1, 2022
cd0191a
Trigger CI
hoodmane Jun 1, 2022
0a4992f
Update src/polyfill/bigint64array.js
hoodmane Jun 2, 2022
b0fe37a
Update src/preamble.js
hoodmane Jun 2, 2022
c0b6169
Move polyfill to shell.js, fix buggy scope handling
hoodmane Jun 2, 2022
d848fad
Revert fix to parseTools
hoodmane Jun 2, 2022
9991238
Try modifying default min browser settings when WASM_BIGINT is present
hoodmane Jun 2, 2022
5973cc1
Add comment
hoodmane Jun 2, 2022
d760a5a
Fix flake8
hoodmane Jun 2, 2022
07b3168
Merge branch 'main' into bigint64array-safari14-shim
hoodmane Jun 6, 2022
57cd5f9
Return true from set handler
hoodmane Jun 9, 2022
8ff2a51
Merge branch 'main' into bigint64array-safari14-shim
hoodmane Jun 9, 2022
d9cf852
Merge branch 'main' into bigint64array-safari14-shim
hoodmane Jun 10, 2022
e48a295
Remove uneeded stuff
hoodmane Jun 10, 2022
50a642f
reduce diff
hoodmane Jun 10, 2022
f3af0f7
Reduce diff
hoodmane Jun 10, 2022
a7c9e90
Update comment
hoodmane Jun 10, 2022
550664b
Update indentation
hoodmane Jun 10, 2022
aa60069
Remove unneeded vars
hoodmane Jun 10, 2022
b93d672
Remove comment
hoodmane Jun 10, 2022
374b9e5
Apply sbc100's suggestion
hoodmane Jun 10, 2022
134aa3e
Address sbc100's review comments
hoodmane Jun 10, 2022
ce8f3c4
Use BigInt constant notation
hoodmane Jun 10, 2022
61a3022
Update changelog
hoodmane Jun 10, 2022
c964738
Add tests
hoodmane Jun 15, 2022
3181088
Merge branch 'main' into bigint64array-safari14-shim
hoodmane Jun 15, 2022
7ee6b31
Fix flake8
hoodmane Jun 15, 2022
b932ec7
Fix indentation
hoodmane Jun 15, 2022
d9a73d5
Merge branch 'main' into bigint64array-safari14-shim
hoodmane Jun 22, 2022
5da883f
Move changelog entry
hoodmane Jun 22, 2022
7f45581
Add !POLYFILL guard
hoodmane Jun 22, 2022
d381d6e
Remove blank line
hoodmane Jun 22, 2022
e6883db
Fix indentation in test
hoodmane Jun 22, 2022
119a84f
Rearrange tests a bit
hoodmane Jun 22, 2022
35936ce
Address some of sbc100's review comments
hoodmane Jun 22, 2022
a3b872c
More adjustments
hoodmane Jun 22, 2022
d45b70c
Use single quotes
hoodmane Jun 22, 2022
bb818df
Use single quotes for js strings too
hoodmane Jun 22, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ See docs/process.md for more on how version tagging works.

3.1.15
------
- Added a shim for `BigInt64Array` so `-sWASM_BIGINT` can be used in Safari
v14. (#17103)

3.1.14 - 06/20/2022
-------------------
Expand Down
115 changes: 115 additions & 0 deletions src/polyfill/bigint64array.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#if !POLYFILL
assert(false, "this file should never be included unless POLYFILL is set");
#endif

if (typeof globalThis.BigInt64Array === "undefined") {
// BigInt64Array polyfill for Safari versions between v14.0 and v15.0.
// All browsers other than Safari added BigInt and BigInt64Array at the same
// time, but Safari introduced BigInt in v14.0 and introduced BigInt64Array in
// v15.0

function partsToBigIntSigned(lower, upper) {
return BigInt(lower) | (BigInt(upper + 2 * (upper & 0x80000000)) << 32n);
}

function partsToBigIntUnsigned(lower, upper) {
return BigInt(lower) | (BigInt(upper) << 32n);
}

function bigIntToParts(value) {
var lower = Number(BigInt(value) & BigInt(0xffffffff)) | 0;
var upper = Number(BigInt(value) >> 32n) | 0;
return [lower, upper];
}

function createBigIntArrayShim(partsToBigInt) {
function createBigInt64Array(array) {
if (typeof array === "number") {
array = new Uint32Array(2 * array);
}
var orig_array;
if (!ArrayBuffer.isView(array)) {
if (array.constructor && array.constructor.name === "ArrayBuffer") {
array = new Uint32Array(array);
} else {
orig_array = array;
array = new Uint32Array(array.length * 2);
}
}
var proxy = new Proxy(
{
slice: function (min, max) {
if (max === undefined) {
max = array.length;
}
var new_buf = array.slice(min * 2, max * 2);
return createBigInt64Array(new_buf);
},
subarray: function (min, max) {
var new_buf = array.subarray(min * 2, max * 2);
return createBigInt64Array(new_buf);
},
[Symbol.iterator]: function* () {
for (var i = 0; i < array.length / 2; i++) {
yield partsToBigInt(array[2 * i], array[2 * i + 1]);
}
},
BYTES_PER_ELEMENT: 2 * array.BYTES_PER_ELEMENT,
buffer: array.buffer,
byteLength: array.byteLength,
byteOffset: array.byteOffset,
length: array.length / 2,
copyWithin: function (target, start, end) {
array.copyWithin(target * 2, start * 2, end * 2);
return proxy;
},
set: function (source, targetOffset) {
if (targetOffset === undefined) {
targetOffset = 0;
}
if (2 * (source.length + targetOffset) > array.length) {
// This is the Chrome error message
// Firefox: "invalid or out-of-range index"
throw new RangeError("offset is out of bounds");
}
for (var i = 0; i < source.length; i++) {
var value = source[i];
var pair = bigIntToParts(value);
array.set(pair, 2 * (targetOffset + i));
}
},
},
{
get: function (target, idx, receiver) {
if (typeof idx !== "string" || !/^\d+$/.test(idx)) {
return Reflect.get(target, idx, receiver);
}
var lower = array[idx * 2];
var upper = array[idx * 2 + 1];
return partsToBigInt(lower, upper);
},
set: function (target, idx, value, receiver) {
if (typeof idx !== "string" || !/^\d+$/.test(idx)) {
return Reflect.set(target, idx, value, receiver);
}
if (typeof value !== "bigint") {
// Chrome error message, Firefox has no "a" in front if "BigInt".
throw new TypeError(`Cannot convert ${value} to a BigInt`);
}
var pair = bigIntToParts(value);
array.set(pair, 2 * idx);
return true;
},
}
);
if (orig_array) {
proxy.set(orig_array);
}
return proxy;
}
return createBigInt64Array;
}

globalThis.BigUint64Array = createBigIntArrayShim(partsToBigIntUnsigned);
globalThis.BigInt64Array = createBigIntArrayShim(partsToBigIntSigned);
}
5 changes: 5 additions & 0 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ var Module = typeof {{{ EXPORT_NAME }}} != 'undefined' ? {{{ EXPORT_NAME }}} : {
#if MIN_CHROME_VERSION < 45 || MIN_EDGE_VERSION < 12 || MIN_FIREFOX_VERSION < 34 || MIN_IE_VERSION != TARGET_NOT_SUPPORTED || MIN_SAFARI_VERSION < 90000
#include "polyfill/objassign.js"
#endif

// See https://caniuse.com/mdn-javascript_builtins_bigint64array
#if WASM_BIGINT && MIN_SAFARI_VERSION < 150000
#include "polyfill/bigint64array.js"
#endif
#endif // POLYFILL

#if MODULARIZE
Expand Down
96 changes: 96 additions & 0 deletions tests/test_bigint64array_polyfill.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
let result = {};
result.BigInt64Array_name = BigInt64Array.name;
let arr2signed = new BigInt64Array(arr1signed.buffer);
let arr2unsigned = new BigUint64Array(arr1unsigned.buffer);

result.arr1_to_arr1_signed = [];
for(let x of bigint_list){
arr1signed[0] = x;
result.arr1_to_arr1_signed.push(arr1signed[0]);
}

result.arr1_to_arr1_unsigned = [];
for(let x of bigint_list){
arr1unsigned[0] = x;
result.arr1_to_arr1_unsigned.push(arr1unsigned[0]);
}

result.arr1_to_arr2_signed = [];
for(let x of bigint_list){
arr1signed[0] = x;
result.arr1_to_arr2_signed.push(arr2signed[0]);
}

result.arr1_to_arr2_unsigned = [];
for(let x of bigint_list){
arr1unsigned[0] = x;
result.arr1_to_arr2_unsigned.push(arr2unsigned[0]);
}

result.arr2_to_arr1_signed = [];
for(let x of bigint_list){
arr2signed[0] = x;
result.arr2_to_arr1_signed.push(arr1signed[0]);
}

result.arr2_to_arr1_unsigned = [];
for(let x of bigint_list){
arr2unsigned[0] = x;
result.arr2_to_arr1_unsigned.push(arr1unsigned[0]);
}


result.assertEquals = [];
function assertEqual(cb){
result.assertEquals.push([cb.toString(), cb()]);
}

let arr1 = arr1unsigned;
let arr2 = arr2unsigned;
assertEqual(() => [arr2.BYTES_PER_ELEMENT, arr1.BYTES_PER_ELEMENT]);
assertEqual(() => [arr2.byteLength, arr1.byteLength]);
assertEqual(() => [arr2.length, arr1.length]);
assertEqual(() => [arr2.slice().length, arr1.slice().length]);
assertEqual(() => [arr2.slice(1, 5).length, arr1.slice(1, 5).length]);

arr1[0] = 1n; arr1[1] = 2n; arr1[2] = 3n; arr1[3] = 4n; arr1[4] = 5n;

result.arr2_slice = Array.from(arr2.slice(1, 5));
result.arr2_subarray = Array.from(arr2.subarray(1, 5));
let reducer = (k, v) => typeof v === 'bigint' ? v.toString() + 'n' : v;
function arraytostring(arr){
return JSON.stringify(Array.from(Array.from(arr)), reducer);
}

let sub = arr2.subarray(1, 5);
assertEqual(() => [sub.byteOffset, 8]);
sub[0] = 7n; sub[1] = 77n; sub[2] = 3n; sub[3] = 66n;
assertEqual(() => [arraytostring(arr1.slice(0, 5)), '["1n","7n","77n","3n","66n"]']);

arr1[2] = 62n;
assertEqual(() => [sub[1], 62n]);

let slice = arr2.slice(1, 5);
assertEqual(() => [slice.byteOffset, 0]);

slice[0] = 777n; slice[1] = 666n; slice[2] = 555n;
assertEqual(() => [arraytostring(arr1.slice(0, 5)), '["1n","7n","62n","3n","66n"]']);

arr2.set([2n, 4n, 8n]);
assertEqual(() => [arraytostring(arr1.slice(0, 3)), '["2n","4n","8n"]']);

arr2.set([1n, 3n, 7n], 6);
assertEqual(() => [arraytostring(arr1.slice(6, 9)), '["1n","3n","7n"]']);

arr1[15] = 111n; arr1[18] = 171n; arr1[19] = 629n;

assertEqual(() => [arraytostring(arr2.slice(-1)), '["629n"]']);
assertEqual(() => [arraytostring(arr2.slice(-5, -1)), '["111n","0n","0n","171n"]']);
assertEqual(() => [arraytostring(arr2.slice(-5, -6)), '[]']);

arr3 = new BigUint64Array(Array.from({length:5}, (_, idx) => BigInt(idx)));
assertEqual(() => [arraytostring(arr3), '["0n","1n","2n","3n","4n"]']);
arr3.copyWithin(0, 2, 10);
assertEqual(() => [arraytostring(arr3), '["2n","3n","4n","3n","4n"]']);

console.log(JSON.stringify(result, reducer));
50 changes: 50 additions & 0 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -12226,3 +12226,53 @@ def test_rust_gxx_personality_v0(self):
}
}
''', assert_returncode=NON_ZERO)

def test_bigint64array_polyfill(self):
bigint64array = read_file(path_from_root('src/polyfill/bigint64array.js'))
test_code = read_file(test_file('test_bigint64array_polyfill.js'))
bigint_list = [
0,
1,
-1,
5,
(1 << 64),
(1 << 64) - 1,
(1 << 64) + 1,
(1 << 63),
(1 << 63) - 1,
(1 << 63) + 1,
]
bigint_list_strs = [str(x) for x in bigint_list]

bigint_list_unsigned = [x % (1 << 64) for x in bigint_list]
bigint_list_signed = [
x if x < 0 else (x % (1 << 64)) - 2 * (x & (1 << 63)) for x in bigint_list
]
bigint_list_unsigned_n = [f'{x}n' for x in bigint_list_unsigned]
bigint_list_signed_n = [f'{x}n' for x in bigint_list_signed]

bigint64array = '\n'.join(bigint64array.splitlines()[3:])

create_file(
'test.js',
f'''
let bigint_list = {bigint_list_strs}.map(x => BigInt(x));
let arr1signed = new BigInt64Array(20);
let arr1unsigned = new BigUint64Array(20);
delete globalThis.BigInt64Array;
''' + bigint64array + test_code
)
output = json.loads(self.run_js('test.js'))
self.assertEqual(output['BigInt64Array_name'], 'createBigInt64Array')
for key in ['arr1_to_arr1', 'arr1_to_arr2', 'arr2_to_arr1']:
print(key + '_unsigned')
self.assertEqual(output[key + '_unsigned'], bigint_list_unsigned_n)
for key in ['arr1_to_arr1', 'arr1_to_arr2', 'arr2_to_arr1']:
print(key + '_signed')
self.assertEqual(output[key + '_signed'], bigint_list_signed_n)

self.assertEqual(output['arr2_slice'], ['2n', '3n', '4n', '5n'])
self.assertEqual(output['arr2_subarray'], ['2n', '3n', '4n', '5n'])

for m, [v1, v2] in output['assertEquals']:
self.assertEqual(v1, v2, msg=m)