Daniel K. Daniel K. - 3 months ago 6
Javascript Question

Re-assigning a function declaration in javascript

Right now I am debugging some weird errors which occur in my GWT (version 2.5.1) application since Firefox 46. The javascript code generated by GWT contains multiple instances of this pattern:

function nullMethod() {

var v;
function f() {
f = nullMethod;
v = { name : 'Joe' };

// this is called from multiple places
console.log((f(), v).name);
console.log((f(), v).name);
console.log((f(), v).name);

Seems that this somehow implements the singleton pattern. But for some reason this does not prevent that the initial declared method is invoked again. The string 'called' is printed to the console multiple times.

But if I try to reproduce this with a little test everything works as expected. The observations from above was made by adding console outputs to the generated code (>5MB).

Now the really weird stuff. If I add "console.log(f.toString())" as the first statement of the function, it prints the intial function on the first invocation. All further invocations print the nullMethod.

Can somebody explain what could be the cause? IE11 works fine. Chrome has the same issue as Firefox 46. I just didn't find old Chrome versions to verify since when this behaviour was introduced. Is it valid to overwrite a function declared this way?

jsbin.com gives a warning on the function re-assignment line:

Line 6: 'f' is a function.


The method f in this case is a Java static initializer. Running it more than once will cause problems in the emulated Java, which expects that this can only run a single time when the class is first referenced or loaded (ignoring classloader issues, since GWT doesnt emulate classloaders).

However, sufficiently old copies of GWT wrap up blocks of code in a try/catch block (iirc this was related to IE6 issues), and at the time, JS scope rules had some ambiguity around them which made this code work consistently, since all browsers supported this. This was known as a "sloppy mode self-defining function".

As part of ES2015, it has been decided that the try block has a different scope than the outer "function-hosted" one (i.e. when you declare something as a function foo() {...}, it exists in a high-level scope). See a ton of discussion on this at https://github.com/tc39/ecma262/issues/162.

This change means that some formerly sane programs no longer work correctly - for a time, this included Google Inbox as well as others.

Updating to a newer version of GWT, or using a custom Linker that no longer wraps each block of top-level JS in old GWT should resolve this. If you still must support whatever ancient browser that requires this behavior (with enough code archeology, I'm sure we could find out why GWT initially did it, but I haven't done this yet), you are going to have to find a compromise that works in both ancient and bleeding edge browsers.