Real World Func Decl in Block Scope Breakages

# Brian Terlson (12 years ago)

I have some data on patterns and sites that may break due to the proposed change to semantics of function decls in block scope. I am not advocating for any changes here but merely dumping some data I've gathered. I will continue gathering data about this breaking change and potentially others (eg. let[x] = 1), so any further data you folks are interested in let me know. I think the January meeting would be a good venue to discuss any of this in detail if warranted.

On December 17th, 2235 sites were crawled and their scripts downloaded. These scripts were then processed in an attempt to identify likely breakages due to the change to the semantics of func decls in block scope. In this dataset, 4% of the scripts contained a function declaration in block scope (mostly inside if and try, although pretty much every node contains a function somewhere in this dataset). However, most of these scripts use the function within the same block and so won't be broken. 20 sites, however, will likely be broken by this change in some way. There is also a chance that the tool used to identify breakages has missed some code that will breka.

Below are some examples of code on the web today that will be broken. For each I include a snippet of code that is heavily edited in an attempt to convey the pattern used and the developer intent. I also attempt to identify what functionality will actually be broken.

Most of the breakages occur in non-library code, with two exceptions: qTip 1.0, and thickbox 3.

ninemsn.com.au

RenderModal = function () {

if (x) { // is an array of shortcuts that can be added. Is statically non-empty.

    function K() {

        // process the array of shortcuts in some way

    }

}

K();

};

Scenario

  1.   Navigate to main page.
    
  2.   Click "Add other shortcuts"
    
  3.   Click "View more shortcuts" - this will not work.
    

yandex.ru

if (_ycssjs("BaH0Fmmo2Sg24lRmTPrK0B8qpaA")) {

function cp(g, c, d) { /* ...*/ }



function csh_ifgsid(c, b) { /*...*/ } }

// ... thousands of lines of code ...

if (_ycssjs("rO+QIoSf2L0NwDn6vjJjy+27nxI")) {

function news() {

    csh_if_gsid();

}

}

if (_ycssjs("YRPF0QVjJmhRiKRu6cvi3YXqYo8")) {

// ... bunch of stuff ...

cp();

}

Scenario

Unknown

g.espncdn.com/nfl-primetime-payoff/en/module/entry?matchupid=478

if (isIE && isWin) {

// ...

} else {

function JSGetSwfVer(i){

    // ...

}

}

function checkFlash(myRev) {

// ...



if (isIE && isWin) {

    // ...

} else {

    aV = JSGetSwfVer(rV);

}

}

Scenario

Can't find a page which uses this code.

www.t-online.de

if (!Adition_Environment) {

var Adition_Environment = (function () {

    var _this = {};

    // ...

    _this.getPrf = function (cuId) {

        var prf = "";

        try {

            prf = Adition_Prfstr(cuId);

        } catch (e) { }

        return prf;

    };

    // ...

})();

}

// snip 1k lines

if (typeof Adition_Prfstr == "undefined") {

function Adition_Prfstr(ADITION_CONTENTUNIT_ID) {

    // ...

}

}

Scenario

Required to display advertisements on the page.

manormystery.com

if (!window._ate) {

window._ate = { /* ... */ };

function addthis_open() { /* ... */ } } else { _ate.inst++; }

if (_atc.abf) {

addthis_open(document.getElementById("ab"), "emailab", window.addthis_url || "[URL]", window.addthis_title || "[TITLE]"); }

Scenario

Social sharing popups broken at least. This may be a general utility used in a number of places.

manhunt.net (NSFW, DO NOT VISIT AT WORK)

/** @todo isFlashInstalled() should probably be gotten by include from js/cmmn/flashDetect.js or upfunc.js. */

if (typeof isFlashInstalled == 'undefined') {

function isFlashInstalled() {

    var requiredMajorVersion = 6; // Major version of Flash required

    var requiredMinorVersion = 0;   // Minor version of Flash required

    var requiredRevision = 0;   // Minor version of Flash required

    var s = new SWFObject();

    if (!s) return false;

    var version = s.installedVer;

    if (!version) return false;

    return (version.major >= requiredMajorVersion && version.minor >= requiredMinorVersion && version.rev >= requiredRevision);

}

}

isFlashInstalled();

Scenario

I avoided this site at work for obvious reasons, but it looks like all login functionality will be broken.

www.163.com

NTESAD_Couplet.prototype = {

NTESCreate: function () {



    var widthsmall = this.options.widthsmall;

    var width = this.options.width;



    if (this.options.leftbig != "" && this.options.leftsmall != "") {

        function bighide() {

            coupletLeft.style.display = "none";

            coupletLeftsmall.style.display = "block";

        }



        function smallhide() {

            coupletLeftsmall.style.display = "none";

            coupletLeft.style.display = "block";

        }

    }



    if (this.options.rightbig != "" && this.options.rightsmall != "") {

        function Rbighide() {

            coupletRight.style.display = "none";

            coupletRightsmall.style.display = "block";

        }

    }



    if (this.NTESPosition()) {

        if (this.options.leftbig != "" && this.options.leftsmall != "" && this.options.rightbig != "" && this.options.rightsmall != "") {

            this.NTESBind(coupletclose, "click", function () { bighide(); Rbighide(); coupletRightsmall.style.display = "none"; coupletLeftsmall.style.display = "none"; });

            this.NTESBind(coupletclose2, "click", function () { bighide(); Rbighide(); coupletRightsmall.style.display = "none"; coupletLeftsmall.style.display = "none"; });

        }

    }

}

}

Scenario

Unknown.

www22.verizon.com

if (floatStyle.match(/Content/)) {

var contentDiv = d.getElementById(_b.Preferences.Render.main_content_id || "content");

if (contentDiv == null) {

    floatStyle = this.Preferences.Render.float_style = 'fixed'

}

function getRightOfContent() {

    return contentDiv.offsetWidth + contentDiv.offsetLeft + 1

}

function fixBackground() {

    if (_b.Preferences.Render.fix_background) {

        db.style.backgroundAttachment = "scroll";

        var margin = null;

        if (document.defaultView && document.defaultView.getComputedStyle) {

            margin = parseInt(document.defaultView.getComputedStyle(contentDiv, null).getPropertyValue("margin-left"), 10)

        } else {

            margin = parseInt(contentDiv.currentStyle.marginLeft, 10)

        }

        if (isNaN(margin) || margin == 0) {

            margin = contentDiv.offsetLeft || 0

        }

        db.style.backgroundPosition = (Math.floor(contentDiv.scrollWidth * -0.5) - 2 + margin) + 'px 0'

    }

}

}

// getRightOfContent/fixBackground used repeatedly after this.

Scenario

Couldn't find a scenario where this code was hit, but the site is expansive. Also, the above code is found in at least 2 different scripts across the site.

www.jeuxvideo.com

if (typeof getAppNexusMegaTag == 'undefined' || typeof getAppNexusMegaTag != 'function') {

function getSize1080667() {

    return '250x250';

}

}

if (typeof inFIF != "undefined" && inFIF == true) {

try {

    parent.getSize1080667 = getSize1080667;

} catch (e) { }

}

Scenario

Ad won't display.

msn.foxsports.com

/* this function is much faster, so if possible we use it. Some IEs are the only ones I know of that need the idiotic second function, generated by an if clause. */ function add32(a, b) {

return (a + b) & 0xFFFFFFFF;

}

if (md5('hello') != '5d41402abc4b2a76b9719d911017c592') {

function add32(x, y) {

    var lsw = (x & 0xFFFF) + (y & 0xFFFF),

        msw = (x >> 16) + (y >> 16) + (lsw >> 16);

    return (msw << 16) | (lsw & 0xFFFF);

}

}

Scenario

Nothing is necessary broken about this, as long as the first add32 functions properly (today everyone with this code is using the second function). This is a fairly common MD5 library (do a search for "idiotic second function" to get an idea, however this was only found once in the dataset).

iwiw.hu/i/belepes

(function () {

if (jQuery.fn.lazyload) {

    function a() {

        return typeof f !== "undefined" ? f : (f = jQuery("body").hasClass("lazyloadimages"))

    }

}

jQuery(function () {

    if (a()) {

        d.apply(document.body)

    }

})

})();

Scenario

Unknown

www.laposte.net

if (!window.$extend) {

function $extend(c, a) {

    for (var b in (a || {})) {

        c[b] = a[b]

    } return c

}

}

// $extend used repeatedly after this

Scenario

Unknown

atpworldtour.com

/*

  • Stores hash values for localization.

  • Will need duplicating for each language.

*/

if (typeof registerNS == "undefined") {

function registerNS(ns) {

    var nsParts = ns.split(".");

    var root = window;

    for (var i = 0; i < nsParts.length; i++) {

        if (typeof root[nsParts[i]] == "undefined")

            root[nsParts[i]] = new Object();

        root = root[nsParts[i]];

    }

}

}

registerNS("atp.utilities");

// called repeatedly after this.

Scenario

Entire site is unusable.

atwiki.jp

if (typeof AddClipsFlag == "undefined") {

var AddClipsFlag = 'addclips';

// ...

function AddClipsLoad() { /* ... */ }

// ...

}

AddClipsLoad();

Scenario

RSS functionality broken. This appears to be a library of some kind (see www.addclips.orgwww.addclips.org).

Library: qTip 1.0: Craga89/qTip1/blob/master/1.0.0-rc3/jquery.qtip-1.0.0-rc3.js as seen on Zoompanel.com

if (t.options.hide.when.event == "inactive") {

function y(z) {



}

} else {

// ...

}

function x(z) {

if (t.options.hide.when.event == "inactive") {

    y()

}

}

Scenario

Used on zoompanel.com. Tooltips will never disappear after showing up.

Library: thickbox 3 (thickbox.net), in use on runescape.com and baixaki.com.br if (!(TB_PrevHTML === "")) {

function goPrev(){

}

$("#TB_prev").click(goPrev);

}

if (!(TB_NextHTML === "")) {

function goNext(){

}

$("#TB_next").click(goNext);

}

document.onkeydown = function (e) {

if (keycode == 190) { // display previous image

    if (!(TB_NextHTML == "")) {

        document.onkeydown = "";

        goNext();

    }

} else if (keycode == 188) { // display next image

    if (!(TB_PrevHTML == "")) {

        document.onkeydown = "";

        goPrev();

    }

}

}

Scenario

Going back/forward with keyboard is broken.

kankan.com

Snippet

if (typeof getCookie == 'undefined') {

function getCookie(name) {



}

}

// ...

if (getCookie('tpar')) {

var irStartTime = new Date().getTime();

window.attachEvent('onunload', irTimeStat); }

Scenario

Unknown, possibly just for tracking purposes.

Google +1 library (only seen on TMZ.com, possibly this lib is out of date) try {

function h(a) {

    throw a;

}

// plus a ton of other decls

} catch (e) { }

try {

h(Error("foo"));

// plus a ton of calls to other block scoped functions } catch (e) { }

Scenario

Plus one button is broken.

# David Herman (12 years ago)

Thanks for the data.

My understanding of the consensus was that block-local functions would only have the new semantics within strict code (which includes module code), so there would be no breakage of existing content.

That said, if we felt the breakage was acceptable, in the long run it'd be preferable to have consistent semantics of block-local functions everywhere. Data like this certainly helps to make more informed decision about that.

# Allen Wirfs-Brock (12 years ago)

On Dec 26, 2012, at 2:02 PM, David Herman wrote:

Hi Brian,

Thanks for the data.

My understanding of the consensus was that block-local functions would only have the new semantics within strict code (which includes module code), so there would be no breakage of existing content.

That isn't my understanding. Rather, it was that we would support block level function declarations in all code. This was based upon the premise that the interoperable intersection of the currently implemented semantics for block level function declarations is very small. Essentially only, uses of this form are currently interoperable among all major implementations:

if (condition) { function foo() {}; foo(); //function declared and invoked in same conditional blocks. }

Other uses (without other explicit feature or browser detection logic being involved) are not likely to be interoperable.

I haven't looked in detail at all of Brian's snippets. But several that I have glanced at look to me like they wouldn't be interoperable among current browsers.

Alen

# Brandon Benvie (12 years ago)

This appears to be correct. A number of the examples can be boiled down to the following test:

if (false) {
    function x(){
      console.log('worked');
    }
}
x();

In Chrome, IE, Opera, and Safari 'worked' will be logged. In Firefox it will silently fail.

# David Herman (12 years ago)

Perhaps, but the mobile web is WebKitty. The ES6 semantics is incompatible with Chrome and Safari's behavior here.

# Brian Terlson (12 years ago)

Most of the snippets fall into either column 5 or 10 in the output table on this site[1] (which is down, hence cached copy, hopefully it works for you guys). These are mostly interoperable across browser.

  1. cc.bingj.com/cache.aspx?q=new-es5-strict-mode-requirement-function-statements-not-at-top-level-of-a-program-or-function-are-prohibited&d=4643002220544120&mkt=en-US&setlang=en-US&w=U93XPP_KaQ0_ZPk7dkr1Ij8kiT-SHMWI

From: es-discuss-bounces at mozilla.org [mailto:es-discuss-bounces at mozilla.org] On Behalf Of Brandon Benvie Sent: Wednesday, December 26, 2012 2:38 PM To: Allen Wirfs-Brock Cc: es-discuss at mozilla.org Subject: Re: Real World Func Decl in Block Scope Breakages

This appears to be correct. A number of the examples can be boiled down to the following test:

if (false) {         function x(){           console.log('worked');         }     }     x();

In Chrome, IE, Opera, and Safari 'worked' will be logged. In Firefox it will silently fail.

On Wed, Dec 26, 2012 at 5:22 PM, Allen Wirfs-Brock <allen at wirfs-brock.com> wrote:

Essentially only,  uses of this form are currently interoperable among all major implementations:

if (condition) {      function foo() {};      foo();   //function declared and invoked in same conditional blocks. }

Other uses (without other explicit feature or browser detection logic being involved) are not likely to be interoperable.

I haven't looked in detail at all of Brian's snippets.  But several that I have glanced at look to me like they wouldn't be interoperable among current browsers.

# Brandon Benvie (12 years ago)

Unless I'm mistaken on how Spidermonkey handles function declarations in block scope, almost none of those examples will work correctly in Firefox. Most of them will work in every other major browser, but not in Firefox. The question of whether the sites are actually broken in Firefox will mostly come down to whether the given code runs in Firefox or not.

# Brendan Eich (12 years ago)

Brian Terlson wrote:

20 sites, however, will likely be broken by this change in some way. There is also a chance that the tool used to identify breakages has missed some code that will breka.

Below are some examples of code on the web today that will be broken. For each I include a snippet of code that is heavily edited in an attempt to convey the pattern used and the developer intent. I also attempt to identify what functionality will actually be broken.

Thanks for the snippets. Some questions interspersed below.

Most of the breakages occur in non-library code, with two exceptions: qTip 1.0, and thickbox 3.

ninemsn.com.au

RenderModal = function () {

if (x) { // is an array of shortcuts that can be added. Is statically non-empty.

function K() {

// process the array of shortcuts in some way

}

}

K();

};

Since x evaluates as truthy no matter what (even an empty array is an object, therefore truthy), this is equivalent to

RenderModal = function () {

function K() {

// process the array of shortcuts in some way

}

K();

};

but of course it lies outside of ES6's function-in-block semantics, yet still in the intersection of Firefox and IE/IE-emulating-browsers.

yandex.ru

if (_ycssjs("BaH0Fmmo2Sg24lRmTPrK0B8qpaA")) {

function cp(g, c, d) { /* ...*/ }

function csh_ifgsid(c, b) { /.../ } }

// ... thousands of lines of code ...

This "then" block presumably closes here, before:

if (_ycssjs("rO+QIoSf2L0NwDn6vjJjy+27nxI")) {

function news() {

csh_if_gsid();

}

}

if (_ycssjs("YRPF0QVjJmhRiKRu6cvi3YXqYo8")) {

// ... bunch of stuff ...

cp();

}

Somehow the "if" conditions are logically connected such that no calls to an undefined identifier occur! Blech.

Scenario

Unknown

Indeed! Cc'ing Chaals in case he can shed light.

g.espncdn.com/nfl-primetime-payoff/en/module/entry?matchupid=478

if (isIE && isWin) {

// ...

} else {

function JSGetSwfVer(i){

// ...

}

}

function checkFlash(myRev) {

// ...

if (isIE && isWin) {

// ...

} else {

aV = JSGetSwfVer(rV);

}

}

Scenario

Can’t find a page which uses this code.

Can you say how many of the 20 cases involve actual live code using the function-in-pre-ES6-block code?

www.t-online.de

if (!Adition_Environment) {

var Adition_Environment = (function () {

var _this = {};

// ...

_this.getPrf = function (cuId) {

var prf = "";

try {

prf = Adition_Prfstr(cuId);

} catch (e) { }

return prf;

};

// ...

})();

}

// snip 1k lines

if (typeof Adition_Prfstr == "undefined") {

function Adition_Prfstr(ADITION_CONTENTUNIT_ID) {

// ...

}

}

Scenario

Required to display advertisements on the page.

Heinous.

manormystery.com

if (!window._ate) {

window._ate = { /* ... */ };

function addthis_open() { /* ... */ } } else { _ate.inst++; }

Did you snip the closing brace for the "then" block left open, the one for "if (!window._ate)"?

if (_atc.abf) {

addthis_open(document.getElementById("ab"), "emailab", window.addthis_url || "[URL]", window.addthis_title || "[TITLE]"); }

Scenario

Social sharing popups broken at least. This may be a general utility used in a number of places.

On the other hand, the snippet seems like a mis-coding of

if (!window._ate) {

window._ate = { /* ... */ };

}

function addthis_open() { /* ... */ } } else { _ate.inst++; }

if (_atc.abf) {

addthis_open(document.getElementById("ab"), "emailab", window.addthis_url || "[URL]", window.addthis_title || "[TITLE]"); }

... }

I'd try to evangelize the owner of this code.

manhunt.net (NSFW, DO NOT VISIT AT WORK)

/** @todo isFlashInstalled() should probably be gotten by include from js/cmmn/flashDetect.js or upfunc.js. */

if (typeof isFlashInstalled == 'undefined') {

function isFlashInstalled() {

var requiredMajorVersion = 6; // Major version of Flash required

var requiredMinorVersion = 0; // Minor version of Flash required

var requiredRevision = 0; // Minor version of Flash required

var s = new SWFObject();

if (!s) return false;

var version = s.installedVer;

if (!version) return false;

return (version.major >= requiredMajorVersion && version.minor >= requiredMinorVersion && version.rev >= requiredRevision);

}

}

isFlashInstalled();

Here is the simplest example of a pre-ES6 Firefox/IE+clones intersection: function-in-block with hoisting to top of outer function or global scope and SpiderMonkey's assignment-like runtime binding both work the same, provided the use(s) of the function's name all occur after any such assignment (in the SpiderMonkey in Firefox scenario).

There's no control flow relation in general, e.g. dominance. However in this case the "if (typeof isFlashInstalled == 'undefined')" head could, to a sufficiently smart CFG analyzer, imply that isFlashInstalled() post-dominates some kind of "definition" of that name.

Ok, enough so far to say a few things:

  1. We have time and (among the bigs involved in ES6) try evangelization. Evangelization can be crowd-sourced too, and can be more effective when done that way.

  2. We have some terrible choices, even if ES6's function-in-block semantics require "use strict". That leaves the above mess a de-facto standard which has never been specified. Specifying the sloppy-mode intersection is going to be ugly and a time-sink.

  3. The carrot of function-in-block may be sweet -- we have evidence in all the good sites your crawl found that are not the insane 20 -- but whether "use strict"; required before all such ES6 function-in-block uses would be a stick remains to be investigated. Did you check for "use strict" or actual enabled-in-IE10 strict mode among the sites you found that declare functions in blocks?

# Brian Terlson (12 years ago)

Responses inline.

-----Original Message----- From: Brendan Eich [mailto:brendan at mozilla.com] Sent: Wednesday, December 26, 2012 3:27 PM To: Brian Terlson Cc: es-discuss at mozilla.org; Charles McCathie Nevile Subject: Re: Real World Func Decl in Block Scope Breakages

Brian Terlson wrote:

20 sites, however, will likely be broken by this change in some way. There is also a chance that the tool used to identify breakages has missed some code that will breka.

Below are some examples of code on the web today that will be broken. For each I include a snippet of code that is heavily edited in an attempt to convey the pattern used and the developer intent. I also attempt to identify what functionality will actually be broken.

Thanks for the snippets. Some questions interspersed below.

Most of the breakages occur in non-library code, with two exceptions: qTip 1.0, and thickbox 3.

ninemsn.com.au

RenderModal = function () {

if (x) { // is an array of shortcuts that can be added. Is statically non-empty.

function K() {

// process the array of shortcuts in some way

}

}

K();

};

Since x evaluates as truthy no matter what (even an empty array is an object, therefore truthy), this is equivalent to

RenderModal = function () {

function K() {

// process the array of shortcuts in some way

}

K();

};

but of course it lies outside of ES6's function-in-block semantics, yet still in the intersection of Firefox and IE/IE-emulating-browsers.

yandex.ru

if (_ycssjs("BaH0Fmmo2Sg24lRmTPrK0B8qpaA")) {

function cp(g, c, d) { /* ...*/ }

function csh_ifgsid(c, b) { /.../ } }

// ... thousands of lines of code ...

This "then" block presumably closes here, before:

if (_ycssjs("rO+QIoSf2L0NwDn6vjJjy+27nxI")) {

function news() {

csh_if_gsid();

}

}

if (_ycssjs("YRPF0QVjJmhRiKRu6cvi3YXqYo8")) {

// ... bunch of stuff ...

cp();

}

Somehow the "if" conditions are logically connected such that no calls to an undefined identifier occur! Blech.

Scenario

Unknown

Indeed! Cc'ing Chaals in case he can shed light.

g.espncdn.com/nfl-primetime- payoff/en/module/entry?matchupid=47 8

if (isIE && isWin) {

// ...

} else {

function JSGetSwfVer(i){

// ...

}

}

function checkFlash(myRev) {

// ...

if (isIE && isWin) {

// ...

} else {

aV = JSGetSwfVer(rV);

}

}

Scenario

Can't find a page which uses this code.

Can you say how many of the 20 cases involve actual live code using the function-in-pre-ES6-block code?

Just going by the snippets that have a known scenario rather than unknown, looks like 10/20. For the others, I do not know for sure whether the snippet is dead code or not. If you have any suggestions for how I could determine this more easily than heavily using the site under instrumentation I can look into collecting that data before the Jan meeting.

www.t-online.de

if (!Adition_Environment) {

var Adition_Environment = (function () {

var _this = {};

// ...

_this.getPrf = function (cuId) {

var prf = "";

try {

prf = Adition_Prfstr(cuId);

} catch (e) { }

return prf;

};

// ...

})();

}

// snip 1k lines

if (typeof Adition_Prfstr == "undefined") {

function Adition_Prfstr(ADITION_CONTENTUNIT_ID) {

// ...

}

}

Scenario

Required to display advertisements on the page.

Heinous.

manormystery.com

if (!window._ate) {

window._ate = { /* ... */ };

function addthis_open() { /* ... */ } } else { _ate.inst++; }

Did you snip the closing brace for the "then" block left open, the one for "if (!window._ate)"?

if (_atc.abf) {

addthis_open(document.getElementById("ab"), "emailab", window.addthis_url || "[URL]", window.addthis_title || "[TITLE]"); }

Scenario

Social sharing popups broken at least. This may be a general utility used in a number of places.

On the other hand, the snippet seems like a mis-coding of

if (!window._ate) {

window._ate = { /* ... */ };

}

function addthis_open() { /* ... */ } } else { _ate.inst++; }

if (_atc.abf) {

addthis_open(document.getElementById("ab"), "emailab", window.addthis_url || "[URL]", window.addthis_title || "[TITLE]"); }

... }

I'd try to evangelize the owner of this code.

manhunt.net (NSFW, DO NOT VISIT AT WORK)

/** @todo isFlashInstalled() should probably be gotten by include from js/cmmn/flashDetect.js or upfunc.js. */

if (typeof isFlashInstalled == 'undefined') {

function isFlashInstalled() {

var requiredMajorVersion = 6; // Major version of Flash required

var requiredMinorVersion = 0; // Minor version of Flash required

var requiredRevision = 0; // Minor version of Flash required

var s = new SWFObject();

if (!s) return false;

var version = s.installedVer;

if (!version) return false;

return (version.major >= requiredMajorVersion && version.minor >= requiredMinorVersion && version.rev >= requiredRevision);

}

}

isFlashInstalled();

Here is the simplest example of a pre-ES6 Firefox/IE+clones intersection: function-in-block with hoisting to top of outer function or global scope and SpiderMonkey's assignment-like runtime binding both work the same, provided the use(s) of the function's name all occur after any such assignment (in the SpiderMonkey in Firefox scenario).

There's no control flow relation in general, e.g. dominance. However in this case the "if (typeof isFlashInstalled == 'undefined')" head could, to a sufficiently smart CFG analyzer, imply that isFlashInstalled() post-dominates some kind of "definition" of that name.

Ok, enough so far to say a few things:

  1. We have time and (among the bigs involved in ES6) try evangelization. Evangelization can be crowd-sourced too, and can be more effective when done that way.

  2. We have some terrible choices, even if ES6's function-in-block semantics require "use strict". That leaves the above mess a de-facto standard which has never been specified. Specifying the sloppy-mode intersection is going to be ugly and a time-sink.

  3. The carrot of function-in-block may be sweet -- we have evidence in all the good sites your crawl found that are not the insane 20 -- but whether "use strict"; required before all such ES6 function-in-block uses would be a stick remains to be investigated. Did you check for "use strict" or actual enabled- in-IE10 strict mode among the sites you found that declare functions in blocks?

Programmers do seem to want func decls in block scope a lot more than I expected, although I wouldn't necessarily call all non-breaking instances of it I found "sane" (eg. the decls I found in while/for statements...), just less insane :)

I didn't check for "use strict". I will work on capturing this data at least in time for the Jan meeting.

As far as IE doc modes, 15/19 are in standards mode (Zoompanel.com, Kankan.com, 163.com, and Verizon.com run in some compat mode). Didn't check the NSFW site.

# Brian Terlson (12 years ago)

Sorry, missed one question in there:

-----Original Message----- From: es-discuss-bounces at mozilla.org [mailto:es-discuss- bounces at mozilla.org] On Behalf Of Brian Terlson Sent: Wednesday, December 26, 2012 4:13 PM To: Brendan Eich Cc: Charles McCathie Nevile; es-discuss at mozilla.org Subject: RE: Real World Func Decl in Block Scope Breakages

Hi Brendan,

Responses inline.

-----Original Message----- From: Brendan Eich [mailto:brendan at mozilla.com] Sent: Wednesday, December 26, 2012 3:27 PM To: Brian Terlson Cc: es-discuss at mozilla.org; Charles McCathie Nevile Subject: Re: Real World Func Decl in Block Scope Breakages

Brian Terlson wrote:

20 sites, however, will likely be broken by this change in some way. There is also a chance that the tool used to identify breakages has missed some code that will breka.

Below are some examples of code on the web today that will be broken. For each I include a snippet of code that is heavily edited in an attempt to convey the pattern used and the developer intent. I also attempt to identify what functionality will actually be broken.

Thanks for the snippets. Some questions interspersed below.

Most of the breakages occur in non-library code, with two exceptions: qTip 1.0, and thickbox 3.

ninemsn.com.au

RenderModal = function () {

if (x) { // is an array of shortcuts that can be added. Is statically non-empty.

function K() {

// process the array of shortcuts in some way

}

}

K();

};

Since x evaluates as truthy no matter what (even an empty array is an object, therefore truthy), this is equivalent to

RenderModal = function () {

function K() {

// process the array of shortcuts in some way

}

K();

};

but of course it lies outside of ES6's function-in-block semantics, yet still in the intersection of Firefox and IE/IE-emulating-browsers.

yandex.ru

if (_ycssjs("BaH0Fmmo2Sg24lRmTPrK0B8qpaA")) {

function cp(g, c, d) { /* ...*/ }

function csh_ifgsid(c, b) { /.../ } }

// ... thousands of lines of code ...

This "then" block presumably closes here, before:

if (_ycssjs("rO+QIoSf2L0NwDn6vjJjy+27nxI")) {

function news() {

csh_if_gsid();

}

}

if (_ycssjs("YRPF0QVjJmhRiKRu6cvi3YXqYo8")) {

// ... bunch of stuff ...

cp();

}

Somehow the "if" conditions are logically connected such that no calls to an undefined identifier occur! Blech.

Scenario

Unknown

Indeed! Cc'ing Chaals in case he can shed light.

g.espncdn.com/nfl-primetime- payoff/en/module/entry?matchupid=47 8

if (isIE && isWin) {

// ...

} else {

function JSGetSwfVer(i){

// ...

}

}

function checkFlash(myRev) {

// ...

if (isIE && isWin) {

// ...

} else {

aV = JSGetSwfVer(rV);

}

}

Scenario

Can't find a page which uses this code.

Can you say how many of the 20 cases involve actual live code using the function-in-pre-ES6-block code?

Just going by the snippets that have a known scenario rather than unknown, looks like 10/20. For the others, I do not know for sure whether the snippet is dead code or not. If you have any suggestions for how I could determine this more easily than heavily using the site under instrumentation I can look into collecting that data before the Jan meeting.

www.t-online.de

if (!Adition_Environment) {

var Adition_Environment = (function () {

var _this = {};

// ...

_this.getPrf = function (cuId) {

var prf = "";

try {

prf = Adition_Prfstr(cuId);

} catch (e) { }

return prf;

};

// ...

})();

}

// snip 1k lines

if (typeof Adition_Prfstr == "undefined") {

function Adition_Prfstr(ADITION_CONTENTUNIT_ID) {

// ...

}

}

Scenario

Required to display advertisements on the page.

Heinous.

manormystery.com

if (!window._ate) {

window._ate = { /* ... */ };

function addthis_open() { /* ... */ } } else { _ate.inst++; }

Did you snip the closing brace for the "then" block left open, the one for "if (!window._ate)"?

The formatting is really bad due to a botched HTML-->plaintext conversion but it's there after the addthis_open decl.

# Brendan Eich (12 years ago)

Brandon Benvie wrote:

This appears to be correct. A number of the examples can be boiled down to the following test:

if (false) {

No, the working examples in Brian's snippets rather test an always-true condition. That means they work in Firefox too.

# Brendan Eich (12 years ago)

Brandon Benvie wrote:

Unless I'm mistaken on how Spidermonkey handles function declarations in block scope, almost none of those examples will work correctly in Firefox. Most of them will work in every other major browser, but not in Firefox.

No, that's not so. The ancient (pre-ES3) SpiderMonkey function-in-block semantics are like an assignment expression in effect, so if control flow reaches the nested function declaration, the var-like binding will be created and initialized. If control flow does not reach one of N possible for a given name, then the var-like binding won't even exist.

Crazy but true.

The snippets Brian posted all look like they'll work in SpiderMonkey / Firefox, at a glance from me. Let me know if I missed something.

# Brandon Benvie (12 years ago)

My tests basically indicate what you said, but with a minor distinction. They don't fail outright, as in throw an error. They just fail silently. As in, it's ok to call the identifier, but it's not going to do anything. This is a curious Schrodinger's cat kind of intermediate state, where it doesn't succeed in doing what it was asked, but doesn't throw an error either, which in JS terms means it air-quotes succeeded but didn't really.

# Brendan Eich (12 years ago)

Brandon Benvie wrote:

My tests basically indicate what you said, but with a minor distinction. They don't fail outright, as in throw an error. They just fail silently. As in, it's ok to call the identifier, but it's not going to do anything.

No, something else is wrong. Can you show such a (reduced, I take it) testcase?

This is a curious Schrodinger's cat kind of intermediate state, where it doesn't succeed in doing what it was asked, but doesn't throw an error either, which in JS terms means it air-quotes succeeded but didn't really.

SpiderMonkey's function-in-block does not create any such magic error-free behavior. I don't know what you're seeing, but it's not what I saw at a glance, and not what SpiderMonkey implements for f-i-b.

# Brandon Benvie (12 years ago)

if I run this code in the tools > web developer > web console, I get a

result of undefined and no error if (false) { function x(){ console.log('worked') } } x();

# Brendan Eich (12 years ago)

[[bcc'ing es-discuss since this is a tangent.]

Well, bcc'ing didn't work -- mailman does not respect. Resending for posterity.

/be]

Brandon Benvie wrote:

if I run this code in the tools > web developer > web console, I get a result of undefined and no error if (false) { function x(){ console.log('worked') } } x();

Best to test other ways than consoles, they do odd things in some browsers. But what you see is what I see when I load the content wrapped in script tags from /tmp/foo.html as well:

<script> if (false) { function x(){ console.log('worked') } } x() </script>

So Firefox is eating the error just as the console is. Not so a SpiderMonkey shell:

js> if (false) { function x(){ console.log('worked') } } x()

typein:1:53 ReferenceError: x is not defined

Let's try onerror:

<script> this.onerror = function(s) { alert('onerror: ' + s); } if (false) { function x(){ console.log('worked') } } x() </script>

Not sure what's up with the non-error, but the REPL shows a ReferenceError as expected. Could you please file a bug cc'ing me, dmandelin at mozilla.com, and :bz? Thanks.

# Brendan Eich (12 years ago)

Brendan Eich wrote:

Brandon Benvie wrote:

Unless I'm mistaken on how Spidermonkey handles function declarations in block scope, almost none of those examples will work correctly in Firefox. Most of them will work in every other major browser, but not in Firefox.

No, that's not so. The ancient (pre-ES3) SpiderMonkey function-in-block semantics are like an assignment expression in effect, so if control flow reaches the nested function declaration, the var-like binding will be created and initialized.

Astute students of ECMA-262 such as yourself probably see the contradiction above: "assignment" != "definition". Indeed I started with assignment ages ago but the code evolved to conditionally define (not run global setter for same-named property for function-in-block-in-global-code).

Anyway, the major point remains: the definition (originally, assignment) happens only if control flow reaches the nested declaration.

# Brendan Eich (12 years ago)

Brendan Eich wrote:

  1. We have time and (among the bigs involved in ES6)

"money"

try evangelization. Evangelization can be crowd-sourced too, and can be more effective when done that way.

Left out a word there ahem.

# Brendan Eich (12 years ago)

Boris Zbarsky mailed that this is fixed in Firefox Nightly, Brandon was in on that email, but I just wanted to confirm that I was testing Firefox 17.0.1 and it swallowed the ReferenceError, but my fresh test in Firefox Nightly logs the ReferenceError as expected (alerts with the onerror mod I show below).

All's well that ends well, but I'd still like to be cc'ed on that bug.

# David Bruant (12 years ago)

Le 27/12/2012 03:07, Brandon Benvie a écrit :

if I run this code in the tools > web developer > web console, I get a result of undefined and no error if (false) { function x(){ console.log('worked') } } x();

I see an error on Firefox Aurora 19 WebConsole on Linux. I also see it when I put the code between <script>.

I don't know if it's related with your case, but last I checked, the Firefox WebConsole had its own global (different from the content global). Something to keep in mind when playing with subtle ECMAScript things (like here, declaring a global function).

# Brendan Eich (12 years ago)

Charles McCathie Nevile wrote:

On Thu, 27 Dec 2012 00:26:48 +0100, Brendan Eich <brendan at mozilla.com> wrote:

Brian Terlson wrote:

20 sites, however, will likely be broken by this change in some way.

I am guessing this is the key point. But there is a scope error, and "this" is undefined. It seems you're trying to find out why we are one of the few sites that do something, because you'd like to change something in ES that would break what we are doing, right? I am not sure exactly what the something or the change are, and that makes it hard for me to find the person who knows the answer (yes, folks, I confess that I didn't write this particular bit of code and haven't even carefully deconstructed it in my head.... ;) ).

So, any more clues? (No list-archive header to follow :( I can of course search, but someone here might be able to give a better reply more efficiently).

Sure, and sorry for lack of context.

The issue is that

function foo() { return 42; }

and

var foo = function() { return 42; }

and variations on the latter function expression syntax are all standardized. However,

if (cond) { function bar() { return 43; } } console.log(bar());

is not standardized. This syntax is not produced the any ECMA-262 standard grammar, but it is supported by all major implementations, with varying semantics. Call it function-in-block (f-i-b for short).

In IE and browsers that reverse-engineered IE JScript's implementation of f-i-b, bar is hoisted whether cond evaluates to truthy or falsy, so the console.log(bar()) always works.

In SpiderMonkey in Firefox and other apps, only if cond evaluates to truthy will bar be defined, leaving the bar() call within the console.log(bar()) expression possibly failing due to bar not being found in the scope chain, or resolving to an outer bar that might not be callable.

There is also a chance that the tool used to identify breakages has missed some code that will breka.

Below are some examples of code on the web today that will be broken. For each I include a snippet of code that is heavily edited in an attempt to convey the pattern used and the developer intent. I also attempt to identify what functionality will actually be broken.

What is the proposed change?

ES6 proposes that f-i-b always bind bar in the nearest enclosing curly-braced block, hoisted in the manner of function in JS today but to block scope, not function or program scope, so that the function can be used in expressions evaluated before control flows to evaluate the declaration of bar.

This means the example can fail if !cond. Only within the "then" block (anywhere, thanks to hoisting) could one safely use bar to mean the function declared within that block.

Clearly we had an incompatible change in mind. The fallback if we can't get away with this compat-break is to have ES6's f-i-b semantics only under "use strict" and have the old mess (browser-dependent) in non-strict ("sloppy") mode code.

Any help you can give on the one of 20 hard cases Brian found will be gratefully received.

# Brendan Eich (12 years ago)

Brendan Eich wrote:

What is the proposed change?

ES6 proposes that f-i-b always bind bar in the nearest enclosing curly-braced block, hoisted in the manner of function in JS today but to block scope, not function or program scope, so that the function can be used in expressions evaluated before control flows to evaluate the declaration of bar.

This means the example can fail if !cond.

Argh, meant to write "even if (cond)" -- i.e. the example will fail because the call to bar() after then if-then cannot possibly call the inner bar defined in the "then" bllock, in the ES6-proposed semantics.

Only within the "then" block (anywhere, thanks to hoisting) could one safely use bar to mean the function declared within that block.

I got this sentence right :-|.

HTH,