<html>
    <head>
      <base href="https://bugs.webkit.org/" />
    </head>
    <body>
      <p>
        <div>
            <b><a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - [ES6]. Implement Annex B.3.3 function hoisting rules for eval"
   href="https://bugs.webkit.org/show_bug.cgi?id=163208#c22">Comment # 22</a>
              on <a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - [ES6]. Implement Annex B.3.3 function hoisting rules for eval"
   href="https://bugs.webkit.org/show_bug.cgi?id=163208">bug 163208</a>
              from <span class="vcard"><a class="email" href="mailto:sbarati&#64;apple.com" title="Saam Barati &lt;sbarati&#64;apple.com&gt;"> <span class="fn">Saam Barati</span></a>
</span></b>
        <pre>Comment on <span class=""><a href="attachment.cgi?id=297742&amp;action=diff" name="attach_297742" title="Patch">attachment 297742</a> <a href="attachment.cgi?id=297742&amp;action=edit" title="Patch">[details]</a></span>
Patch

View in context: <a href="https://bugs.webkit.org/attachment.cgi?id=297742&amp;action=review">https://bugs.webkit.org/attachment.cgi?id=297742&amp;action=review</a>

This code mostly LGTM, some comments below.

Also, it's worth opening a bugzilla for a bug that has existed since I implemented the first version of sloppy hoisting:
function foo() {
    {
         let bar;
         {
              function bar() { }
          }
    }
    return bar;
}
in jsc: foo() === function bar() { }
expected behavior: foo(); throws a ReferenceError
I believe your eval code exhibits the same bad behavior for local lexical variables in the eval.

<span class="quote">&gt; Source/JavaScriptCore/bytecode/CodeBlock.cpp:2822
&gt; +        // Fixme: <a class="bz_bug_link 
          bz_status_NEW "
   title="NEW - Add llint optimization for op_resolve_closest_var_scope and op_is_scope_var_type op codes"
   href="show_bug.cgi?id=166418">https://bugs.webkit.org/show_bug.cgi?id=166418</a></span >

Style: &quot;Fixme&quot; =&gt; &quot;FIXME&quot;

<span class="quote">&gt; Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:2186
&gt; +RegisterID* BytecodeGenerator::emitResolveOuterScope(RegisterID* dst, const Identifier&amp; property)</span >

Nit: Lets call this: &quot;emitResolveOuterVarScope&quot;

<span class="quote">&gt; Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp:2198
&gt; +RegisterID* BytecodeGenerator::emitCheckIfScopeVarType(RegisterID* dst, RegisterID* scope)</span >

Nit: Lets call this: &quot;emitCheckIfScopeIsVarType&quot;

<span class="quote">&gt; Source/JavaScriptCore/jit/JITPropertyAccess.cpp:829
&gt; +void JIT::emitSlow_op_is_scope_var_type(Instruction* currentInstruction, Vector&lt;SlowCaseEntry&gt;::iterator&amp; iter)
&gt; +{
&gt; +    linkSlowCase(iter);
&gt; +    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_is_scope_var_type);
&gt; +    slowPathCall.call();
&gt; +}
&gt; +
&gt; +void JIT::emitSlow_op_resolve_closest_var_scope(Instruction* currentInstruction, Vector&lt;SlowCaseEntry&gt;::iterator&amp; iter)
&gt; +{
&gt; +    linkSlowCase(iter);
&gt; +    JITSlowPathCall slowPathCall(this, currentInstruction, slow_path_resolve_closest_var_scope);
&gt; +    slowPathCall.call();
&gt; +}</span >

These shouldn't be slow paths. The opcodes should just do the call on the fast patch, and should not have slow paths.

<span class="quote">&gt; Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm:2124
&gt; +_llint_op_resolve_closest_var_scope:
&gt; +    traceExecution()
&gt; +    callOpcodeSlowPath(_slow_path_resolve_closest_var_scope)
&gt; +    dispatch(4)
&gt; +
&gt; +_llint_op_is_scope_var_type:
&gt; +    traceExecution()
&gt; +    callOpcodeSlowPath(_slow_path_is_scope_var_type)
&gt; +    dispatch(3)</span >

The 32 and 64 implementations are the same, so they can just go inside LowLevelInterpreter.asm

<span class="quote">&gt; Source/JavaScriptCore/parser/Parser.h:639
&gt; +                    addResult.iterator-&gt;value.setIsFunction();</span >

Why is this change needed?

<span class="quote">&gt; Source/JavaScriptCore/runtime/CommonSlowPaths.cpp:834
&gt; +    bool result = false;
&gt; +    if (scope-&gt;isGlobalObject())
&gt; +        result = true;
&gt; +    else if (SymbolTable* scopeSymbolTable = scope-&gt;symbolTable())
&gt; +        result = scopeSymbolTable-&gt;scopeType() == SymbolTable::ScopeType::VarScope;</span >

This doesn't match the code inside DFGOperations. Which code is correct?

<span class="quote">&gt; Source/JavaScriptCore/runtime/JSScope.cpp:234
&gt; +                if (object-&gt;hasProperty(exec, ident))
&gt; +                    return object;</span >

I think you need to check an exception here.

<span class="quote">&gt; Source/JavaScriptCore/runtime/JSScope.cpp:242
&gt; +        if (object-&gt;hasProperty(exec, ident)) {</span >

ditto

<span class="quote">&gt; JSTests/stress/eval-func-decl-within-eval-with-reassign-to-var.js:14
&gt; +function foo() {
&gt; +    {
&gt; +        var f = 20;
&gt; +        eval('var f = 15; eval(&quot; { function f() { }; } &quot;)');
&gt; +        assert(typeof f, &quot;function&quot;);
&gt; +    }
&gt; +    assert(typeof f, &quot;function&quot;, &quot;#1&quot;);
&gt; +}</span >

Another good test to have:
function foo() {
    eval(&quot;if (false) { function bar() { } }&quot;);
    assert(bar === undefined);
}

<span class="quote">&gt; JSTests/stress/eval-func-decl-within-eval-with-reassign-to-var.js:56
&gt; +/* //TODO: Set error 
&gt; +// Fixme: Current test does not work because it should raise exception that f could 
&gt; +// not be redeclared
&gt; +function goo() {
&gt; +    {   
&gt; +        var error = false;
&gt; +        try {
&gt; +            let f = 20;
&gt; +            eval('var f = 15; eval(&quot; { function f() { }; } &quot;)');
&gt; +        } catch (e) {
&gt; +            error = e instanceof SyntaxError;
&gt; +        }
&gt; +        assert(error, true, &quot;syntax error should be raisen&quot;);
&gt; +    }
&gt; +    assert(typeof f, &quot;undefined&quot;, &quot;#6&quot;);
&gt; +}
&gt; +
&gt; +for (var i = 0; i &lt; 10000; i++) {
&gt; +    goo();
&gt; +    assert(typeof f, &quot;undefined&quot;, &quot;#7&quot;);
&gt; +}
&gt; +*/</span >

What's happening with this?

<span class="quote">&gt; JSTests/stress/eval-func-decl-within-eval-with-reassign-to-var.js:65
&gt; +function hoo() {
&gt; +    {
&gt; +        let h = 20;
&gt; +        eval('var h = 15; eval(&quot; if (false){ function h() { }; } &quot;);');
&gt; +        assert(h, 15);
&gt; +    }
&gt; +    assert(typeof h, &quot;undefined&quot;, &quot;#8&quot;);
&gt; +}</span >

Another good test:
function foo() { let h = 20; eval(&quot;var h; if (false) { function h() { } }&quot;);  return h; }
assert(foo() === 20);</pre>
        </div>
      </p>
      <hr>
      <span>You are receiving this mail because:</span>
      
      <ul>
          <li>You are the assignee for the bug.</li>
      </ul>
    </body>
</html>