Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e38e078
missing functions in link.py
slavek-kucera Aug 22, 2025
9defd56
refactor LE heap conversion
slavek-kucera Aug 22, 2025
886a28a
return name of the heap from isHEAPAccess
slavek-kucera Aug 22, 2025
ff1adb0
extract make sequence code
slavek-kucera Aug 22, 2025
d0433c1
extract call growmemviews
slavek-kucera Aug 22, 2025
8428444
heap access replacement
slavek-kucera Aug 22, 2025
9945bd9
fixes
slavek-kucera Aug 25, 2025
f249237
minor cleanup
slavek-kucera Aug 25, 2025
17870e8
add tests
slavek-kucera Aug 25, 2025
7c24a3c
prettier
slavek-kucera Aug 25, 2025
438e5eb
allow combined options to work
slavek-kucera Aug 27, 2025
037f253
skip establishStackSpace in asanifyTransform
slavek-kucera Aug 28, 2025
217f8cc
fix webidl support
slavek-kucera Aug 28, 2025
734183a
incorrectly disabled SAFE_HEAP annotations
slavek-kucera Aug 28, 2025
cafd8e6
always suppress checkTypes
slavek-kucera Aug 28, 2025
6452c67
disable wasm2js on big endian
slavek-kucera Aug 28, 2025
ba465ba
closure does not know waitAsync
slavek-kucera Aug 28, 2025
1938426
closure false warning workaround
slavek-kucera Aug 29, 2025
0c307bb
BE flag not propagated
slavek-kucera Aug 29, 2025
100f89f
Fix marshaling of arrays of primitive types
slavek-kucera Aug 29, 2025
c3173ce
disable incompatible tests
slavek-kucera Aug 29, 2025
77804ca
better waitAsync closure fix
slavek-kucera Aug 29, 2025
b1d78b3
more closure...
slavek-kucera Aug 29, 2025
fc9de32
signature update
slavek-kucera Aug 29, 2025
b583764
and more closure...
slavek-kucera Aug 29, 2025
49ae33b
cleanup
slavek-kucera Sep 1, 2025
1137bce
one more chance for closure
slavek-kucera Sep 1, 2025
27bb154
typo
slavek-kucera Sep 1, 2025
858e30a
mark incompatible test
slavek-kucera Sep 1, 2025
a06e86a
produce error when combining wasm2js and big endian support
slavek-kucera Sep 2, 2025
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
5 changes: 5 additions & 0 deletions src/closure-externs/closure-externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ var Atomics = {};
Atomics.compareExchange = function() {};
Atomics.exchange = function() {};
Atomics.wait = function() {};
/**
* @param {number=} maxWaitMilliseconds
* @suppress {duplicate, checkTypes}
*/
Atomics.waitAsync = function(i32a, index, value, maxWaitMilliseconds) {};
Atomics.notify = function() {};
Atomics.load = function() {};
Atomics.store = function() {};
Expand Down
80 changes: 80 additions & 0 deletions src/lib/libemval.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ var LibraryEmVal = {
_emval_new_array__deps: ['$Emval'],
_emval_new_array: () => Emval.toHandle([]),

#if !SUPPORT_BIG_ENDIAN
_emval_new_array_from_memory_view__deps: ['$Emval'],
_emval_new_array_from_memory_view: (view) => {
view = Emval.toValue(view);
Expand All @@ -109,6 +110,85 @@ var LibraryEmVal = {
for (var i = 0; i < view.length; i++) a[i] = view[i];
return Emval.toHandle(a);
},
_emval_array_to_memory_view__deps: ['$Emval'],
_emval_array_to_memory_view: (dst, src) => {
dst = Emval.toValue(dst);
src = Emval.toValue(src);
dst.set(src);
},
#else
_emval_new_array_from_memory_view__deps: ['$Emval'],
_emval_new_array_from_memory_view: (view) => {
view = Emval.toValue(view);
var reader = (() => {
if (view.BYTES_PER_ELEMENT===1)
return i => view[i];
const dv = new DataView(view.buffer, view.byteOffset);
switch(view.BYTES_PER_ELEMENT) {
case 2:
if (view instanceof Int16Array)
return i => dv.getInt16(i * 2, true)
if (view instanceof Uint16Array)
return i => dv.getUint16(i * 2, true)
break;
case 4:
if (view instanceof Int32Array)
return i => dv.getInt32(i * 4, true)
if (view instanceof Uint32Array)
return i => dv.getUint32(i * 4, true)
if (view instanceof Float32Array)
return i => dv.getFloat32(i * 4, true)
break;
case 8:
if (view instanceof BigInt32Array)
return i => dv.getBigInt32(i * 8, true)
if (view instanceof BigUint32Array)
return i => dv.getBigUint32(i * 8, true)
if (view instanceof Float64Array)
return i => dv.getFloat64(i * 8, true)
break;
}
})();
var a = new Array(view.length);
for (var i = 0; i < view.length; i++) a[i] = reader(i);
return Emval.toHandle(a);
},
_emval_array_to_memory_view__deps: ['$Emval'],
_emval_array_to_memory_view: (dst, src) => {
dst = Emval.toValue(dst);
src = Emval.toValue(src);
var writer = (() => {
if (dst.BYTES_PER_ELEMENT===1)
return (i, v) => { dst[i] = v; };
const dv = new DataView(dst.buffer, dst.byteOffset);
switch(dst.BYTES_PER_ELEMENT) {
case 2:
if (dst instanceof Int16Array)
return (i, v) => dv.setInt16(i * 2, v, true)
if (dst instanceof Uint16Array)
return (i, v) => dv.setUint16(i * 2, v, true)
break;
case 4:
if (dst instanceof Int32Array)
return (i, v) => dv.setInt32(i * 4, v, true)
if (dst instanceof Uint32Array)
return (i, v) => dv.setUint32(i * 4, v, true)
if (dst instanceof Float32Array)
return (i, v) => dv.setFloat32(i * 4, v, true)
break;
case 8:
if (dst instanceof BigInt32Array)
return (i, v) => dv.setBigInt32(i * 8, v, true)
if (dst instanceof BigUint32Array)
return (i, v) => dv.setBigUint32(i * 8, v, true)
if (dst instanceof Float64Array)
return (i, v) => dv.setFloat64(i * 8, v, true)
break;
}
})();
for (var i = 0; i < src.length; i++) writer(i, src[i]);
},
#endif

_emval_new_object__deps: ['$Emval'],
_emval_new_object: () => Emval.toHandle({}),
Expand Down
8 changes: 0 additions & 8 deletions src/lib/libhtml5.js
Original file line number Diff line number Diff line change
Expand Up @@ -1001,10 +1001,8 @@ var LibraryHTML5 = {
$fillFullscreenChangeEventData: (eventStruct) => {
var fullscreenElement = getFullscreenElement();
var isFullscreen = !!fullscreenElement;
#if !SAFE_HEAP
// Assigning a boolean to HEAP32 with expected type coercion.
/** @suppress{checkTypes} */
#endif
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.isFullscreen, 'isFullscreen', 'i8') }}};
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenFullscreenChangeEvent.fullscreenEnabled, 'JSEvents.fullscreenEnabled()', 'i8') }}};
// If transitioning to fullscreen, report info about the element that is now fullscreen.
Expand Down Expand Up @@ -1544,10 +1542,8 @@ var LibraryHTML5 = {
$fillPointerlockChangeEventData: (eventStruct) => {
var pointerLockElement = document.pointerLockElement;
var isPointerlocked = !!pointerLockElement;
#if !SAFE_HEAP
// Assigning a boolean to HEAP32 with expected type coercion.
/** @suppress{checkTypes} */
#endif
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenPointerlockChangeEvent.isActive, 'isPointerlocked', 'i8') }}};
var nodeName = JSEvents.getNodeNameForTarget(pointerLockElement);
var id = pointerLockElement?.id || '';
Expand Down Expand Up @@ -1746,10 +1742,8 @@ var LibraryHTML5 = {
var visibilityStates = [ "hidden", "visible", "prerender", "unloaded" ];
var visibilityState = visibilityStates.indexOf(document.visibilityState);

#if !SAFE_HEAP
// Assigning a boolean to HEAP32 with expected type coercion.
/** @suppress{checkTypes} */
#endif
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.hidden, 'document.hidden', 'i8') }}};
{{{ makeSetValue('eventStruct', C_STRUCTS.EmscriptenVisibilityChangeEvent.visibilityState, 'visibilityState', 'i32') }}};
},
Expand Down Expand Up @@ -1943,10 +1937,8 @@ var LibraryHTML5 = {
if (typeof e.buttons[i] == 'object') {
{{{ makeSetValue('eventStruct+i', C_STRUCTS.EmscriptenGamepadEvent.digitalButton, 'e.buttons[i].pressed', 'i8') }}};
} else {
#if !SAFE_HEAP
// Assigning a boolean to HEAP32, that's ok, but Closure would like to warn about it:
/** @suppress {checkTypes} */
#endif
{{{ makeSetValue('eventStruct+i', C_STRUCTS.EmscriptenGamepadEvent.digitalButton, 'e.buttons[i] == 1', 'i8') }}};
}
}
Expand Down
2 changes: 0 additions & 2 deletions src/lib/libsdl.js
Original file line number Diff line number Diff line change
Expand Up @@ -857,12 +857,10 @@ var LibrarySDL = {
var code = SDL.lookupKeyCodeForEvent(event);
// Ignore key events that we don't (yet) map to SDL keys
if (!code) return;
#if !SAFE_HEAP
// Assigning a boolean to HEAP8, that's alright but Closure would like to warn about it.
// TODO(https://github.com/emscripten-core/emscripten/issues/16311):
// This is kind of ugly hack. Perhaps we can find a better way?
/** @suppress{checkTypes} */
#endif
{{{ makeSetValue('SDL.keyboardState', 'code', 'down', 'i8') }}};
// TODO: lmeta, rmeta, numlock, capslock, KMOD_MODE, KMOD_RESERVED
SDL.modState =
Expand Down
1 change: 1 addition & 0 deletions src/lib/libsigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ sigs = {
_emscripten_thread_mailbox_await__sig: 'vp',
_emscripten_thread_set_strongref__sig: 'vp',
_emscripten_throw_longjmp__sig: 'v',
_emval_array_to_memory_view__sig: 'vpp',
_emval_await__sig: 'pp',
_emval_coro_make_promise__sig: 'ppp',
_emval_coro_suspend__sig: 'vpp',
Expand Down
11 changes: 1 addition & 10 deletions src/runtime_asan.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,5 @@ function _asan_js_check_index(arr, index, asanFn) {
const elemSize = arr.BYTES_PER_ELEMENT;
asanFn(index * elemSize, elemSize);
}
}

function _asan_js_load(arr, index) {
_asan_js_check_index(arr, index, ___asan_loadN);
return arr[index];
}

function _asan_js_store(arr, index, value) {
_asan_js_check_index(arr, index, ___asan_storeN);
return arr[index] = value;
return index;
}
8 changes: 0 additions & 8 deletions src/runtime_safe_heap.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,6 @@ function SAFE_HEAP_INDEX(arr, idx, action) {
return idx;
}

function SAFE_HEAP_LOAD(arr, idx) {
return arr[SAFE_HEAP_INDEX(arr, idx, 'loading')];
}

function SAFE_HEAP_STORE(arr, idx, value) {
return arr[SAFE_HEAP_INDEX(arr, idx, 'storing')] = value;
}

function segfault() {
abort('segmentation fault');
}
Expand Down
3 changes: 2 additions & 1 deletion system/include/emscripten/val.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ void _emval_run_destructors(EM_DESTRUCTORS handle);

EM_VAL _emval_new_array(void);
EM_VAL _emval_new_array_from_memory_view(EM_VAL mv);
void _emval_array_to_memory_view(EM_VAL dst, EM_VAL src);
EM_VAL _emval_new_object(void);
EM_VAL _emval_new_cstring(const char*);
EM_VAL _emval_new_u8string(const char*);
Expand Down Expand Up @@ -828,7 +829,7 @@ std::vector<T> convertJSArrayToNumberVector(const val& v) {
// See https://www.ecma-international.org/ecma-262/6.0/#sec-%typedarray%.prototype.set-array-offset
// and https://www.ecma-international.org/ecma-262/6.0/#sec-tonumber
val memoryView{ typed_memory_view(l, rv.data()) };
memoryView.call<void>("set", v);
internal::_emval_array_to_memory_view(memoryView.as_handle(), v.as_handle());

return rv;
}
Expand Down
55 changes: 55 additions & 0 deletions test/js_optimizer/LittleEndianGrowableHeap-output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
a = (growMemViews(), HEAP8)[x];

(growMemViews(), HEAP8)[x] = a;

a = (growMemViews(), HEAPU8)[x];

(growMemViews(), HEAPU8)[x] = a;

a = LE_HEAP_LOAD_I16((growMemViews(), x * 2));

LE_HEAP_STORE_I16((growMemViews(), x * 2), a);

a = LE_HEAP_LOAD_U16((growMemViews(), x * 2));

LE_HEAP_STORE_U16((growMemViews(), x * 2), a);

a = LE_HEAP_LOAD_I32((growMemViews(), x * 4));

LE_HEAP_STORE_I32((growMemViews(), x * 4), a);

a = LE_HEAP_LOAD_U32((growMemViews(), x * 4));

LE_HEAP_STORE_U32((growMemViews(), x * 4), a);

a = LE_HEAP_LOAD_F32((growMemViews(), x * 4));

LE_HEAP_STORE_F32((growMemViews(), x * 4), a);

a = LE_HEAP_LOAD_F64((growMemViews(), x * 8));

LE_HEAP_STORE_F64((growMemViews(), x * 8), a);

HEAP[x];

HeAp[x];

LE_ATOMICS_ADD(heap, offset, value);

LE_ATOMICS_AND(heap, offset, value);

LE_ATOMICS_COMPAREEXCHANGE(heap, offset, expected, replacement);

LE_ATOMICS_EXCHANGE(heap, offset, value);

LE_ATOMICS_LOAD(heap, offset);

LE_ATOMICS_OR(heap, offset, value);

LE_ATOMICS_SUB(heap, offset, value);

LE_ATOMICS_WAIT(heap, offset, value, timeout);

LE_ATOMICS_WAITASYNC(heap, offset, value, timeout);

LE_ATOMICS_XOR(heap, offset, value);
28 changes: 28 additions & 0 deletions test/js_optimizer/LittleEndianGrowableHeap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
a = HEAP8[x]; // HEAP8
HEAP8[x] = a;
a = HEAPU8[x]; // HEAPU8
HEAPU8[x] = a;
a = HEAP16[x]; // HEAP16
HEAP16[x] = a;
a = HEAPU16[x]; // HEAPU16
HEAPU16[x] = a;
a = HEAP32[x]; // HEAPI32
HEAP32[x] = a;
a = HEAPU32[x]; // HEAPU32
HEAPU32[x] = a;
a = HEAPF32[x]; // HEAPF32
HEAPF32[x] = a;
a = HEAPF64[x]; // HEAPF64
HEAPF64[x] = a;
HEAP[x]; // should not be changed
HeAp[x];
Atomics.add(heap, offset, value);
Atomics.and(heap, offset, value);
Atomics.compareExchange(heap, offset, expected, replacement);
Atomics.exchange(heap, offset, value);
Atomics.load(heap, offset);
Atomics.or(heap, offset, value);
Atomics.sub(heap, offset, value);
Atomics.wait(heap, offset, value, timeout);
Atomics.waitAsync(heap, offset, value, timeout);
Atomics.xor(heap, offset, value);
55 changes: 55 additions & 0 deletions test/js_optimizer/LittleEndianGrowableSafeHeap-output.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
a = (growMemViews(), HEAP8)[SAFE_HEAP_INDEX((growMemViews(), HEAP8), x, "loading")];

(growMemViews(), HEAP8)[SAFE_HEAP_INDEX((growMemViews(), HEAP8), x, "storing")] = a;

a = (growMemViews(), HEAPU8)[SAFE_HEAP_INDEX((growMemViews(), HEAPU8), x, "loading")];

(growMemViews(), HEAPU8)[SAFE_HEAP_INDEX((growMemViews(), HEAPU8), x, "storing")] = a;

a = LE_HEAP_LOAD_I16((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAP16), x, "loading") * 2));

LE_HEAP_STORE_I16((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAP16), x, "storing") * 2), a);

a = LE_HEAP_LOAD_U16((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPU16), x, "loading") * 2));

LE_HEAP_STORE_U16((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPU16), x, "storing") * 2), a);

a = LE_HEAP_LOAD_I32((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAP32), x, "loading") * 4));

LE_HEAP_STORE_I32((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAP32), x, "storing") * 4), a);

a = LE_HEAP_LOAD_U32((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPU32), x, "loading") * 4));

LE_HEAP_STORE_U32((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPU32), x, "storing") * 4), a);

a = LE_HEAP_LOAD_F32((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPF32), x, "loading") * 4));

LE_HEAP_STORE_F32((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPF32), x, "storing") * 4), a);

a = LE_HEAP_LOAD_F64((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPF64), x, "loading") * 8));

LE_HEAP_STORE_F64((growMemViews(), SAFE_HEAP_INDEX((growMemViews(), HEAPF64), x, "storing") * 8), a);

HEAP[x];

HeAp[x];

LE_ATOMICS_ADD(heap, offset, value);

LE_ATOMICS_AND(heap, offset, value);

LE_ATOMICS_COMPAREEXCHANGE(heap, offset, expected, replacement);

LE_ATOMICS_EXCHANGE(heap, offset, value);

LE_ATOMICS_LOAD(heap, offset);

LE_ATOMICS_OR(heap, offset, value);

LE_ATOMICS_SUB(heap, offset, value);

LE_ATOMICS_WAIT(heap, offset, value, timeout);

LE_ATOMICS_WAITASYNC(heap, offset, value, timeout);

LE_ATOMICS_XOR(heap, offset, value);
28 changes: 28 additions & 0 deletions test/js_optimizer/LittleEndianGrowableSafeHeap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
a = HEAP8[x]; // HEAP8
HEAP8[x] = a;
a = HEAPU8[x]; // HEAPU8
HEAPU8[x] = a;
a = HEAP16[x]; // HEAP16
HEAP16[x] = a;
a = HEAPU16[x]; // HEAPU16
HEAPU16[x] = a;
a = HEAP32[x]; // HEAPI32
HEAP32[x] = a;
a = HEAPU32[x]; // HEAPU32
HEAPU32[x] = a;
a = HEAPF32[x]; // HEAPF32
HEAPF32[x] = a;
a = HEAPF64[x]; // HEAPF64
HEAPF64[x] = a;
HEAP[x]; // should not be changed
HeAp[x];
Atomics.add(heap, offset, value);
Atomics.and(heap, offset, value);
Atomics.compareExchange(heap, offset, expected, replacement);
Atomics.exchange(heap, offset, value);
Atomics.load(heap, offset);
Atomics.or(heap, offset, value);
Atomics.sub(heap, offset, value);
Atomics.wait(heap, offset, value, timeout);
Atomics.waitAsync(heap, offset, value, timeout);
Atomics.xor(heap, offset, value);
Loading
Loading