Bug 1175394 part 1 - Create an unmapped (strict) arguments object for functions with rest/destructuring/default arguments. r=jorendorff
authorJan de Mooij <jdemooij@mozilla.com>
Wed, 02 Sep 2015 12:57:10 +0200
changeset 260558 cd0f55213a1400376b0d21a0a849bf27aef26780
parent 260557 45f2da1082dd8b981d4f742725bdd889bfd42517
child 260559 8c61215957220573b0051a2194b4e19dcad1e195
push id17287
push userryanvm@gmail.com
push dateWed, 02 Sep 2015 18:54:33 +0000
treeherderb2g-inbound@7ca8e465cbd2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs1175394
milestone43.0a1
Bug 1175394 part 1 - Create an unmapped (strict) arguments object for functions with rest/destructuring/default arguments. r=jorendorff
js/src/frontend/Parser.cpp
js/src/frontend/SharedContext.h
js/src/jit-test/tests/arguments/mapped-unmapped-args.js
js/src/jit-test/tests/arguments/rest-with-arguments.js
js/src/jit-test/tests/arrow-functions/arguments-4.js
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CompileInfo.h
js/src/jit/IonAnalysis.cpp
js/src/jit/IonBuilder.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/ArgumentsObject.cpp
js/src/vm/Xdr.h
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1076,22 +1076,23 @@ Parser<FullParseHandler>::checkFunctionA
          * eagerly. The debugger can walk the scope chain and observe any
          * values along it.
          */
         if (pc->sc->hasDebuggerStatement())
             funbox->setDefinitelyNeedsArgsObj();
 
         /*
          * Check whether any parameters have been assigned within this
-         * function. In strict mode parameters do not alias arguments[i], and
-         * to make the arguments object reflect initial parameter values prior
-         * to any mutation we create it eagerly whenever parameters are (or
-         * might, in the case of calls to eval) be assigned.
+         * function. If the arguments object is unmapped (strict mode or
+         * function with default/rest/destructing args), parameters do not alias
+         * arguments[i], and to make the arguments object reflect initial
+         * parameter values prior to any mutation we create it eagerly whenever
+         * parameters are (or might, in the case of calls to eval) assigned.
          */
-        if (pc->sc->needStrictChecks()) {
+        if (!funbox->hasMappedArgsObj()) {
             for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) {
                 DefinitionList& dlist = r.front().value();
                 for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) {
                     Definition* dn = dr.front<FullParseHandler>();
                     if (dn->kind() == Definition::ARG && dn->isAssigned())
                         funbox->setDefinitelyNeedsArgsObj();
                 }
             }
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -235,27 +235,27 @@ class SharedContext
 
     void setExplicitUseStrict()           { anyCxFlags.hasExplicitUseStrict        = true; }
     void setBindingsAccessedDynamically() { anyCxFlags.bindingsAccessedDynamically = true; }
     void setHasDebuggerStatement()        { anyCxFlags.hasDebuggerStatement        = true; }
     void setHasDirectEval()               { anyCxFlags.hasDirectEval               = true; }
 
     inline bool allLocalsAliased();
 
-    bool strict() {
+    bool strict() const {
         return strictScript || localStrict;
     }
     bool setLocalStrictMode(bool strict) {
         bool retVal = localStrict;
         localStrict = strict;
         return retVal;
     }
 
     // JSOPTION_EXTRA_WARNINGS warnings or strict mode errors.
-    bool needStrictChecks() {
+    bool needStrictChecks() const {
         return strict() || extraWarnings;
     }
 
     bool isDotVariable(JSAtom* atom) const {
         return atom == context->names().dotGenerator || atom == context->names().dotGenRVal;
     }
 };
 
@@ -342,16 +342,20 @@ class FunctionBox : public ObjectBox, pu
                                              funCxFlags.needsHomeObject          = true; }
     void setDerivedClassConstructor()      { MOZ_ASSERT(function()->isClassConstructor());
                                              funCxFlags.isDerivedClassConstructor = true; }
 
     bool hasDefaults() const {
         return length != function()->nargs() - function()->hasRest();
     }
 
+    bool hasMappedArgsObj() const {
+        return !strict() && !function()->hasRest() && !hasDefaults() && !hasDestructuringArgs;
+    }
+
     // Return whether this or an enclosing function is being parsed and
     // validated as asm.js. Note: if asm.js validation fails, this will be false
     // while the function is being reparsed. This flag can be used to disable
     // certain parsing features that are necessary in general, but unnecessary
     // for validated asm.js.
     bool useAsmOrInsideUseAsm() const {
         return useAsm || insideUseAsm;
     }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/arguments/mapped-unmapped-args.js
@@ -0,0 +1,61 @@
+// An unmapped arguments object is created for strict functions or functions
+// with default/rest/destructuring args.
+
+load(libdir + "asserts.js");
+
+function testDefaults(a, b=3) {
+    a = 3;
+    b = 4;
+    assertEq(arguments.length, 1);
+    assertEq(arguments[0], 1);
+    assertEq(arguments[1], undefined);
+    arguments[0] = 5;
+    assertEq(a, 3);
+    assertThrowsInstanceOf(() => arguments.callee, TypeError);
+}
+testDefaults(1);
+
+function testRest(a, ...rest) {
+    a = 3;
+    assertEq(arguments.length, 3);
+    assertEq(arguments[0], 1);
+    assertEq(arguments[1], 2);
+    arguments[0] = 5;
+    assertEq(a, 3);
+    arguments[1] = 6;
+    assertEq(arguments[1], 6);
+    assertEq(rest.toString(), "2,3");
+    assertThrowsInstanceOf(() => arguments.callee, TypeError);
+}
+testRest(1, 2, 3);
+
+function testDestructuring(a, {foo, bar}, b) {
+    a = 3;
+    bar = 4;
+    b = 1;
+    assertEq(arguments.length, 3);
+    assertEq(arguments[0], 1);
+    assertEq(arguments[1].bar, 2);
+    assertEq(arguments[2], 9);
+    assertThrowsInstanceOf(() => arguments.callee, TypeError);
+}
+testDestructuring(1, {foo: 1, bar: 2}, 9);
+
+function testStrict(a) {
+    "use strict";
+    a = 3;
+    assertEq(arguments[0], 1);
+    arguments[0] = 8;
+    assertEq(a, 3);
+    assertThrowsInstanceOf(() => arguments.callee, TypeError);
+}
+testStrict(1, 2);
+
+function testMapped(a) {
+    a = 3;
+    assertEq(arguments[0], 3);
+    arguments[0] = 5;
+    assertEq(a, 5);
+    assertEq(arguments.callee, testMapped);
+}
+testMapped(1);
--- a/js/src/jit-test/tests/arguments/rest-with-arguments.js
+++ b/js/src/jit-test/tests/arguments/rest-with-arguments.js
@@ -1,39 +1,34 @@
 // 'arguments' is allowed with rest parameters.
 
-// FIXME: We should create an unmapped arguments object in this case,
-// see bug 1175394. This test is not correct until then.
-
 var args;
 
 function restWithArgs(a, b, ...rest) {
     return arguments;
 }
 
 args = restWithArgs(1, 3, 6, 9);
 assertEq(args.length, 4);
-assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":[6,9],"3":9}',
-	 "Did you just fix bug 1175394?");
+assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":6,"3":9}');
 
 args = restWithArgs();
 assertEq(args.length, 0);
 
 args = restWithArgs(4, 5);
 assertEq(args.length, 2);
 assertEq(JSON.stringify(args), '{"0":4,"1":5}');
 
 function restWithArgsEval(a, b, ...rest) {
     return eval("arguments");
 }
 
 args = restWithArgsEval(1, 3, 6, 9);
 assertEq(args.length, 4);
-assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":[6,9],"3":9}',
-	 "Did you just fix bug 1175394?");
+assertEq(JSON.stringify(args), '{"0":1,"1":3,"2":6,"3":9}');
 
 function g(...rest) {
     h();
 }
 function h() {
     g.arguments;
 }
 g();
--- a/js/src/jit-test/tests/arrow-functions/arguments-4.js
+++ b/js/src/jit-test/tests/arrow-functions/arguments-4.js
@@ -4,17 +4,17 @@ load(libdir + "asserts.js");
 assertEq((function(...rest) { return (x => arguments)(1, 2)})().length, 0);
 
 function restAndArgs(...rest) {
     return () => eval("arguments");
 }
 
 var args = restAndArgs(1, 2, 3)();
 assertEq(args.length, 3);
-assertDeepEq(args[0], [1, 2, 3], "This is bogus, see bug 1175394");
+assertEq(args[0], 1);
 assertEq(args[1], 2);
 assertEq(args[2], 3);
 
 (function() {
     return ((...rest) => {
         assertDeepEq(rest, [1, 2, 3]);
         assertEq(arguments.length, 2);
         assertEq(eval("arguments").length, 2);
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2616,19 +2616,19 @@ BaselineCompiler::emit_JSOP_SETLOCAL()
     uint32_t local = GET_LOCALNO(pc);
     storeValue(frame.peek(-1), frame.addressOfLocal(local), R0);
     return true;
 }
 
 bool
 BaselineCompiler::emitFormalArgAccess(uint32_t arg, bool get)
 {
-    // Fast path: the script does not use |arguments|, or is strict. In strict
-    // mode, formals do not alias the arguments object.
-    if (!script->argumentsHasVarBinding() || script->strict()) {
+    // Fast path: the script does not use |arguments| or formals don't
+    // alias the arguments object.
+    if (!script->argumentsAliasesFormals()) {
         if (get) {
             frame.pushArg(arg);
         } else {
             // See the comment in emit_JSOP_SETLOCAL.
             frame.syncStack(1);
             storeValue(frame.peek(-1), frame.addressOfArg(arg), R0);
         }
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -5860,17 +5860,17 @@ TryAttachMagicArgumentsGetPropStub(JSCon
 {
     MOZ_ASSERT(!*attached);
 
     if (!val.isMagic(JS_OPTIMIZED_ARGUMENTS))
         return true;
 
     // Try handling arguments.callee on optimized arguments.
     if (name == cx->names().callee) {
-        MOZ_ASSERT(!script->strict());
+        MOZ_ASSERT(script->hasMappedArgsObj());
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetProp(MagicArgs.callee) stub");
 
         // Unlike ICGetProp_ArgumentsLength, only magic argument stubs are
         // supported at the moment.
         ICStub* monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
         ICGetProp_ArgumentsCallee::Compiler compiler(cx, monitorStub);
         ICStub* newStub = compiler.getStub(compiler.getStubSpace(script));
@@ -6474,17 +6474,17 @@ ComputeGetPropResult(JSContext* cx, Base
 {
     // Handle arguments.length and arguments.callee on optimized arguments, as
     // it is not an object.
     if (val.isMagic(JS_OPTIMIZED_ARGUMENTS) && IsOptimizedArguments(frame, val)) {
         if (op == JSOP_LENGTH) {
             res.setInt32(frame->numActualArgs());
         } else {
             MOZ_ASSERT(name == cx->names().callee);
-            MOZ_ASSERT(!frame->script()->strict());
+            MOZ_ASSERT(frame->script()->hasMappedArgsObj());
             res.setObject(*frame->callee());
         }
     } else {
         if (op == JSOP_GETPROP || op == JSOP_LENGTH) {
             if (!GetProperty(cx, val, name, res))
                 return false;
         } else if (op == JSOP_CALLPROP) {
             if (!CallProperty(cx, val, name, res))
--- a/js/src/jit/CompileInfo.h
+++ b/js/src/jit/CompileInfo.h
@@ -455,17 +455,17 @@ class CompileInfo
     }
     bool argumentsAliasesFormals() const {
         return script()->argumentsAliasesFormals();
     }
     bool needsArgsObj() const {
         return scriptNeedsArgsObj_;
     }
     bool argsObjAliasesFormals() const {
-        return scriptNeedsArgsObj_ && !script()->strict();
+        return scriptNeedsArgsObj_ && script()->hasMappedArgsObj();
     }
 
     AnalysisMode analysisMode() const {
         return analysisMode_;
     }
 
     bool isAnalysis() const {
         return analysisMode_ != Analysis_None;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -3440,20 +3440,21 @@ ArgumentsUseCanBeLazy(JSContext* cx, JSS
         return true;
     }
 
     // MGetArgumentsObjectArg needs to be considered as a use that allows laziness.
     if (ins->isGetArgumentsObjectArg() && index == 0)
         return true;
 
     // arguments.length length can read fp->numActualArgs() directly.
-    // arguments.callee can read fp->callee() directly in non-strict code.
+    // arguments.callee can read fp->callee() directly if the arguments object
+    // is mapped.
     if (ins->isCallGetProperty() && index == 0 &&
         (ins->toCallGetProperty()->name() == cx->names().length ||
-         (!script->strict() && ins->toCallGetProperty()->name() == cx->names().callee)))
+         (script->hasMappedArgsObj() && ins->toCallGetProperty()->name() == cx->names().callee)))
     {
         return true;
     }
 
     return false;
 }
 
 bool
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10623,17 +10623,17 @@ IonBuilder::getPropTryArgumentsCallee(bo
     if (!checkIsDefinitelyOptimizedArguments(obj, &isOptimizedArgs))
         return false;
     if (!isOptimizedArgs)
         return true;
 
     if (name != names().callee)
         return true;
 
-    MOZ_ASSERT(!script()->strict());
+    MOZ_ASSERT(script()->hasMappedArgsObj());
 
     obj->setImplicitlyUsedUnchecked();
     current->push(getCallee());
 
     trackOptimizationSuccess();
     *emitted = true;
     return true;
 }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -584,16 +584,17 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         SavedCallerFun,
         Strict,
         ContainsDynamicNameAccess,
         FunHasExtensibleScope,
         FunNeedsDeclEnvObject,
         FunHasAnyAliasedFormal,
         ArgumentsHasVarBinding,
         NeedsArgsObj,
+        HasMappedArgsObj,
         IsGeneratorExp,
         IsLegacyGenerator,
         IsStarGenerator,
         OwnSource,
         ExplicitUseStrict,
         SelfHosted,
         HasSingleton,
         TreatAsRunOnce,
@@ -711,16 +712,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (script->funNeedsDeclEnvObject())
             scriptBits |= (1 << FunNeedsDeclEnvObject);
         if (script->funHasAnyAliasedFormal())
             scriptBits |= (1 << FunHasAnyAliasedFormal);
         if (script->argumentsHasVarBinding())
             scriptBits |= (1 << ArgumentsHasVarBinding);
         if (script->analyzedArgsUsage() && script->needsArgsObj())
             scriptBits |= (1 << NeedsArgsObj);
+        if (script->hasMappedArgsObj())
+            scriptBits |= (1 << HasMappedArgsObj);
         if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
             scriptBits |= (1 << OwnSource);
         if (script->isGeneratorExp())
             scriptBits |= (1 << IsGeneratorExp);
         if (script->isLegacyGenerator())
             scriptBits |= (1 << IsLegacyGenerator);
         if (script->isStarGenerator())
             scriptBits |= (1 << IsStarGenerator);
@@ -851,16 +854,18 @@ js::XDRScript(XDRState<mode>* xdr, Handl
         if (scriptBits & (1 << FunNeedsDeclEnvObject))
             script->funNeedsDeclEnvObject_ = true;
         if (scriptBits & (1 << FunHasAnyAliasedFormal))
             script->funHasAnyAliasedFormal_ = true;
         if (scriptBits & (1 << ArgumentsHasVarBinding))
             script->setArgumentsHasVarBinding();
         if (scriptBits & (1 << NeedsArgsObj))
             script->setNeedsArgsObj(true);
+        if (scriptBits & (1 << HasMappedArgsObj))
+            script->hasMappedArgsObj_ = true;
         if (scriptBits & (1 << IsGeneratorExp))
             script->isGeneratorExp_ = true;
         if (scriptBits & (1 << HasSingleton))
             script->hasSingletons_ = true;
         if (scriptBits & (1 << TreatAsRunOnce))
             script->treatAsRunOnce_ = true;
         if (scriptBits & (1 << HasNonSyntacticScope))
             script->hasNonSyntacticScope_ = true;
@@ -2723,16 +2728,17 @@ JSScript::linkToFunctionFromEmitter(js::
 
     if (funbox->argumentsHasLocalBinding()) {
         script->setArgumentsHasVarBinding();
         if (funbox->definitelyNeedsArgsObj())
             script->setNeedsArgsObj(true);
     } else {
         MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
     }
+    script->hasMappedArgsObj_ = funbox->hasMappedArgsObj();
 
     script->funLength_ = funbox->length;
 
     script->isGeneratorExp_ = funbox->inGenexpLambda;
     script->setGeneratorKind(funbox->generatorKind());
 
     // Link the function and the script to each other, so that StaticScopeIter
     // may walk the scope chain of currently compiling scripts.
@@ -2814,25 +2820,27 @@ JSScript::fullyInitFromEmitter(Exclusive
     // Assert that the properties set by linkToFunctionFromEmitter are
     // correct.
     if (funbox) {
         MOZ_ASSERT(script->funHasExtensibleScope_ == funbox->hasExtensibleScope());
         MOZ_ASSERT(script->funNeedsDeclEnvObject_ == funbox->needsDeclEnvObject());
         MOZ_ASSERT(script->needsHomeObject_ == funbox->needsHomeObject());
         MOZ_ASSERT(script->isDerivedClassConstructor_ == funbox->isDerivedClassConstructor());
         MOZ_ASSERT(script->argumentsHasVarBinding() == funbox->argumentsHasLocalBinding());
+        MOZ_ASSERT(script->hasMappedArgsObj() == funbox->hasMappedArgsObj());
         MOZ_ASSERT(script->functionNonDelazifying() == funbox->function());
         MOZ_ASSERT(script->isGeneratorExp_ == funbox->inGenexpLambda);
         MOZ_ASSERT(script->generatorKind() == funbox->generatorKind());
     } else {
         MOZ_ASSERT(!script->funHasExtensibleScope_);
         MOZ_ASSERT(!script->funNeedsDeclEnvObject_);
         MOZ_ASSERT(!script->needsHomeObject_);
         MOZ_ASSERT(!script->isDerivedClassConstructor_);
         MOZ_ASSERT(!script->argumentsHasVarBinding());
+        MOZ_ASSERT(!script->hasMappedArgsObj());
         MOZ_ASSERT(!script->isGeneratorExp_);
         MOZ_ASSERT(script->generatorKind() == NotGenerator);
     }
 #endif
 
     if (bce->constList.length() != 0)
         bce->constList.finish(script->consts());
     if (bce->objectList.length != 0)
@@ -3337,16 +3345,17 @@ js::detail::CopyScript(JSContext* cx, Ha
     dst->funLength_ = src->funLength();
     dst->nTypeSets_ = src->nTypeSets();
     dst->nslots_ = src->nslots();
     if (src->argumentsHasVarBinding()) {
         dst->setArgumentsHasVarBinding();
         if (src->analyzedArgsUsage())
             dst->setNeedsArgsObj(src->needsArgsObj());
     }
+    dst->hasMappedArgsObj_ = src->hasMappedArgsObj();
     dst->cloneHasArray(src);
     dst->strict_ = src->strict();
     dst->explicitUseStrict_ = src->explicitUseStrict();
     dst->bindingsAccessedDynamically_ = src->bindingsAccessedDynamically();
     dst->funHasExtensibleScope_ = src->funHasExtensibleScope();
     dst->funNeedsDeclEnvObject_ = src->funNeedsDeclEnvObject();
     dst->funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal();
     dst->hasSingletons_ = src->hasSingletons();
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -1130,16 +1130,20 @@ class JSScript : public js::gc::TenuredC
     // Freeze constraints for stack type sets have been generated.
     bool hasFreezeConstraints_:1;
 
     /* See comments below. */
     bool argsHasVarBinding_:1;
     bool needsArgsAnalysis_:1;
     bool needsArgsObj_:1;
 
+    // Whether the arguments object for this script, if it needs one, should be
+    // mapped (alias formal parameters).
+    bool hasMappedArgsObj_:1;
+
     // Generation for this script's TypeScript. If out of sync with the
     // TypeZone's generation, the TypeScript needs to be swept.
     //
     // This should be a uint32 but is instead a bool so that MSVC packs it
     // correctly.
     bool typesGeneration_:1;
 
     // Do not relazify this script. This is used by the relazify() testing
@@ -1399,17 +1403,17 @@ class JSScript : public js::gc::TenuredC
 
     /* See ContextFlags::funArgumentsHasLocalBinding comment. */
     bool argumentsHasVarBinding() const {
         return argsHasVarBinding_;
     }
     jsbytecode* argumentsBytecode() const { MOZ_ASSERT(code()[0] == JSOP_ARGUMENTS); return code(); }
     void setArgumentsHasVarBinding();
     bool argumentsAliasesFormals() const {
-        return argumentsHasVarBinding() && !strict();
+        return argumentsHasVarBinding() && hasMappedArgsObj();
     }
 
     js::GeneratorKind generatorKind() const {
         return js::GeneratorKindFromBits(generatorKindBits_);
     }
     bool isGenerator() const { return generatorKind() != js::NotGenerator; }
     bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
     bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
@@ -1445,27 +1449,30 @@ class JSScript : public js::gc::TenuredC
     inline bool ensureHasAnalyzedArgsUsage(JSContext* cx);
     bool needsArgsObj() const {
         MOZ_ASSERT(analyzedArgsUsage());
         return needsArgsObj_;
     }
     void setNeedsArgsObj(bool needsArgsObj);
     static bool argumentsOptimizationFailed(JSContext* cx, js::HandleScript script);
 
+    bool hasMappedArgsObj() const {
+        return hasMappedArgsObj_;
+    }
+
     /*
      * Arguments access (via JSOP_*ARG* opcodes) must access the canonical
-     * location for the argument. If an arguments object exists AND this is a
-     * non-strict function (where 'arguments' aliases formals), then all access
-     * must go through the arguments object. Otherwise, the local slot is the
-     * canonical location for the arguments. Note: if a formal is aliased
-     * through the scope chain, then script->formalIsAliased and JSOP_*ARG*
-     * opcodes won't be emitted at all.
+     * location for the argument. If an arguments object exists AND it's mapped
+     * ('arguments' aliases formals), then all access must go through the
+     * arguments object. Otherwise, the local slot is the canonical location for
+     * the arguments. Note: if a formal is aliased through the scope chain, then
+     * script->formalIsAliased and JSOP_*ARG* opcodes won't be emitted at all.
      */
     bool argsObjAliasesFormals() const {
-        return needsArgsObj() && !strict();
+        return needsArgsObj() && hasMappedArgsObj();
     }
 
     uint32_t typesGeneration() const {
         return (uint32_t) typesGeneration_;
     }
 
     void setTypesGeneration(uint32_t generation) {
         MOZ_ASSERT(generation <= 1);
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -202,17 +202,17 @@ JSCompartment::getOrCreateArgumentsTempl
     obj.set(templateObj);
     return templateObj;
 }
 
 template <typename CopyArgs>
 /* static */ ArgumentsObject*
 ArgumentsObject::create(JSContext* cx, HandleFunction callee, unsigned numActuals, CopyArgs& copy)
 {
-    bool strict = callee->strict();
+    bool strict = !callee->nonLazyScript()->hasMappedArgsObj(); // TODO: rename "strict" everywhere?
     ArgumentsObject* templateObj = cx->compartment()->getOrCreateArgumentsTemplateObject(cx, strict);
     if (!templateObj)
         return nullptr;
 
     RootedShape shape(cx, templateObj->lastProperty());
     RootedObjectGroup group(cx, templateObj->group());
 
     unsigned numFormals = callee->nargs();
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 304;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 305;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 406,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");