Bug 777061 - Don't count defaults in the length property of functions. r=jorendorff
authorBenjamin Peterson <benjamin@python.org>
Wed, 19 Sep 2012 16:46:16 -0400
changeset 107653 7fa37833033153902f7e8c009af41bfa27b12c3a
parent 107652 1c70ee7b0375db0ce6adb240a05d2819372977bf
child 107654 e161d18990dc4abbe263076745686a7aee201f31
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersjorendorff
bugs777061
milestone18.0a1
Bug 777061 - Don't count defaults in the length property of functions. r=jorendorff
js/src/jit-test/tests/arguments/defaults-basic.js
js/src/jit-test/tests/arguments/defaults-with-rest.js
js/src/jsfun.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/vm/Xdr.h
--- a/js/src/jit-test/tests/arguments/defaults-basic.js
+++ b/js/src/jit-test/tests/arguments/defaults-basic.js
@@ -1,22 +1,22 @@
 function f1(a, bIs, b=3) {
     assertEq(a, 1);
     assertEq(b, bIs);
 }
-assertEq(f1.length, 3);
+assertEq(f1.length, 2);
 f1(1, 3);
 f1(1, 42, 42);
 f1(1, 3, undefined);
 function f2(a, bIs, cIs, b=3, c=4) {
     assertEq(a, 1);
     assertEq(b, bIs);
     assertEq(c, cIs);
 }
-assertEq(f2.length, 5);
+assertEq(f2.length, 3);
 f2(1, 3, 4);
 f2(1, 42, 4, 42);
 f2(1, 42, 43, 42, 43);
 f2(1, 3, 4, undefined);
 f2(1, 42, 4, 42, undefined);
 f2(1, 3, 42, undefined, 42);
 function f3(a, b, c=4) {
     assertEq(a, 1);
--- a/js/src/jit-test/tests/arguments/defaults-with-rest.js
+++ b/js/src/jit-test/tests/arguments/defaults-with-rest.js
@@ -1,16 +1,16 @@
 load(libdir + "eqArrayHelper.js");
 
 function f1(a, bIs, b=3, ...rest) {
     assertEq(a, 1);
     assertEq(bIs, b);
     assertEqArray(rest, []);
 }
-assertEq(f1.length, 3);
+assertEq(f1.length, 2);
 f1(1, 3);
 f1(1, 42, 42);
 function f2(a=rest, ...rest) {
     assertEq(a, undefined);
 }
 f2();
 function f3(a=rest, ...rest) {
     assertEq(a, 1);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -307,20 +307,22 @@ fun_resolve(JSContext *cx, HandleObject 
         objp.set(fun);
         return true;
     }
 
     if (JSID_IS_ATOM(id, cx->names().length) || JSID_IS_ATOM(id, cx->names().name)) {
         JS_ASSERT(!IsInternalFunctionObject(obj));
 
         RootedValue v(cx);
-        if (JSID_IS_ATOM(id, cx->names().length))
-            v.setInt32(fun->nargs - fun->hasRest());
-        else
+        if (JSID_IS_ATOM(id, cx->names().length)) {
+            uint16_t defaults = fun->isInterpreted() ? fun->script()->ndefaults : 0;
+            v.setInt32(fun->nargs - defaults - fun->hasRest());
+        } else {
             v.setString(fun->atom() == NULL ?  cx->runtime->emptyString : fun->atom());
+        }
 
         if (!DefineNativeProperty(cx, fun, id, v, JS_PropertyStub, JS_StrictPropertyStub,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return false;
         }
         objp.set(fun);
         return true;
     }
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -392,16 +392,17 @@ js::XDRScript(XDRState<mode> *xdr, Handl
         IsGeneratorExp,
         OwnSource,
         ExplicitUseStrict
     };
 
     uint32_t length, lineno, nslots;
     uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, i;
     uint32_t prologLength, version;
+    uint32_t ndefaults = 0;
     uint32_t nTypeSets = 0;
     uint32_t scriptBits = 0;
 
     JSContext *cx = xdr->cx();
     Rooted<JSScript*> script(cx);
     nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
     jssrcnote *notes = NULL;
 
@@ -445,16 +446,17 @@ js::XDRScript(XDRState<mode> *xdr, Handl
         if (script->hasObjects())
             nobjects = script->objects()->length;
         if (script->hasRegexps())
             nregexps = script->regexps()->length;
         if (script->hasTrynotes())
             ntrynotes = script->trynotes()->length;
 
         nTypeSets = script->nTypeSets;
+        ndefaults = script->ndefaults;
 
         if (script->noScriptRval)
             scriptBits |= (1 << NoScriptRval);
         if (script->savedCallerFun)
             scriptBits |= (1 << SavedCallerFun);
         if (script->strictModeCode)
             scriptBits |= (1 << StrictModeCode);
         if (script->explicitUseStrict)
@@ -503,16 +505,18 @@ js::XDRScript(XDRState<mode> *xdr, Handl
     if (!xdr->codeUint32(&nobjects))
         return JS_FALSE;
     if (!xdr->codeUint32(&nregexps))
         return JS_FALSE;
     if (!xdr->codeUint32(&nconsts))
         return JS_FALSE;
     if (!xdr->codeUint32(&nTypeSets))
         return JS_FALSE;
+    if (!xdr->codeUint32(&ndefaults))
+        return JS_FALSE;
     if (!xdr->codeUint32(&scriptBits))
         return JS_FALSE;
 
     if (mode == XDR_DECODE) {
         /* Note: version is packed into the 32b space with another 16b value. */
         JSVersion version_ = JSVersion(version & JS_BITMASK(16));
         JS_ASSERT((version_ & VersionFlags::FULL_MASK) == unsigned(version_));
 
@@ -545,16 +549,17 @@ js::XDRScript(XDRState<mode> *xdr, Handl
     if (mode == XDR_DECODE) {
         if (!JSScript::partiallyInit(cx, script, length, nsrcnotes, natoms, nobjects, nregexps,
                                      ntrynotes, nconsts, nTypeSets))
             return false;
 
         JS_ASSERT(!script->mainOffset);
         script->mainOffset = prologLength;
         script->nfixed = uint16_t(version >> 16);
+        script->ndefaults = ndefaults;
 
         /* If we know nsrcnotes, we allocated space for notes in script. */
         notes = script->notes();
         *scriptp = script;
 
         if (scriptBits & (1 << StrictModeCode))
             script->strictModeCode = true;
         if (scriptBits & (1 << ExplicitUseStrict))
@@ -1681,16 +1686,18 @@ JSScript::fullyInitFromEmitter(JSContext
         if (funbox->argumentsHasLocalBinding()) {
             // This must precede the script->bindings.transfer() call below
             script->setArgumentsHasVarBinding();
             if (funbox->definitelyNeedsArgsObj())
                 script->setNeedsArgsObj(true);
         } else {
             JS_ASSERT(!funbox->definitelyNeedsArgsObj());
         }
+
+        script->ndefaults = funbox->ndefaults;
     }
 
     RootedFunction fun(cx, NULL);
     if (funbox) {
         JS_ASSERT(!bce->script->noScriptRval);
         script->isGenerator = funbox->isGenerator();
         script->isGeneratorExp = funbox->inGenexpLambda;
         script->setFunction(funbox->fun());
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -391,24 +391,26 @@ struct JSScript : public js::gc::Cell
 #ifdef DEBUG
     // Unique identifier within the compartment for this script, used for
     // printing analysis information.
     uint32_t        id_;
   private:
     uint32_t        idpad;
 #endif
 
-    uint32_t        PADDING;
-
     // 16-bit fields.
 
   private:
+    uint16_t        PADDING;
+
     uint16_t        version;    /* JS version under which script was compiled */
 
   public:
+    uint16_t        ndefaults;  /* number of defaults the function has */
+
     uint16_t        nfixed;     /* number of slots besides stack operands in
                                    slot array */
 
     uint16_t        nTypeSets;  /* number of type sets used in this script for
                                    dynamic type monitoring */
 
     uint16_t        nslots;     /* vars plus maximum stack depth */
     uint16_t        staticLevel;/* static level for display maintenance */
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -20,17 +20,17 @@ namespace js {
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number is XDR'd near the front of xdr bytecode and
  * aborts deserialization if there is a mismatch between the current
  * and saved versions. If deserialization fails, the data should be
  * invalidated if possible.
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 131);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 132);
 
 class XDRBuffer {
   public:
     XDRBuffer(JSContext *cx)
       : context(cx), base(NULL), cursor(NULL), limit(NULL) { }
 
     JSContext *cx() const {
         return context;