Bug 1254185 - Deal with missing arguments assigned to block bindings. (r=jimb)
authorShu-yu Guo <shu@rfrn.org>
Fri, 18 Mar 2016 15:07:26 -0700
changeset 289419 574e8c5132b9ced6db7395b9f85440f43649555f
parent 289418 e627a445ff01f28fb49c32f2e28e91c034420574
child 289420 ed7f0e024ce8122abe08d98eba42587e9b349777
push id73835
push usershu@rfrn.org
push dateFri, 18 Mar 2016 22:04:59 +0000
treeherdermozilla-inbound@3a6988962137 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1254185
milestone48.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 1254185 - Deal with missing arguments assigned to block bindings. (r=jimb)
js/src/jit-test/tests/debug/Frame-eval-25.js
js/src/jsobj.h
js/src/vm/ScopeObject-inl.h
js/src/vm/ScopeObject.cpp
--- a/js/src/jit-test/tests/debug/Frame-eval-25.js
+++ b/js/src/jit-test/tests/debug/Frame-eval-25.js
@@ -1,17 +1,25 @@
-// |jit-test| error: TypeError
-//
 // Make sure we can recover missing arguments even when it gets assigned to
 // another slot.
 
+load(libdir + "asserts.js");
 load(libdir + "evalInFrame.js");
 
 function h() {
   evalInFrame(1, "a.push(0)");
 }
 
 function f() {
   var a = arguments;
   h();
 }
 
-f();
+assertThrowsInstanceOf(f, TypeError);
+
+function g() {
+  {
+    let a = arguments;
+    h();
+  }
+}
+
+assertThrowsInstanceOf(g, TypeError);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -448,17 +448,17 @@ class JSObject : public js::gc::Cell
      * above it before the global object is reached.
      */
 
     /*
      * Get the enclosing scope of an object. When called on non-scope object,
      * this will just be the global (the name "enclosing scope" still applies
      * in this situation because non-scope objects can be on the scope chain).
      */
-    inline JSObject* enclosingScope();
+    inline JSObject* enclosingScope() const;
 
     inline js::GlobalObject& global() const;
     inline bool isOwnGlobal() const;
 
     /*
      * ES5 meta-object properties and operations.
      */
 
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -235,17 +235,17 @@ StaticScopeIter<allowGC>::module() const
 {
     MOZ_ASSERT(type() == Module);
     return obj->template as<ModuleObject>();
 }
 
 }  /* namespace js */
 
 inline JSObject*
-JSObject::enclosingScope()
+JSObject::enclosingScope() const
 {
     if (is<js::ScopeObject>())
         return &as<js::ScopeObject>().enclosingScope();
 
     if (is<js::DebugScopeObject>())
         return &as<js::DebugScopeObject>().enclosingScope();
 
     if (is<js::GlobalObject>())
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1943,19 +1943,48 @@ class DebugScopeProxy : public BaseProxy
      * where evalInFrame(N, str) means to evaluate str N frames up.
      *
      * In this case we don't know we need to recover a missing arguments
      * object until after we've performed the property get.
      */
     static bool isMagicMissingArgumentsValue(JSContext* cx, ScopeObject& scope, HandleValue v)
     {
         bool isMagic = v.isMagic() && v.whyMagic() == JS_OPTIMIZED_ARGUMENTS;
-        MOZ_ASSERT_IF(isMagic,
-                      isFunctionScope(scope) &&
-                      scope.as<CallObject>().callee().nonLazyScript()->argumentsHasVarBinding());
+
+#ifdef DEBUG
+        // The |scope| object here is not limited to CallObjects but may also
+        // be block scopes in case of the following:
+        //
+        //   function f() { { let a = arguments; } }
+        //
+        // We need to check that |scope|'s static scope's nearest function
+        // scope has an 'arguments' var binding. The dynamic scope chain is
+        // not sufficient: |f| above will not have a CallObject because there
+        // are no aliased body-level bindings.
+        if (isMagic) {
+            JSFunction* callee = nullptr;
+            if (isFunctionScope(scope)) {
+                callee = &scope.as<CallObject>().callee();
+            } else {
+                // We will never have a DynamicWithObject here because no
+                // binding accesses on with scopes are unaliased.
+                for (StaticScopeIter<NoGC> ssi(&scope.as<ClonedBlockObject>().staticBlock());
+                     !ssi.done();
+                     ssi++)
+                {
+                    if (ssi.type() == StaticScopeIter<NoGC>::Function) {
+                        callee = &ssi.fun();
+                        break;
+                    }
+                }
+            }
+            MOZ_ASSERT(callee && callee->nonLazyScript()->argumentsHasVarBinding());
+        }
+#endif
+
         return isMagic;
     }
 
     /*
      * Create a missing arguments object. If the function returns true but
      * argsObj is null, it means the scope is dead.
      */
     static bool createMissingArguments(JSContext* cx, ScopeObject& scope,