Bug 1379717 - Throw TypeError when accessing caller/arguments on accessor method. r=shu
authorAndré Bargull <andre.bargull@gmail.com>
Fri, 28 Jul 2017 12:55:48 -0700
changeset 423013 3bceabbf445d41049258415796c2961a04b7ef9d
parent 423012 74d5a19b281479b5c52a9bfb9ff283bad3b74451
child 423014 820435b8583623782455b7f18e84b1a1b568e2ab
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs1379717
milestone56.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 1379717 - Throw TypeError when accessing caller/arguments on accessor method. r=shu
js/src/jsfun.cpp
js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -115,51 +115,50 @@ AdvanceToActiveCallLinear(JSContext* cx,
 static void
 ThrowTypeErrorBehavior(JSContext* cx)
 {
     JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_ERROR, GetErrorMessage, nullptr,
                                      JSMSG_THROW_TYPE_ERROR);
 }
 
 static bool
-IsFunctionInStrictMode(JSFunction* fun)
+IsSloppyNormalFunction(JSFunction* fun)
 {
-    // Interpreted functions have a strict flag.
-    if (fun->isInterpreted() && fun->strict())
-        return true;
-
-    // Only asm.js functions can also be strict.
-    return IsAsmJSStrictModeModuleOrFunction(fun);
-}
-
-static bool
-IsNewerTypeFunction(JSFunction* fun) {
-    return fun->isArrow() ||
-           fun->isStarGenerator() ||
-           fun->isLegacyGenerator() ||
-           fun->isAsync() ||
-           fun->isMethod();
+    // FunctionDeclaration or FunctionExpression in sloppy mode.
+    if (fun->kind() == JSFunction::NormalFunction) {
+        if (fun->isBuiltin() || fun->isBoundFunction())
+            return false;
+
+        if (fun->isStarGenerator() || fun->isLegacyGenerator() || fun->isAsync())
+            return false;
+
+        MOZ_ASSERT(fun->isInterpreted());
+        return !fun->strict();
+    }
+
+    // Or asm.js function in sloppy mode.
+    if (fun->kind() == JSFunction::AsmJS)
+        return !IsAsmJSStrictModeModuleOrFunction(fun);
+
+    return false;
 }
 
 // Beware: this function can be invoked on *any* function! That includes
 // natives, strict mode functions, bound functions, arrow functions,
 // self-hosted functions and constructors, asm.js functions, functions with
 // destructuring arguments and/or a rest argument, and probably a few more I
 // forgot. Turn back and save yourself while you still can. It's too late for
 // me.
 static bool
 ArgumentsRestrictions(JSContext* cx, HandleFunction fun)
 {
-    // Throw if the function is a builtin (note: this doesn't include asm.js),
-    // a strict mode function, or a bound function.
+    // Throw unless the function is a sloppy, normal function.
     // TODO (bug 1057208): ensure semantics are correct for all possible
     // pairings of callee/caller.
-    if (fun->isBuiltin() || IsFunctionInStrictMode(fun) ||
-        fun->isBoundFunction() || IsNewerTypeFunction(fun))
-    {
+    if (!IsSloppyNormalFunction(fun)) {
         ThrowTypeErrorBehavior(cx);
         return false;
     }
 
     // Otherwise emit a strict warning about |f.arguments| to discourage use of
     // this non-standard, performance-harmful feature.
     if (!JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_WARNING | JSREPORT_STRICT, GetErrorMessage,
                                            nullptr, JSMSG_DEPRECATED_USAGE, js_arguments_str))
@@ -232,23 +231,20 @@ ArgumentsSetter(JSContext* cx, unsigned 
 // natives, strict mode functions, bound functions, arrow functions,
 // self-hosted functions and constructors, asm.js functions, functions with
 // destructuring arguments and/or a rest argument, and probably a few more I
 // forgot. Turn back and save yourself while you still can. It's too late for
 // me.
 static bool
 CallerRestrictions(JSContext* cx, HandleFunction fun)
 {
-    // Throw if the function is a builtin (note: this doesn't include asm.js),
-    // a strict mode function, or a bound function.
+    // Throw unless the function is a sloppy, normal function.
     // TODO (bug 1057208): ensure semantics are correct for all possible
     // pairings of callee/caller.
-    if (fun->isBuiltin() || IsFunctionInStrictMode(fun) ||
-        fun->isBoundFunction() || IsNewerTypeFunction(fun))
-    {
+    if (!IsSloppyNormalFunction(fun)) {
         ThrowTypeErrorBehavior(cx);
         return false;
     }
 
     // Otherwise emit a strict warning about |f.caller| to discourage use of
     // this non-standard, performance-harmful feature.
     if (!JS_ReportErrorFlagsAndNumberASCII(cx, JSREPORT_WARNING | JSREPORT_STRICT, GetErrorMessage,
                                            nullptr, JSMSG_DEPRECATED_USAGE, js_caller_str))
--- a/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js
+++ b/js/src/tests/ecma_6/extensions/newer-type-functions-caller-arguments.js
@@ -5,39 +5,90 @@
 // every 'get' and 'set' of 'caller' and 'arguments'.
 // Additionally, 16.2 (Forbidden Extensions) forbids adding properties to all non-"legacy" function
 // declarations and expressions. This creates the effect that all newer-style functions act like
 // strict-mode functions when accessing their 'caller' and 'arguments' properties.
 
 const container = {
     async asyncMethod() {},
     *genMethod() {},
-    method() {}
+    method() {},
+    get getterMethod() {},
+    set setterMethod(x) {},
 };
 
-[
+function* genDecl(){}
+async function asyncDecl(){}
+class classDecl {}
+class extClassDecl extends Object {}
+class classDeclExplicitCtor { constructor(){} }
+class extClassDeclExplicitCtor extends Object { constructor(){} }
+
+const functions = [
+    // Declarations
+    genDecl,
+    asyncDecl,
+    classDecl,
+    extClassDecl,
+    classDeclExplicitCtor,
+    extClassDeclExplicitCtor,
+
+    // Expressions
     async function(){},
     function*(){},
     () => {},
     async () => {},
+    class {},
+    class extends Object {},
+    class { constructor(){} },
+    class extends Object { constructor(){} },
 
+    // Methods
     container.asyncMethod,
     container.genMethod,
-    container.method
-].forEach(f => {
+    container.method,
+    Object.getOwnPropertyDescriptor(container, "getterMethod").get,
+    Object.getOwnPropertyDescriptor(container, "setterMethod").set,
+
+    // Bound functions
+    function(){}.bind(),
+
+    // Built-ins (native and self-hosted)
+    Function,
+    Function.prototype.bind,
+];
+
+const supportsAsyncGenerator = (function() {
+    try {
+        eval("async function* f(){}");
+        return true;
+    } catch (e) {
+        return false;
+    }
+});
+
+if (supportsAsyncGenerator) {
+eval(`
+    async function* asyncGenDecl(){}
+
+    functions.push(asyncGenDecl);
+    functions.push(async function*(){});
+    functions.push({async* asyncGenerator(){}}.asyncGenerator);
+`);
+}
+
+functions.forEach(f => {
     checkArgumentsAccess(f);
     checkCallerAccess(f);
 });
 
 function checkArgumentsAccess(f) {
     assertThrowsInstanceOf(() => f.arguments, TypeError,
                            `Expected 'arguments' property access to throw on ${f}`);
 }
 
 function checkCallerAccess(f) {
     assertThrowsInstanceOf(() => f.caller, TypeError,
                            `Expected 'caller' property access to throw on ${f}`);
 }
 
 if (typeof reportCompare === "function")
-  reportCompare(true, true);
-
-print("Tests complete");
+    reportCompare(true, true);