Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9979b1b
missing functions in link.py
slavek-kucera Aug 22, 2025
7a212b9
refactor LE heap conversion
slavek-kucera Aug 22, 2025
e681162
return name of the heap from isHEAPAccess
slavek-kucera Aug 22, 2025
dba4c2b
extract make sequence code
slavek-kucera Aug 22, 2025
2c6340f
extract call growmemviews
slavek-kucera Aug 22, 2025
48e36e9
heap access replacement
slavek-kucera Aug 22, 2025
9ff0167
fixes
slavek-kucera Aug 25, 2025
d19511d
minor cleanup
slavek-kucera Aug 25, 2025
2ae88f5
add tests
slavek-kucera Aug 25, 2025
d940692
prettier
slavek-kucera Aug 25, 2025
fdf3412
allow combined options to work
slavek-kucera Aug 27, 2025
609aca1
skip establishStackSpace in asanifyTransform
slavek-kucera Aug 28, 2025
9b3a4d6
fix webidl support
slavek-kucera Aug 28, 2025
3a67a2b
incorrectly disabled SAFE_HEAP annotations
slavek-kucera Aug 28, 2025
a526e01
always suppress checkTypes
slavek-kucera Aug 28, 2025
bed4d9f
disable wasm2js on big endian
slavek-kucera Aug 28, 2025
dd66c95
closure does not know waitAsync
slavek-kucera Aug 28, 2025
c4b4688
closure false warning workaround
slavek-kucera Aug 29, 2025
d1943c0
BE flag not propagated
slavek-kucera Aug 29, 2025
57efae1
Fix marshaling of arrays of primitive types
slavek-kucera Aug 29, 2025
4108ca6
disable incompatible tests
slavek-kucera Aug 29, 2025
8fbafa9
better waitAsync closure fix
slavek-kucera Aug 29, 2025
d12208e
more closure...
slavek-kucera Aug 29, 2025
68f51b9
signature update
slavek-kucera Aug 29, 2025
77061cc
and more closure...
slavek-kucera Aug 29, 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
82 changes: 82 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,87 @@ 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;
}
})();
// using for..loop is faster than Array.from
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 Datadst(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;
}
})();
// using for..loop is faster than Array.from
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
4 changes: 3 additions & 1 deletion src/lib/liblittle_endian_heap.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,9 @@ function LE_HEAP_UPDATE() {
},
$LE_ATOMICS_WAITASYNC: (heap, offset, value, timeout) => {
const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1];
return Atomics.waitAsync(heap, offset, order(value), timeout);
/** @suppress {missingProperties} */
const result = Atomics.waitAsync(heap, offset, order(value), timeout);
return result;
},
$LE_ATOMICS_XOR: (heap, offset, value) => {
const order = LE_ATOMICS_NATIVE_BYTE_ORDER[heap.BYTES_PER_ELEMENT - 1];
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