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 324471 1e932a9badfac50e6dcfa4a4da395c7644cbc73a
parent 324470 c837438ca4532da5a87949f8c416017889e1cb1d
child 324472 df0caa6a80d9a13333e1bc78b5a9f1919a08ed28
push id31006
push usercbook@mozilla.com
push dateTue, 29 Nov 2016 10:40:01 +0000
treeherdermozilla-central@f8107cf96144 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs636635
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);