Please support "No Exceptions" in WASM 2023 for Unity 6

Targeting WebAssembly 2023 disables exceptions set to None.

Our application is originally built to crash when something “wrong” happens, as WebGL did not allow catching exceptions at an acceptable performance level. This also ensures that our users cannot continue using the application (and saving to a database) when runtime data gets corrupted by uncaught exceptions.

It would be very much appreciated to enjoy the new features of WASM 2023 and still allowing the application to exit/quit on exceptions since all our flows are built around this way of dealing with errors.

Is it possible to add “None” exception support to WASM 2023 in a future update?

Is there any Unity employee that can chime in on this topic?

In the Wasm 2023 feature set, exception catching is “built in” into the WebAssembly VM codegen behavior.

Before Wasm 2023, WebAssembly specification did not natively support exceptions, so it “loaned” exception support from the outside Javascript VM, implementing exceptions by calling out to throwing JS exceptions.

There were quite a few performance implications to the old behavior, but it did provide this nice feature that it was possible to disable exceptions in a build just by tweaking the JS side code. The compiled Wasm code didn’t need to change, and the same code could be shipped in exceptions enabled vs disabled builds: only a small tweak to the JS code would be needed.

However now with Wasm 2023, since exceptions are a built-in feature to WebAssembly, disabling exceptions means that all the compiled code (including Unity engine and C# runtime) will need to be compiled twice to generate a no-exceptions and an exceptions-enabled builds. This would result in doubling of the number of permutations of the Unity Web engine that would need to be shipped.

We are currently struggling quite a bit with the number of combinations: the engine distributable is at 15 variants, and the upcoming Wasm64 feature set is expected to bring that up to 23 or so; which is a large download footprint for the Web Player Module to have during Editor installation. (the number of variants multiply combinatorially) No-exceptions variants of Wasm2023 would further increase this download size by several hundred MBs.

So this is all to say that it is quite tricky to manage. Originally our hope was that with native Wasm exception handling, the “no exceptions” mode would be a thing of the past, and help to simplify the number of combinations needed here.

I’ve pinged the IL2CPP team to see if they would have a mechanism to allow the C# runtime to abort on thrown exceptions, although not completely sure if such a mechanism exists. For the time being, I recommend still using the non-Wasm2023 feature set to keep the ability to disable exception catching.

In the future we are looking to remove the non-Wasm2023 feature set. Maybe at that point it might be possible to replace that with a exceptions enabled vs disabled combinations of Wasm2023, to enable this use case. We’ll definitely keep this request on the radar.

Thank you @jukka_j for the detailed reply. For now, we will keep using pre-2023 wasm until we have found a solution.

Is there any workaround possible? Would calling unityInstance.Quit() work when specific error messages or generic exceptions are thrown?

I have also looked a little into the _JS_LOG_DUMP method on the javascript side of the Unity Web Engine. However, it seems like the exceptions are thrown as errors (type:0), plus it does not seem possible to override this method with a custom jslib file. Maybe there is still a solution possible there?

var LibraryLoggingWebGL = {

JS_Log_Dump__deps: ['$UTF8ToString'],
JS_Log_Dump: function(ptr, type)
{
	var str = UTF8ToString(ptr);
	if (typeof dump == 'function')
		dump (str);
	switch (type)
	{
		case 0: //LogType_Error
		case 1: //LogType_Assert
		case 4: //LogType_Exception
			console.error (str);
			return;

		case 2: //LogType_Warning
			console.warn (str);
			return;

		case 3: //LogType_Log
		case 5: //LogType_Debug
			console.log (str);
			return;			

		default:
			console.error ("Unknown console message type!")
			console.error (str);
	}
},

JS_Log_StackTrace__deps: ['$stringToUTF8', '$lengthBytesUTF8'],
JS_Log_StackTrace: function(buffer, bufferSize)
{
	var trace = stackTrace();
	if (buffer)
		stringToUTF8(trace, buffer, bufferSize);
	return lengthBytesUTF8(trace);	
}
};

mergeInto(LibraryManager.library, LibraryLoggingWebGL);

Calling unityInstance.Quit() will work after any error message prints (console.error()s).

However, if any exception is thrown and exception catching is disabled, then the game instance is in an undefined state, so unityInstance.Quit() may not work properly. (in many cases it probably will, but this is not guaranteed)

It should be possible to override JS_Log_Dump() with a custom function. The exact details come down to how Emscripten pulls in these JSLib files for linking, and it may be the case that your custom function that does the overriding may need to have a filename that comes after the filename “Logging.js” where JS_Log_Dump() appears. So you can try naming your JSLib function as zzzlogging.js or similar.

Another way is to try to use a JSPre file, and in that file, do an assignment over the original:

_JS_Log_Dump = function(ptr,type) { ... };

(note the leading underscore that is generated by Emscripten for name mangling)