<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[199179] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/199179">199179</a></dd>
<dt>Author</dt> <dd>sbarati@apple.com</dd>
<dt>Date</dt> <dd>2016-04-07 14:01:42 -0700 (Thu, 07 Apr 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>Initial implementation of annex b.3.3 behavior was incorrect
https://bugs.webkit.org/show_bug.cgi?id=156276

Reviewed by Keith Miller.

Source/JavaScriptCore:

I almost got annex B.3.3 correct in my first implementation.
There is a subtlety here I got wrong. We always create a local binding for
a function at the very beginning of execution of a block scope. So we
hoist function declarations to their local binding within a given
block scope. When we actually evaluate the function declaration statement
itself, we must lookup the binding in the current scope, and bind the
value to the binding in the &quot;var&quot; scope. We perform the following
abstract operations when executing a function declaration statement.

f = lookupBindingInCurrentScope(&quot;func&quot;)
store(varScope, &quot;func&quot;, f)

I got this wrong by performing the store to the var binding at the beginning
of the block scope instead of when we evaluate the function declaration statement.
This behavior is observable. For example, a program could change the value
of &quot;func&quot; before the actual function declaration statement executes.
Consider the following two functions:
```
function foo1() {
    // func === undefined
    {
        // typeof func === &quot;function&quot;
        function func() { } // Executing this statement binds the local &quot;func&quot; binding to the implicit &quot;func&quot; var binding.
        func = 20 // This sets the local &quot;func&quot; binding to 20.
    }
    // typeof func === &quot;function&quot;
}

function foo2() {
    // func === undefined
    {
        // typeof func === &quot;function&quot;
        func = 20 // This sets the local &quot;func&quot; binding to 20.
        function func() { } // Executing this statement binds the local &quot;func&quot; binding to the implicit &quot;func&quot; var binding.
    }
    // func === 20
}
```

* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::initializeBlockScopedFunctions):
(JSC::BytecodeGenerator::hoistSloppyModeFunctionIfNecessary):
* bytecompiler/BytecodeGenerator.h:
(JSC::BytecodeGenerator::emitNodeForLeftHandSide):
* bytecompiler/NodesCodegen.cpp:
(JSC::FuncDeclNode::emitBytecode):
* tests/stress/sloppy-mode-function-hoisting.js:
(test.foo):
(test):
(test.):
(test.bar):
(test.switch.case.0):
(test.capFoo1):
(test.switch.capFoo2):
(test.outer):
(foo):

LayoutTests:

* js/function-declarations-in-switch-statement-expected.txt:
* js/script-tests/function-declarations-in-switch-statement.js:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsjsfunctiondeclarationsinswitchstatementexpectedtxt">trunk/LayoutTests/js/function-declarations-in-switch-statement-expected.txt</a></li>
<li><a href="#trunkLayoutTestsjsscripttestsfunctiondeclarationsinswitchstatementjs">trunk/LayoutTests/js/script-tests/function-declarations-in-switch-statement.js</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp">trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorh">trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h</a></li>
<li><a href="#trunkSourceJavaScriptCorebytecompilerNodesCodegencpp">trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoretestsstresssloppymodefunctionhoistingjs">trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/LayoutTests/ChangeLog        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2016-04-07  Saam barati  &lt;sbarati@apple.com&gt;
+
+        Initial implementation of annex b.3.3 behavior was incorrect
+        https://bugs.webkit.org/show_bug.cgi?id=156276
+
+        Reviewed by Keith Miller.
+
+        * js/function-declarations-in-switch-statement-expected.txt:
+        * js/script-tests/function-declarations-in-switch-statement.js:
+
</ins><span class="cx"> 2016-04-06  Ada Chan  &lt;adachan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Rename TextTrackRepresentationiOS to TextTrackRepresentationCocoa and enable on Mac
</span></span></pre></div>
<a id="trunkLayoutTestsjsfunctiondeclarationsinswitchstatementexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/function-declarations-in-switch-statement-expected.txt (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/function-declarations-in-switch-statement-expected.txt        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/LayoutTests/js/function-declarations-in-switch-statement-expected.txt        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -3,7 +3,7 @@
</span><span class="cx"> WARN: shouldBe() expects string arguments
</span><span class="cx"> PASS 20 is 20
</span><span class="cx"> WARN: shouldBe() expects string arguments
</span><del>-PASS 20 is 20
</del><ins>+PASS -1 is -1
</ins><span class="cx"> PASS successfullyParsed is true
</span><span class="cx"> 
</span><span class="cx"> TEST COMPLETE
</span></span></pre></div>
<a id="trunkLayoutTestsjsscripttestsfunctiondeclarationsinswitchstatementjs"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/js/script-tests/function-declarations-in-switch-statement.js (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/js/script-tests/function-declarations-in-switch-statement.js        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/LayoutTests/js/script-tests/function-declarations-in-switch-statement.js        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -21,4 +21,4 @@
</span><span class="cx"> 
</span><span class="cx"> shouldBe(t(1), '20');
</span><span class="cx"> shouldBe(t(2), '20');
</span><del>-shouldBe(t(3), '20');
</del><ins>+shouldBe(t(3), '-1');
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/Source/JavaScriptCore/ChangeLog        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -1,3 +1,67 @@
</span><ins>+2016-04-07  Saam barati  &lt;sbarati@apple.com&gt;
+
+        Initial implementation of annex b.3.3 behavior was incorrect
+        https://bugs.webkit.org/show_bug.cgi?id=156276
+
+        Reviewed by Keith Miller.
+
+        I almost got annex B.3.3 correct in my first implementation.
+        There is a subtlety here I got wrong. We always create a local binding for
+        a function at the very beginning of execution of a block scope. So we
+        hoist function declarations to their local binding within a given
+        block scope. When we actually evaluate the function declaration statement
+        itself, we must lookup the binding in the current scope, and bind the
+        value to the binding in the &quot;var&quot; scope. We perform the following
+        abstract operations when executing a function declaration statement.
+
+        f = lookupBindingInCurrentScope(&quot;func&quot;)
+        store(varScope, &quot;func&quot;, f)
+
+        I got this wrong by performing the store to the var binding at the beginning
+        of the block scope instead of when we evaluate the function declaration statement.
+        This behavior is observable. For example, a program could change the value
+        of &quot;func&quot; before the actual function declaration statement executes.
+        Consider the following two functions:
+        ```
+        function foo1() {
+            // func === undefined
+            {
+                // typeof func === &quot;function&quot;
+                function func() { } // Executing this statement binds the local &quot;func&quot; binding to the implicit &quot;func&quot; var binding.
+                func = 20 // This sets the local &quot;func&quot; binding to 20.
+            }
+            // typeof func === &quot;function&quot;
+        }
+
+        function foo2() {
+            // func === undefined
+            {
+                // typeof func === &quot;function&quot;
+                func = 20 // This sets the local &quot;func&quot; binding to 20.
+                function func() { } // Executing this statement binds the local &quot;func&quot; binding to the implicit &quot;func&quot; var binding.
+            }
+            // func === 20
+        }
+        ```
+
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::initializeBlockScopedFunctions):
+        (JSC::BytecodeGenerator::hoistSloppyModeFunctionIfNecessary):
+        * bytecompiler/BytecodeGenerator.h:
+        (JSC::BytecodeGenerator::emitNodeForLeftHandSide):
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::FuncDeclNode::emitBytecode):
+        * tests/stress/sloppy-mode-function-hoisting.js:
+        (test.foo):
+        (test):
+        (test.):
+        (test.bar):
+        (test.switch.case.0):
+        (test.capFoo1):
+        (test.switch.capFoo2):
+        (test.outer):
+        (foo):
+
</ins><span class="cx"> 2016-04-07  Alex Christensen  &lt;achristensen@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         Build fix after r199170
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -1934,18 +1934,30 @@
</span><span class="cx">         emitNewFunctionExpressionCommon(temp.get(), function);
</span><span class="cx">         bool isLexicallyScoped = true;
</span><span class="cx">         emitPutToScope(scope, variableForLocalEntry(name, entry, symbolTableIndex, isLexicallyScoped), temp.get(), DoNotThrowIfNotFound, Initialization);
</span><ins>+    }
+}
</ins><span class="cx"> 
</span><del>-        if (iter-&gt;value.isSloppyModeHoistingCandidate() &amp;&amp; m_scopeNode-&gt;hasSloppyModeHoistedFunction(name.impl())) {
-            ASSERT(m_varScopeSymbolTableIndex);
-            ASSERT(*m_varScopeSymbolTableIndex &lt; m_symbolTableStack.size());
-            SymbolTableStackEntry&amp; varScope = m_symbolTableStack[*m_varScopeSymbolTableIndex];
-            SymbolTable* varSymbolTable = varScope.m_symbolTable.get();
-            RELEASE_ASSERT(varSymbolTable-&gt;scopeType() == SymbolTable::ScopeType::VarScope);
-            SymbolTableEntry entry = varSymbolTable-&gt;get(name.impl());
-            RELEASE_ASSERT(!entry.isNull());
-            bool isLexicallyScoped = false;
-            emitPutToScope(varScope.m_scope, variableForLocalEntry(name, entry, varScope.m_symbolTableConstantIndex, isLexicallyScoped), temp.get(), DoNotThrowIfNotFound, Initialization);
</del><ins>+void BytecodeGenerator::hoistSloppyModeFunctionIfNecessary(const Identifier&amp; functionName)
+{
+    if (m_scopeNode-&gt;hasSloppyModeHoistedFunction(functionName.impl())) {
+        Variable currentFunctionVariable = variable(functionName);
+        RefPtr&lt;RegisterID&gt; currentValue;
+        if (RegisterID* local = currentFunctionVariable.local())
+            currentValue = local;
+        else {
+            RefPtr&lt;RegisterID&gt; scope = emitResolveScope(nullptr, currentFunctionVariable);
+            currentValue = emitGetFromScope(newTemporary(), scope.get(), currentFunctionVariable, DoNotThrowIfNotFound);
</ins><span class="cx">         }
</span><ins>+        
+        ASSERT(m_varScopeSymbolTableIndex);
+        ASSERT(*m_varScopeSymbolTableIndex &lt; m_symbolTableStack.size());
+        SymbolTableStackEntry&amp; varScope = m_symbolTableStack[*m_varScopeSymbolTableIndex];
+        SymbolTable* varSymbolTable = varScope.m_symbolTable.get();
+        ASSERT(varSymbolTable-&gt;scopeType() == SymbolTable::ScopeType::VarScope);
+        SymbolTableEntry entry = varSymbolTable-&gt;get(functionName.impl());
+        ASSERT(!entry.isNull());
+        bool isLexicallyScoped = false;
+        emitPutToScope(varScope.m_scope, variableForLocalEntry(functionName, entry, varScope.m_symbolTableConstantIndex, isLexicallyScoped), currentValue.get(), DoNotThrowIfNotFound, NotInitialization);
</ins><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerBytecodeGeneratorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -478,6 +478,8 @@
</span><span class="cx">             return emitNode(n);
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        void hoistSloppyModeFunctionIfNecessary(const Identifier&amp; functionName);
+
</ins><span class="cx">     private:
</span><span class="cx">         void emitTypeProfilerExpressionInfo(const JSTextPosition&amp; startDivot, const JSTextPosition&amp; endDivot);
</span><span class="cx">     public:
</span></span></pre></div>
<a id="trunkSourceJavaScriptCorebytecompilerNodesCodegencpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -3173,8 +3173,9 @@
</span><span class="cx"> 
</span><span class="cx"> // ------------------------------ FuncDeclNode ---------------------------------
</span><span class="cx"> 
</span><del>-void FuncDeclNode::emitBytecode(BytecodeGenerator&amp;, RegisterID*)
</del><ins>+void FuncDeclNode::emitBytecode(BytecodeGenerator&amp; generator, RegisterID*)
</ins><span class="cx"> {
</span><ins>+    generator.hoistSloppyModeFunctionIfNecessary(metadata()-&gt;ident());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> // ------------------------------ FuncExprNode ---------------------------------
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoretestsstresssloppymodefunctionhoistingjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js (199178 => 199179)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js        2016-04-07 21:00:40 UTC (rev 199178)
+++ trunk/Source/JavaScriptCore/tests/stress/sloppy-mode-function-hoisting.js        2016-04-07 21:01:42 UTC (rev 199179)
</span><span class="lines">@@ -222,10 +222,21 @@
</span><span class="cx"> test(function() {
</span><span class="cx">     assert(foo === undefined);
</span><span class="cx">     while (truthy()) {
</span><ins>+        assert(foo() === 20);
</ins><span class="cx">         break;
</span><span class="cx"> 
</span><span class="cx">         function foo() { return 20; }
</span><span class="cx">     }
</span><ins>+    assert(foo === undefined);
+});
+
+test(function() {
+    assert(foo === undefined);
+    while (truthy()) {
+        assert(foo() === 20);
+        function foo() { return 20; }
+        break;
+    }
</ins><span class="cx">     assert(foo() === 20);
</span><span class="cx"> });
</span><span class="cx"> 
</span><span class="lines">@@ -238,6 +249,18 @@
</span><span class="cx"> 
</span><span class="cx">         function foo() { return 20; }
</span><span class="cx">     }
</span><ins>+    assert(foo === undefined);
+    assert(bar() === undefined);
+});
+
+test(function() {
+    function bar() { return foo; }
+    assert(foo === undefined);
+    assert(bar() === undefined);
+    while (truthy()) {
+        function foo() { return 20; }
+        break;
+    }
</ins><span class="cx">     assert(foo() === 20);
</span><span class="cx">     assert(bar()() === 20);
</span><span class="cx"> });
</span><span class="lines">@@ -280,7 +303,22 @@
</span><span class="cx">     case 0:
</span><span class="cx">         function foo() { return 20; }
</span><span class="cx">         break;
</span><ins>+    case 1:
+        assert(foo() === 20);
+        break;
</ins><span class="cx">     }
</span><ins>+    assert(foo === undefined);
+});
+
+test(function() {
+    assert(foo === undefined);
+    switch(1) {
+    case 1:
+        assert(foo() === 20);
+    case 0:
+        function foo() { return 20; }
+        break;
+    }
</ins><span class="cx">     assert(foo() === 20);
</span><span class="cx"> });
</span><span class="cx"> 
</span><span class="lines">@@ -351,6 +389,39 @@
</span><span class="cx">         break;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    assert(foo === undefined);
+});
+
+test(function() {
+    function capFoo1() { return foo; }
+    assert(foo === undefined);
+    assert(capFoo1() === undefined);
+    switch(1) {
+    case 0:
+        function foo() { return bar; }
+        function capFoo2() { return foo; }
+    case 1:
+        let bar = 20;
+        assert(foo() === 20);
+        assert(capFoo1() === undefined);
+        assert(capFoo2() === foo);
+        assert(capFoo2()() === 20);
+        break;
+    }
+
+    assert(foo === undefined);
+});
+
+test(function() {
+    assert(foo === undefined);
+    switch(1) {
+    case 1:
+        let bar = 20;
+        assert(foo() === 20);
+    case 0:
+        function foo() { return bar; }
+    }
+
</ins><span class="cx">     assert(foo() === 20);
</span><span class="cx"> });
</span><span class="cx"> 
</span><span class="lines">@@ -622,6 +693,23 @@
</span><span class="cx">     assert(typeof z === &quot;function&quot;);
</span><span class="cx"> });
</span><span class="cx"> 
</span><ins>+test(function() {
+    function outer() { return f; }
+    assert(outer() === undefined);
+    {
+        assert(outer() === undefined);
+        assert(f() === 2);
+        f = 100
+        assert(outer() === undefined);
+        function f() { return 1 }
+        assert(outer() === 100);
+        f = 200
+        assert(outer() === 100); // 100
+        function f() { return 2 }
+        assert(outer() === 200);
+    }
+});
+
</ins><span class="cx"> for (let i = 0; i &lt; 500; i++)
</span><span class="cx">     assert(foo() === 25);
</span><span class="cx"> function foo() { return 20; }
</span></span></pre>
</div>
</div>

</body>
</html>