Bug 636635 - Do not create named lambda binding for a function created by Function constructor. r=till
authorTooru Fujisawa <arai_a@mac.com>
Tue, 29 Nov 2016 09:08:43 +0900
changeset 324504 1e932a9badfac50e6dcfa4a4da395c7644cbc73a
parent 324503 c837438ca4532da5a87949f8c416017889e1cb1d
child 324505 df0caa6a80d9a13333e1bc78b5a9f1919a08ed28
push id24
push usermaklebus@msu.edu
push dateTue, 20 Dec 2016 03:11:33 +0000
reviewerstill
bugs636635
milestone53.0a1
Bug 636635 - Do not create named lambda binding for a function created by Function constructor. r=till
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/jsapi-tests/moz.build
js/src/jsapi-tests/testFunctionBinding.cpp
js/src/tests/ecma_6/Function/constructor-binding.js
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2153,38 +2153,38 @@ Parser<ParseHandler>::declareDotGenerato
     AddDeclaredNamePtr p = funScope.lookupDeclaredNameForAdd(dotGenerator);
     if (!p && !funScope.addDeclaredName(pc, p, dotGenerator, DeclarationKind::Var))
         return false;
     return true;
 }
 
 template <typename ParseHandler>
 bool
-Parser<ParseHandler>::finishFunctionScopes()
+Parser<ParseHandler>::finishFunctionScopes(bool isStandaloneFunction)
 {
     FunctionBox* funbox = pc->functionBox();
 
     if (funbox->hasParameterExprs) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->functionScope()))
             return false;
     }
 
-    if (funbox->function()->isNamedLambda()) {
+    if (funbox->function()->isNamedLambda() && !isStandaloneFunction) {
         if (!propagateFreeNamesAndMarkClosedOverBindings(pc->namedLambdaScope()))
             return false;
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<FullParseHandler>::finishFunction()
-{
-    if (!finishFunctionScopes())
+Parser<FullParseHandler>::finishFunction(bool isStandaloneFunction /* = false */)
+{
+    if (!finishFunctionScopes(isStandaloneFunction))
         return false;
 
     FunctionBox* funbox = pc->functionBox();
     bool hasParameterExprs = funbox->hasParameterExprs;
 
     if (hasParameterExprs) {
         Maybe<VarScope::Data*> bindings = newVarScopeData(pc->varScope());
         if (!bindings)
@@ -2195,36 +2195,36 @@ Parser<FullParseHandler>::finishFunction
     {
         Maybe<FunctionScope::Data*> bindings = newFunctionScopeData(pc->functionScope(),
                                                                     hasParameterExprs);
         if (!bindings)
             return false;
         funbox->functionScopeBindings().set(*bindings);
     }
 
-    if (funbox->function()->isNamedLambda()) {
+    if (funbox->function()->isNamedLambda() && !isStandaloneFunction) {
         Maybe<LexicalScope::Data*> bindings = newLexicalScopeData(pc->namedLambdaScope());
         if (!bindings)
             return false;
         funbox->namedLambdaBindings().set(*bindings);
     }
 
     return true;
 }
 
 template <>
 bool
-Parser<SyntaxParseHandler>::finishFunction()
+Parser<SyntaxParseHandler>::finishFunction(bool isStandaloneFunction /* = false */)
 {
     // The LazyScript for a lazily parsed function needs to know its set of
     // free variables and inner functions so that when it is fully parsed, we
     // can skip over any already syntax parsed inner functions and still
     // retain correct scope information.
 
-    if (!finishFunctionScopes())
+    if (!finishFunctionScopes(isStandaloneFunction))
         return false;
 
     // There are too many bindings or inner functions to be saved into the
     // LazyScript. Do a full parse.
     if (pc->closedOverBindingsForLazy().length() >= LazyScript::NumClosedOverBindingsLimit ||
         pc->innerFunctionsForLazy.length() >= LazyScript::NumInnerFunctionsLimit)
     {
         MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
@@ -2307,17 +2307,17 @@ Parser<FullParseHandler>::standaloneFunc
     ParseContext funpc(this, funbox, newDirectives);
     if (!funpc.init())
         return null();
     funpc.setIsStandaloneFunctionBody();
 
     YieldHandling yieldHandling = GetYieldHandling(generatorKind, asyncKind);
     AutoAwaitIsKeyword awaitIsKeyword(&tokenStream, asyncKind == AsyncFunction);
     if (!functionFormalParametersAndBody(InAllowed, yieldHandling, fn, Statement,
-                                         parameterListEnd))
+                                         parameterListEnd, /* isStandaloneFunction = */ true))
     {
         return null();
     }
 
     TokenKind tt;
     if (!tokenStream.getToken(&tt, TokenStream::Operand))
         return null();
     if (tt != TOK_EOF) {
@@ -3373,17 +3373,18 @@ Parser<FullParseHandler>::standaloneLazy
     return pn;
 }
 
 template <typename ParseHandler>
 bool
 Parser<ParseHandler>::functionFormalParametersAndBody(InHandling inHandling,
                                                       YieldHandling yieldHandling,
                                                       Node pn, FunctionSyntaxKind kind,
-                                                      Maybe<uint32_t> parameterListEnd /* = Nothing() */)
+                                                      Maybe<uint32_t> parameterListEnd /* = Nothing() */,
+                                                      bool isStandaloneFunction /* = false */)
 {
     // Given a properly initialized parse context, try to parse an actual
     // function without concern for conversion to strict mode, use of lazy
     // parsing and such.
 
     FunctionBox* funbox = pc->functionBox();
     RootedFunction fun(context, funbox->function());
 
@@ -3482,17 +3483,17 @@ Parser<ParseHandler>::functionFormalPara
         funbox->bufEnd = pos().end;
         if (kind == Statement && !matchOrInsertSemicolonAfterExpression())
             return false;
     }
 
     if (IsMethodDefinitionKind(kind) && pc->superScopeNeedsHomeObject())
         funbox->setNeedsHomeObject();
 
-    if (!finishFunction())
+    if (!finishFunction(isStandaloneFunction))
         return false;
 
     handler.setEndPosition(body, pos().begin);
     handler.setEndPosition(pn, pos().end);
     handler.setFunctionBody(pn, body);
 
     return true;
 }
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1067,17 +1067,18 @@ class Parser final : private JS::AutoGCR
     bool innerFunction(Node pn, ParseContext* outerpc, FunctionBox* funbox, InHandling inHandling,
                        YieldHandling yieldHandling, FunctionSyntaxKind kind,
                        Directives inheritedDirectives, Directives* newDirectives);
 
     // Parse a function's formal parameters and its body assuming its function
     // ParseContext is already on the stack.
     bool functionFormalParametersAndBody(InHandling inHandling, YieldHandling yieldHandling,
                                          Node pn, FunctionSyntaxKind kind,
-                                         mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing());
+                                         mozilla::Maybe<uint32_t> parameterListEnd = mozilla::Nothing(),
+                                         bool isStandaloneFunction = false);
 
     // Determine whether |yield| is a valid name in the current context, or
     // whether it's prohibited due to strictness, JS version, or occurrence
     // inside a star generator.
     bool yieldExpressionsSupported() {
         return (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()) && !pc->isAsync();
     }
 
@@ -1342,18 +1343,18 @@ class Parser final : private JS::AutoGCR
                        FunctionSyntaxKind kind,
                        GeneratorKind generatorKind, FunctionAsyncKind asyncKind, bool tryAnnexB,
                        Directives inheritedDirectives, Directives* newDirectives);
     bool trySyntaxParseInnerFunction(Node pn, HandleFunction fun, InHandling inHandling,
                                      YieldHandling yieldHandling, FunctionSyntaxKind kind,
                                      GeneratorKind generatorKind, FunctionAsyncKind asyncKind,
                                      bool tryAnnexB,
                                      Directives inheritedDirectives, Directives* newDirectives);
-    bool finishFunctionScopes();
-    bool finishFunction();
+    bool finishFunctionScopes(bool isStandaloneFunction);
+    bool finishFunction(bool isStandaloneFunction = false);
     bool leaveInnerFunction(ParseContext* outerpc);
 
     bool matchOrInsertSemicolonHelper(TokenStream::Modifier modifier);
     bool matchOrInsertSemicolonAfterExpression();
     bool matchOrInsertSemicolonAfterNonExpression();
 
   public:
     enum FunctionCallBehavior {
--- a/js/src/jsapi-tests/moz.build
+++ b/js/src/jsapi-tests/moz.build
@@ -32,16 +32,17 @@ UNIFIED_SOURCES += [
     'testException.cpp',
     'testExternalArrayBuffer.cpp',
     'testExternalStrings.cpp',
     'testFindSCCs.cpp',
     'testForceLexicalInitialization.cpp',
     'testForOfIterator.cpp',
     'testForwardSetProperty.cpp',
     'testFreshGlobalEvalRedefinition.cpp',
+    'testFunctionBinding.cpp',
     'testFunctionProperties.cpp',
     'testGCAllocator.cpp',
     'testGCCellPtr.cpp',
     'testGCChunkPool.cpp',
     'testGCExactRooting.cpp',
     'testGCFinalizeCallback.cpp',
     'testGCHeapPostBarriers.cpp',
     'testGCHooks.cpp',
new file mode 100644
--- /dev/null
+++ b/js/src/jsapi-tests/testFunctionBinding.cpp
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Test function name binding.
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "jsfriendapi.h"
+
+#include "jsapi-tests/tests.h"
+
+using namespace js;
+
+BEGIN_TEST(test_functionBinding)
+{
+    RootedFunction fun(cx);
+
+    JS::CompileOptions options(cx);
+    options.setFileAndLine(__FILE__, __LINE__);
+
+    // Named function shouldn't have it's binding.
+    const char s1chars[] = "return (typeof s1) == 'undefined';";
+    JS::AutoObjectVector emptyScopeChain(cx);
+    CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s1", 0, nullptr, s1chars,
+                              strlen(s1chars), &fun));
+    CHECK(fun);
+
+    JS::AutoValueVector args(cx);
+    RootedValue rval(cx);
+    CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval));
+    CHECK(rval.isBoolean());
+    CHECK(rval.toBoolean());
+
+    // Named function shouldn't have `anonymous` binding.
+    const char s2chars[] = "return (typeof anonymous) == 'undefined';";
+    CHECK(JS::CompileFunction(cx, emptyScopeChain, options, "s2", 0, nullptr, s2chars,
+                              strlen(s2chars), &fun));
+    CHECK(fun);
+
+    CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval));
+    CHECK(rval.isBoolean());
+    CHECK(rval.toBoolean());
+
+    // Anonymous function shouldn't have `anonymous` binding.
+    const char s3chars[] = "return (typeof anonymous) == 'undefined';";
+    CHECK(JS::CompileFunction(cx, emptyScopeChain, options, nullptr, 0, nullptr, s3chars,
+                              strlen(s3chars), &fun));
+    CHECK(fun);
+
+    CHECK(JS::Call(cx, UndefinedHandleValue, fun, args, &rval));
+    CHECK(rval.isBoolean());
+    CHECK(rval.toBoolean());
+
+    return true;
+}
+END_TEST(test_functionBinding)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Function/constructor-binding.js
@@ -0,0 +1,11 @@
+var BUGNUMBER = 636635;
+var summary = "A function created by Function constructor shouldn't have anonymous binding";
+
+print(BUGNUMBER + ": " + summary);
+
+assertEq(new Function("return typeof anonymous")(), "undefined");
+assertEq(new Function("return function() { return typeof anonymous; }")()(), "undefined");
+assertEq(new Function("return function() { eval(''); return typeof anonymous; }")()(), "undefined");
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);