FMOD js doesn't respect strict mode

I’ve started using FMOD recently to implement it into my game, but I found that the file fmodstudio.js for the WASM implementation doesn’t respect strict mode, and overall has a bunch of weird and inconsistent things in it.

The biggest issue I ran into was its liberal use of undefined variables that are not global variables, here is the full list:

“GL”,
“SDL”,
“retValue”,
“infocontext”,
“inforate”,
“mInputRegistered”,
“context”,
“read”,
“readbuffer”,
“scriptArgs”,
“quit”,
“printErr”,
“FMOD_JS_MixFunction”,
“_as_script_node”,
“OutputWebAudio_resumeAudio”,
“_as_output_buffer”,
“mWorkletNode”,
“mModulePolling”,
“mModuleLoading”,
“mStartInterval”,
“mStopInterval”,
“mSuspendInterval”,
“mResumeInterval”,
“mWorkletNodeConnected”,
“mContext”,
“mAddModuleRef”,
“FMOD_JS_MixerSlowpathFunction”,
“FMOD_JS_MixerFastpathFunction”,
“mSpeakerChannelCount”,
“mUrl”,
“mOutputData”,
“contextForCheck”,
“mSharedArrayBuffers”,
“OutputAudioWorklet_resumeAudio”,
“waitForAudioWorklet”,
“readline”,
“FinalizationGroup”,
“dateNow”,
“MozBlobBuilder”,
“WebKitBlobBuilder”,
“Fibers”,

Some of these are propertly guarded, usually with a typeof condition like so:

if (typeof quit === “function”) {
// code
}

but some other variables are just used plainly in sloppy mode as local variables, such as:

function() {
contextForCheck = new (window.AudioContext ||
window.webkitAudioContext)();
if (!contextForCheck) {
return 0;
}
retValue = 0;
if (self.AudioWorkletNode) {
if (contextForCheck.audioWorklet.addModule) {
retValue = 1;
}
}
contextForCheck.close();
null;
return retValue;
}

this function uses 2 variables without defining them and while this would work in sloppy mode due to the web’s backwards compatibility, it seems to break on some modern browsers sometimes.

This same code does something else that’s weird:

contextForCheck = new (window.AudioContext || window.webkitAudioContext)();

it “executes” the constructor. I think this also only works due to backwards compatibility but shouldn’t be done on modern browsers.

Another example where the file does something weird is here:

function UTF8ArrayToString(heap, idx, maxBytesToRead) {
var endIdx = idx + maxBytesToRead;
var endPtr = idx;
while (heap[endPtr] && !(endPtr >= endIdx)) ++endPtr;
if (endPtr - idx > 16 && heap.subarray && UTF8Decoder) {
return UTF8Decoder.decode(heap.subarray(idx, endPtr));
} else {
var str = “”;
while (idx < endPtr) {
var u0 = heap[idx++];
if (!(u0 & 128)) {
str += String.fromCharCode(u0);
continue;
}
var u1 = heap[idx++] & 63;
if ((u0 & 224) == 192) {
str += String.fromCharCode(((u0 & 31) << 6) | u1);
continue;
}
var u2 = heap[idx++] & 63;
if ((u0 & 240) == 224) {
u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
} else {
u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heap[idx++] & 63);
}
if (u0 < 65536) {
str += String.fromCharCode(u0);
} else {
var ch = u0 - 65536;
str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023));
}
}
}
return str;
}

This function defines the variable “str” inside the else branch, but returns it outside of the else branch

This works for 2 reasons:
1- var used to define the variables on the current scope, and from what I can tell, conditional and loop statements do not define a “new” scope so the variable keeps existing until the end of the function

2- the first if has a return statement in it, and so the final return statement will never get executed without running through else first.

Regardless of that, it’s very confusing and uses ancient weird properties of the var keyword. Either put the return statement inside the else, define the var outside the if-else or remove the else keyword entirely since the if already returns, so anything below it counts as a de-facto else.

Sorry if this post reads like an unwarranted code review, it’s just that I ran into random and unconsistant crashes and I investigated the code and thought I’d report weird occurrences since most of these should be fairly simple fixes.

Here is a pastebin with my “fixed” version that I ended up using: let undefVars = [ "GL", "SDL", "retValue", "infocontext", "infora - Pastebin.com
(another reason I’m reporting these is because I’d rather not have to worry about FMOD breaking for no reason when I inevitably update to a new version in the future)

Thanks for the detailed post, I’ve passed this along to the development team for further investigation.

Hi, just checking up on this.

Has this been fixed in any more recent version, or should I keep my version for now?

This was fixed in 2.02.17.

1 Like