Remove JSFunction fixed slots and unused fields, bug 697537.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 26 Oct 2011 13:02:57 -0700
changeset 82910 57b753e28ffdb29a579e989863d46e527a8e0e5d
parent 82909 36694940133d5b140324ba342174ce10343df18f
child 82911 226503831db200d513e1318258622ff65ecadc65
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs697537
milestone10.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
Remove JSFunction fixed slots and unused fields, bug 697537.
js/src/frontend/BytecodeGenerator.cpp
js/src/frontend/Parser.cpp
js/src/jsapi.cpp
js/src/jsbuiltins.h
js/src/jscell.h
js/src/jscompartment.cpp
js/src/jsexn.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsfuninlines.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsgcinlines.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsprobes.cpp
js/src/jsscope.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/StubCalls.cpp
js/src/tracejit/Writer.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/GlobalObject.h
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCQuickStubs.cpp
js/xpconnect/src/XPCWrappedNativeInfo.cpp
--- a/js/src/frontend/BytecodeGenerator.cpp
+++ b/js/src/frontend/BytecodeGenerator.cpp
@@ -2373,17 +2373,16 @@ BindNameToSlot(JSContext *cx, JSCodeGene
     uint16 level = cookie.level();
     JS_ASSERT(cg->staticLevel >= level);
 
     const uintN skip = cg->staticLevel - level;
     if (skip != 0) {
         JS_ASSERT(cg->inFunction());
         JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, cg->roLexdeps->lookup(atom));
         JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
-        JS_ASSERT(cg->fun()->u.i.skipmin <= skip);
 
         /*
          * If op is a mutating opcode, this upvar's lookup skips too many levels,
          * or the function is heavyweight, we fall back on JSOP_*NAME*.
          */
         if (op != JSOP_NAME)
             return JS_TRUE;
         if (skip >= UpvarCookie::UPVAR_LEVEL_LIMIT)
@@ -5590,16 +5589,46 @@ EmitWith(JSContext *cx, JSCodeGenerator 
     if (!js_EmitTree(cx, cg, pn->pn_right))
         return false;
     if (js_Emit1(cx, cg, JSOP_LEAVEWITH) < 0)
         return false;
     ok = js_PopStatementCG(cx, cg);
     return true;
 }
 
+static bool
+SetMethodFunction(JSContext *cx, JSFunctionBox *funbox, JSAtom *atom)
+{
+    /* Replace a boxed function with a new one with a method atom. */
+    JSFunction *fun = js_NewFunction(cx, NULL, NULL,
+                                     funbox->function()->nargs,
+                                     funbox->function()->flags,
+                                     funbox->function()->getParent(),
+                                     funbox->function()->atom,
+                                     JSFunction::ExtendedFinalizeKind);
+    if (!fun)
+        return false;
+
+    JSScript *script = funbox->function()->script();
+    if (script) {
+        fun->setScript(funbox->function()->script());
+        if (!fun->script()->typeSetFunction(cx, fun))
+            return false;
+    }
+
+    JS_ASSERT(funbox->function()->joinable());
+    fun->setJoinable();
+
+    fun->setMethodAtom(atom);
+
+    funbox->object = fun;
+
+    return true;
+}
+
 JSBool
 js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
 {
     JSBool useful, wantval;
     JSStmtInfo stmtInfo;
     JSStmtInfo *stmt;
     ptrdiff_t top, off, tmp, beq, jmp, tmp2, tmp3;
     JSParseNode *pn2, *pn3;
@@ -6459,16 +6488,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                      * can't escape directly.
                      */
                     if (!wantval &&
                         pn2->isKind(TOK_ASSIGN) &&
                         pn2->isOp(JSOP_NOP) &&
                         pn2->pn_left->isOp(JSOP_SETPROP) &&
                         pn2->pn_right->isOp(JSOP_LAMBDA) &&
                         pn2->pn_right->pn_funbox->joinable()) {
+                        if (!SetMethodFunction(cx, pn2->pn_right->pn_funbox, pn2->pn_left->pn_atom))
+                            return JS_FALSE;
                         pn2->pn_left->setOp(JSOP_SETMETHOD);
                     }
                     if (!js_EmitTree(cx, cg, pn2))
                         return JS_FALSE;
                     if (js_Emit1(cx, cg, op) < 0)
                         return JS_FALSE;
                 }
             }
@@ -7223,16 +7254,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                 JSParseNode *init = pn2->pn_right;
                 bool lambda = init->isOp(JSOP_LAMBDA);
                 if (lambda)
                     ++methodInits;
                 if (op == JSOP_INITPROP && lambda && init->pn_funbox->joinable()) {
                     obj = NULL;
                     op = JSOP_INITMETHOD;
                     pn2->setOp(op);
+                    if (!SetMethodFunction(cx, init->pn_funbox, pn3->pn_atom))
+                        return JS_FALSE;
                 } else {
                     /*
                      * Disable NEWOBJECT on initializers that set __proto__, which has
                      * a non-standard setter on objects.
                      */
                     if (pn3->pn_atom == cx->runtime->atomState.protoAtom)
                         obj = NULL;
                     op = JSOP_INITPROP;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1092,17 +1092,16 @@ Parser::analyzeFunctions(JSTreeContext *
 static uintN
 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
 {
     uintN allskipmin = UpvarCookie::FREE_LEVEL;
 
     do {
         JSParseNode *fn = funbox->node;
         JS_ASSERT(fn->isArity(PN_FUNC));
-        JSFunction *fun = funbox->function();
         int fnlevel = level;
 
         /*
          * An eval can leak funbox, functions along its ancestor line, and its
          * immediate kids. Since FindFunArgs uses DFS and the parser propagates
          * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
          * already been marked as funargs by this point. Therefore we have to
          * flag only funbox->node and funbox->kids' nodes here.
@@ -1173,22 +1172,20 @@ FindFunArgs(JSFunctionBox *funbox, int l
                 --kidskipmin;
                 if (kidskipmin != 0 && kidskipmin < skipmin)
                     skipmin = kidskipmin;
             }
         }
 
         /*
          * Finally, after we've traversed all of the current function's kids,
-         * minimize fun's skipmin against our accumulated skipmin. Do likewise
-         * with allskipmin, but minimize across funbox and all of its siblings,
-         * to compute our return value.
+         * minimize allskipmin against our accumulated skipmin. Minimize across
+         * funbox and all of its siblings, to compute our return value.
          */
         if (skipmin != UpvarCookie::FREE_LEVEL) {
-            fun->u.i.skipmin = skipmin;
             if (skipmin < allskipmin)
                 allskipmin = skipmin;
         }
     } while ((funbox = funbox->siblings) != NULL);
 
     return allskipmin;
 }
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4285,17 +4285,22 @@ JS_PUBLIC_API(JSFunction *)
 JS_NewFunctionById(JSContext *cx, JSNative native, uintN nargs, uintN flags, JSObject *parent,
                    jsid id)
 {
     JS_ASSERT(JSID_IS_STRING(id));
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);
 
-    return js_NewFunction(cx, NULL, native, nargs, flags, parent, JSID_TO_ATOM(id));
+    /* Allow natives created through this interface to use {Get,Set}FunctionNativeReserved. */
+    AllocKind kind = native
+        ? (AllocKind) JSFunction::ExtendedFinalizeKind
+        : (AllocKind) JSFunction::FinalizeKind;
+
+    return js_NewFunction(cx, NULL, native, nargs, flags, parent, JSID_TO_ATOM(id), kind);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
 {
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, parent);  // XXX no funobj for now
     if (!parent) {
@@ -4313,26 +4318,26 @@ JS_CloneFunctionObject(JSContext *cx, JS
          */
         Value v = ObjectValue(*funobj);
         js_ReportIsNotFunction(cx, &v, 0);
         return NULL;
     }
 
     JSFunction *fun = funobj->toFunction();
     if (!fun->isInterpreted())
-        return CloneFunctionObject(cx, fun, parent);
+        return CloneFunctionObject(cx, fun, parent, fun->getAllocKind());
 
     if (fun->script()->compileAndGo) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
         return NULL;
     }
 
     if (!fun->isFlatClosure())
-        return CloneFunctionObject(cx, fun, parent);
+        return CloneFunctionObject(cx, fun, parent, fun->getAllocKind());
 
     /*
      * A flat closure carries its own environment, so why clone it? In case
      * someone wants to mutate its fixed slots or add ad-hoc properties. API
      * compatibility suggests we not return funobj and let callers mutate the
      * returned object at will.
      *
      * But it's worse than that: API compatibility according to the test for
@@ -4359,17 +4364,17 @@ JS_CloneFunctionObject(JSContext *cx, JS
                 return NULL;
             }
             obj = obj->getParentOrScopeChain();
         }
 
         Value v;
         if (!obj->getGeneric(cx, r.front().propid(), &v))
             return NULL;
-        clone->getFlatClosureUpvars()[i] = v;
+        clone->toFunction()->getFlatClosureUpvars()[i] = v;
     }
 
     return clone;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetFunctionObject(JSFunction *fun)
 {
@@ -4413,17 +4418,18 @@ JS_IsNativeFunction(JSObject *funobj, JS
         return false;
     JSFunction *fun = funobj->toFunction();
     return fun->isNative() && fun->native() == call;
 }
 
 JSBool
 js_generic_native_method_dispatcher(JSContext *cx, uintN argc, Value *vp)
 {
-    JSFunctionSpec *fs = (JSFunctionSpec *) vp->toObject().getReservedSlot(0).toPrivate();
+    JSFunctionSpec *fs = (JSFunctionSpec *)
+        vp->toObject().toFunction()->getNativeReserved(0).toPrivate();
     JS_ASSERT((fs->flags & JSFUN_GENERIC_NATIVE) != 0);
 
     if (argc < 1) {
         js_ReportMissingArg(cx, *vp, 0);
         return JS_FALSE;
     }
 
     /*
@@ -4475,27 +4481,26 @@ JS_DefineFunctions(JSContext *cx, JSObje
                 if (!ctor)
                     return JS_FALSE;
             }
 
             flags &= ~JSFUN_GENERIC_NATIVE;
             fun = js_DefineFunction(cx, ctor, ATOM_TO_JSID(atom),
                                     js_generic_native_method_dispatcher,
                                     fs->nargs + 1,
-                                    flags & ~JSFUN_TRCINFO);
+                                    flags & ~JSFUN_TRCINFO,
+                                    JSFunction::ExtendedFinalizeKind);
             if (!fun)
                 return JS_FALSE;
 
             /*
              * As jsapi.h notes, fs must point to storage that lives as long
              * as fun->object lives.
              */
-            Value priv = PrivateValue(fs);
-            if (!js_SetReservedSlot(cx, fun, 0, priv))
-                return JS_FALSE;
+            fun->setNativeReserved(0, PrivateValue(fs));
         }
 
         fun = js_DefineFunction(cx, obj, ATOM_TO_JSID(atom), fs->call, fs->nargs, flags);
         if (!fun)
             return JS_FALSE;
     }
     return JS_TRUE;
 }
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -615,22 +615,20 @@ JS_DECLARE_CALLINFO(js_AddProperty)
 JS_DECLARE_CALLINFO(js_AddAtomProperty)
 JS_DECLARE_CALLINFO(js_HasNamedProperty)
 JS_DECLARE_CALLINFO(js_HasNamedPropertyInt32)
 JS_DECLARE_CALLINFO(js_TypeOfObject)
 JS_DECLARE_CALLINFO(js_BooleanIntToString)
 JS_DECLARE_CALLINFO(js_NewNullClosure)
 
 /* Defined in jsfun.cpp. */
-JS_DECLARE_CALLINFO(js_AllocFlatClosure)
 JS_DECLARE_CALLINFO(js_PutArgumentsOnTrace)
 JS_DECLARE_CALLINFO(js_PutCallObjectOnTrace)
 JS_DECLARE_CALLINFO(js_SetCallVar)
 JS_DECLARE_CALLINFO(js_SetCallArg)
-JS_DECLARE_CALLINFO(js_CloneFunctionObject)
 JS_DECLARE_CALLINFO(js_CreateCallObjectOnTrace)
 JS_DECLARE_CALLINFO(js_NewArgumentsOnTrace)
 
 /* Defined in jsnum.cpp. */
 JS_DECLARE_CALLINFO(js_NumberToString)
 
 /* Defined in jsobj.cpp. */
 JS_DECLARE_CALLINFO(js_Object_tn)
--- a/js/src/jscell.h
+++ b/js/src/jscell.h
@@ -60,18 +60,16 @@ enum AllocKind {
     FINALIZE_OBJECT4_BACKGROUND,
     FINALIZE_OBJECT8,
     FINALIZE_OBJECT8_BACKGROUND,
     FINALIZE_OBJECT12,
     FINALIZE_OBJECT12_BACKGROUND,
     FINALIZE_OBJECT16,
     FINALIZE_OBJECT16_BACKGROUND,
     FINALIZE_OBJECT_LAST = FINALIZE_OBJECT16_BACKGROUND,
-    FINALIZE_FUNCTION,
-    FINALIZE_FUNCTION_AND_OBJECT_LAST = FINALIZE_FUNCTION,
     FINALIZE_SCRIPT,
     FINALIZE_SHAPE,
     FINALIZE_BASE_SHAPE,
     FINALIZE_TYPE_OBJECT,
 #if JS_HAS_XML_SUPPORT
     FINALIZE_XML,
 #endif
     FINALIZE_SHORT_STRING,
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -468,17 +468,17 @@ JSCompartment::markTypes(JSTracer *trc)
     JS_ASSERT(activeAnalysis);
 
     for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         MarkScript(trc, script, "mark_types_script");
     }
 
     for (size_t thingKind = FINALIZE_OBJECT0;
-         thingKind <= FINALIZE_FUNCTION_AND_OBJECT_LAST;
+         thingKind <= FINALIZE_OBJECT_LAST;
          thingKind++) {
         for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) {
             JSObject *object = i.get<JSObject>();
             if (!object->isNewborn() && object->hasSingletonType())
                 MarkObject(trc, *object, "mark_types_singleton");
         }
     }
 
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -691,20 +691,16 @@ StackTraceToString(JSContext *cx, JSExnP
 /* XXXbe Consolidate the ugly truth that we don't treat filename as UTF-8
          with these two functions. */
 static JSString *
 FilenameToString(JSContext *cx, const char *filename)
 {
     return JS_NewStringCopyZ(cx, filename);
 }
 
-enum {
-    JSSLOT_ERROR_EXNTYPE = 0
-};
-
 static JSBool
 Exception(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /*
      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
      * called as functions, without operator new.  But as we do not give
@@ -763,17 +759,17 @@ Exception(JSContext *cx, uintN argc, Val
     uint32_t lineno;
     if (args.length() > 2) {
         if (!ValueToECMAUint32(cx, args[2], &lineno))
             return false;
     } else {
         lineno = iter.done() ? 0 : js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
     }
 
-    intN exnType = args.callee().getReservedSlot(JSSLOT_ERROR_EXNTYPE).toInt32();
+    intN exnType = args.callee().toFunction()->getNativeReserved(0).toInt32();
     if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType))
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
 /*
@@ -1026,20 +1022,21 @@ InitErrorClass(JSContext *cx, GlobalObje
                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0) ||
         !DefineNativeProperty(cx, errorProto, lineNumberId, Int32Value(0),
                               JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE, 0, 0))
     {
         return NULL;
     }
 
     /* Create the corresponding constructor. */
-    JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1);
+    JSFunction *ctor = global->createConstructor(cx, Exception, &ErrorClass, name, 1,
+                                                 JSFunction::ExtendedFinalizeKind);
     if (!ctor)
         return NULL;
-    ctor->setReservedSlot(JSSLOT_ERROR_EXNTYPE, Int32Value(int32(type)));
+    ctor->setNativeReserved(0, Int32Value(int32(type)));
 
     if (!LinkConstructorAndPrototype(cx, ctor, errorProto))
         return NULL;
 
     if (!DefineConstructorAndPrototype(cx, global, key, ctor, errorProto))
         return NULL;
 
     JS_ASSERT(!errorProto->getPrivate());
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -202,16 +202,28 @@ js::GetObjectSlotSpan(const JSObject *ob
 }
 
 JS_FRIEND_API(bool)
 js::IsOriginalScriptFunction(JSFunction *fun)
 {
     return fun->script()->function() == fun;
 }
 
+JS_FRIEND_API(const Value &)
+js::GetFunctionNativeReserved(JSObject *fun, size_t which)
+{
+    return fun->toFunction()->getNativeReserved(which);
+}
+
+JS_FRIEND_API(void)
+js::SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val)
+{
+    fun->toFunction()->setNativeReserved(which, val);
+}
+
 /*
  * The below code is for temporary telemetry use. It can be removed when
  * sufficient data has been harvested.
  */
 
 extern size_t sE4XObjectsCreated;
 
 JS_FRIEND_API(size_t)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -271,16 +271,22 @@ JS_FRIEND_API(JSObject *)
 GetObjectParentMaybeScope(const JSObject *obj);
 
 JS_FRIEND_API(JSObject *)
 GetObjectGlobal(JSObject *obj);
 
 JS_FRIEND_API(bool)
 IsOriginalScriptFunction(JSFunction *fun);
 
+JS_FRIEND_API(const Value &)
+GetFunctionNativeReserved(JSObject *fun, size_t which);
+
+JS_FRIEND_API(void)
+SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val);
+
 inline JSObject *
 GetObjectProto(const JSObject *obj)
 {
     return reinterpret_cast<const shadow::Object*>(obj)->type->proto;
 }
 
 inline void *
 GetObjectPrivate(const JSObject *obj)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1022,28 +1022,28 @@ SetCallArg(JSContext *cx, JSObject *obj,
 
 JSBool
 GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    *vp = callobj.getCallee()->getFlatClosureUpvar(i);
+    *vp = callobj.getCallee()->toFunction()->getFlatClosureUpvar(i);
     return true;
 }
 
 JSBool
 SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
     CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    callobj.getCallee()->setFlatClosureUpvar(i, *vp);
+    callobj.getCallee()->toFunction()->setFlatClosureUpvar(i, *vp);
     return true;
 }
 
 JSBool
 GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
@@ -1235,17 +1235,17 @@ StackFrame::getValidCalleeObject(JSConte
                 }
 
                 if (shape->hasSlot()) {
                     Value v = thisp->getSlot(shape->slot());
                     JSFunction *clone;
 
                     if (IsFunctionObject(v, &clone) &&
                         clone->script() == fun->script() &&
-                        clone->hasMethodObj(*thisp)) {
+                        clone->methodObj() == thisp) {
                         /*
                          * N.B. If the method barrier was on a function
                          * with singleton type, then while crossing the
                          * method barrier CloneFunctionObject will have
                          * ignored the attempt to clone the function.
                          */
                         JS_ASSERT_IF(!clone->hasSingletonType(), clone != fun);
                         *vp = v;
@@ -1567,17 +1567,17 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
         if (!fun->isInterpreted()) {
             JSAutoByteString funNameBytes;
             if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION,
                                      name);
             }
             return false;
         }
-        firstword = (fun->u.i.skipmin << 2) | !!fun->atom;
+        firstword = !!fun->atom;
         flagsword = (fun->nargs << 16) | fun->flags;
     } else {
         fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
         if (!fun)
             return false;
         if (!fun->clearParent(cx))
             return false;
         if (!fun->clearType(cx))
@@ -1592,17 +1592,16 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
         return false;
     if (!JS_XDRUint32(xdr, &flagsword))
         return false;
 
     if (xdr->mode == JSXDR_DECODE) {
         fun->nargs = flagsword >> 16;
         JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
         fun->flags = uint16(flagsword);
-        fun->u.i.skipmin = uint16(firstword >> 2);
     }
 
     /*
      * Don't directly store into fun->u.i.script because we want this to happen
      * at the same time as we set the script's owner.
      */
     JSScript *script = fun->script();
     if (!js_XDRScript(xdr, &script))
@@ -1632,17 +1631,17 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
  * if v is an object) returning true if .prototype is found.
  */
 static JSBool
 fun_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
 {
     while (obj->isFunction()) {
         if (!obj->isBoundFunction())
             break;
-        obj = obj->getBoundFunctionTarget();
+        obj = obj->toFunction()->getBoundFunctionTarget();
     }
 
     Value pval;
     if (!obj->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &pval))
         return JS_FALSE;
 
     if (pval.isPrimitive()) {
         /*
@@ -1652,53 +1651,73 @@ fun_hasInstance(JSContext *cx, JSObject 
         js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, ObjectValue(*obj), NULL);
         return JS_FALSE;
     }
 
     *bp = js_IsDelegate(cx, &pval.toObject(), *v);
     return JS_TRUE;
 }
 
+inline void
+JSFunction::trace(JSTracer *trc)
+{
+    if (isFlatClosure() && isExtended() && script()->bindings.hasUpvars()) {
+        Value *upvars = getFlatClosureUpvars();
+        if (upvars)
+            MarkValueRange(trc, script()->bindings.countUpvars(), upvars, "upvars");
+    }
+
+    if (joinable()) {
+        JSAtom *atom = methodAtom();
+        if (atom)
+            MarkString(trc, atom, "methodAtom");
+        JSObject *obj = methodObj();
+        if (obj)
+            MarkObject(trc, *obj, "methodObj");
+    }
+
+    if (isNative() && isExtended()) {
+        MarkValueRange(trc, JS_ARRAY_LENGTH(toExtended()->extu.nativeReserved),
+                       toExtended()->extu.nativeReserved, "nativeReserved");
+    }
+
+    if (atom)
+        MarkString(trc, atom, "atom");
+
+    if (isInterpreted()) {
+        if (script())
+            MarkScript(trc, script(), "script");
+        if (callScope())
+            MarkObject(trc, *callScope(), "fun_callscope");
+    }
+}
+
+
+
 static void
 fun_trace(JSTracer *trc, JSObject *obj)
 {
-    JSFunction *fun = obj->toFunction();
-
-    if (fun->isFlatClosure() && fun->script()->bindings.hasUpvars()) {
-        Value *upvars = obj->getFlatClosureUpvars();
-        if (upvars)
-            MarkValueRange(trc, fun->script()->bindings.countUpvars(), upvars, "upvars");
-    }
-
-    if (fun->atom)
-        MarkString(trc, fun->atom, "atom");
-
-    if (fun->isInterpreted()) {
-        if (fun->script())
-            MarkScript(trc, fun->script(), "script");
-        if (fun->callScope())
-            MarkObject(trc, *fun->callScope(), "fun_callscope");
-    }
+    obj->toFunction()->trace(trc);
 }
 
 static void
 fun_finalize(JSContext *cx, JSObject *obj)
 {
-    obj->finalizeUpvarsIfFlatClosure();
+    if (obj->toFunction()->isFlatClosure())
+        obj->toFunction()->finalizeUpvars();
 }
 
 /*
  * Reserve two slots in all function objects for XPConnect.  Note that this
  * does not bloat every instance, only those on which reserved slots are set,
  * and those on which ad-hoc properties are defined.
  */
 JS_FRIEND_DATA(Class) js::FunctionClass = {
     js_Function_str,
     JSCLASS_NEW_RESOLVE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSFunction::CLASS_RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     fun_enumerate,
     (JSResolveOp)fun_resolve,
     JS_ConvertStub,
@@ -1890,114 +1909,118 @@ js_fun_apply(JSContext *cx, uintN argc, 
 
 namespace js {
 
 JSBool
 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp);
 
 }
 
+static const uint32 JSSLOT_BOUND_FUNCTION_THIS       = 0;
+static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1;
+
+static const uint32 BOUND_FUNCTION_RESERVED_SLOTS = 2;
+
 inline bool
-JSObject::initBoundFunction(JSContext *cx, const Value &thisArg,
-                            const Value *args, uintN argslen)
+JSFunction::initBoundFunction(JSContext *cx, const Value &thisArg,
+                              const Value *args, uintN argslen)
 {
     JS_ASSERT(isFunction());
 
-    setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
-    setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
-
     /*
      * Convert to a dictionary to set the BOUND_FUNCTION flag and increase
-     * the slot span to cover the arguments.
+     * the slot span to cover the arguments and additional slots for the 'this'
+     * value and arguments count.
      */
     if (!toDictionaryMode(cx))
         return false;
 
     lastProperty()->base()->setObjectFlag(BaseShape::BOUND_FUNCTION);
 
-    JS_ASSERT(slotSpan() == JSSLOT_FREE(&FunctionClass));
-    if (!setSlotSpan(cx, slotSpan() + argslen))
+    if (!setSlotSpan(cx, BOUND_FUNCTION_RESERVED_SLOTS + argslen))
         return false;
 
-    copySlotRange(FUN_CLASS_RESERVED_SLOTS, args, argslen);
+    setSlot(JSSLOT_BOUND_FUNCTION_THIS, thisArg);
+    setSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT, PrivateUint32Value(argslen));
+
+    copySlotRange(BOUND_FUNCTION_RESERVED_SLOTS, args, argslen);
 
     return true;
 }
 
 inline JSObject *
-JSObject::getBoundFunctionTarget() const
+JSFunction::getBoundFunctionTarget() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
     /* Bound functions abuse |parent| to store their target function. */
     return getParent();
 }
 
 inline const js::Value &
-JSObject::getBoundFunctionThis() const
+JSFunction::getBoundFunctionThis() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
     return getSlot(JSSLOT_BOUND_FUNCTION_THIS);
 }
 
 inline const js::Value &
-JSObject::getBoundFunctionArgument(uintN which) const
+JSFunction::getBoundFunctionArgument(uintN which) const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
     JS_ASSERT(which < getBoundFunctionArgumentCount());
 
-    return getSlot(FUN_CLASS_RESERVED_SLOTS + which);
+    return getSlot(BOUND_FUNCTION_RESERVED_SLOTS + which);
 }
 
 inline size_t
-JSObject::getBoundFunctionArgumentCount() const
+JSFunction::getBoundFunctionArgumentCount() const
 {
     JS_ASSERT(isFunction());
     JS_ASSERT(isBoundFunction());
 
     return getSlot(JSSLOT_BOUND_FUNCTION_ARGS_COUNT).toPrivateUint32();
 }
 
 namespace js {
 
 /* ES5 15.3.4.5.1 and 15.3.4.5.2. */
 JSBool
 CallOrConstructBoundFunction(JSContext *cx, uintN argc, Value *vp)
 {
-    JSObject *obj = &vp[0].toObject();
-    JS_ASSERT(obj->isFunction());
-    JS_ASSERT(obj->isBoundFunction());
+    JSFunction *fun = vp[0].toObject().toFunction();
+    JS_ASSERT(fun->isBoundFunction());
 
     bool constructing = IsConstructing(vp);
 
     /* 15.3.4.5.1 step 1, 15.3.4.5.2 step 3. */
-    uintN argslen = obj->getBoundFunctionArgumentCount();
+    uintN argslen = fun->getBoundFunctionArgumentCount();
 
     if (argc + argslen > StackSpace::ARGS_LENGTH_MAX) {
         js_ReportAllocationOverflow(cx);
         return false;
     }
 
     /* 15.3.4.5.1 step 3, 15.3.4.5.2 step 1. */
-    JSObject *target = obj->getBoundFunctionTarget();
+    JSObject *target = fun->getBoundFunctionTarget();
 
     /* 15.3.4.5.1 step 2. */
-    const Value &boundThis = obj->getBoundFunctionThis();
+    const Value &boundThis = fun->getBoundFunctionThis();
 
     InvokeArgsGuard args;
     if (!cx->stack.pushInvokeArgs(cx, argc + argslen, &args))
         return false;
 
     /* 15.3.4.5.1, 15.3.4.5.2 step 4. */
     for (uintN i = 0; i < argslen; i++)
-        args[i] = obj->getBoundFunctionArgument(i);
+        args[i] = fun->getBoundFunctionArgument(i);
     memcpy(args.array() + argslen, vp + 2, argc * sizeof(Value));
 
     /* 15.3.4.5.1, 15.3.4.5.2 step 5. */
     args.calleev().setObject(*target);
 
     if (!constructing)
         args.thisv() = boundThis;
 
@@ -2075,17 +2098,17 @@ fun_bind(JSContext *cx, uintN argc, Valu
         return false;
 
     /* NB: Bound functions abuse |parent| to store their target. */
     if (!funobj->setParent(cx, target))
         return false;
 
     /* Steps 7-9. */
     Value thisArg = args.length() >= 1 ? args[0] : UndefinedValue();
-    if (!funobj->initBoundFunction(cx, thisArg, boundArgs, argslen))
+    if (!funobj->toFunction()->initBoundFunction(cx, thisArg, boundArgs, argslen))
         return false;
 
     /* Steps 17, 19-21 are handled by fun_resolve. */
     /* Step 18 is the default for new functions. */
 
     /* Step 22. */
     args.rval().setObject(*funobj);
     return true;
@@ -2319,79 +2342,82 @@ LookupInterpretedFunctionPrototype(JSCon
     JS_ASSERT(!shape->isMethod());
     return shape;
 }
 
 } /* namespace js */
 
 JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, Native native, uintN nargs,
-               uintN flags, JSObject *parent, JSAtom *atom)
+               uintN flags, JSObject *parent, JSAtom *atom, js::gc::AllocKind kind)
 {
     JSFunction *fun;
 
     if (funobj) {
         JS_ASSERT(funobj->isFunction());
         JS_ASSERT(funobj->getParent() == parent);
     } else {
-        funobj = NewFunction(cx, SkipScopeParent(parent));
+        funobj = NewFunction(cx, SkipScopeParent(parent), kind);
         if (!funobj)
             return NULL;
         if (native && !funobj->setSingletonType(cx))
             return NULL;
     }
     fun = static_cast<JSFunction *>(funobj);
 
     /* Initialize all function members. */
     fun->nargs = uint16(nargs);
-    fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRCINFO);
+    fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK);
+    if (kind == JSFunction::ExtendedFinalizeKind) {
+        fun->flags |= JSFUN_EXTENDED;
+        fun->clearExtended();
+    }
     if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
         JS_ASSERT(!native);
-        JS_ASSERT(nargs == 0);
-        fun->u.i.skipmin = 0;
         fun->u.i.script_ = NULL;
         fun->setCallScope(parent);
     } else {
         fun->u.n.clasp = NULL;
         if (flags & JSFUN_TRCINFO) {
 #ifdef JS_TRACER
             JSNativeTraceInfo *trcinfo =
                 JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo *, native);
             fun->u.n.native = (Native) trcinfo->native;
-            fun->u.n.trcinfo = trcinfo;
-#else
-            fun->u.n.trcinfo = NULL;
 #endif
         } else {
             fun->u.n.native = native;
-            fun->u.n.trcinfo = NULL;
         }
         JS_ASSERT(fun->u.n.native);
     }
     fun->atom = atom;
 
     return fun;
 }
 
 JSFunction * JS_FASTCALL
 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
-                       JSObject *proto)
+                       JSObject *proto, gc::AllocKind kind)
 {
     JS_ASSERT(parent);
     JS_ASSERT(proto);
 
-    JSFunction *clone = NewFunction(cx, SkipScopeParent(parent));
+    JSFunction *clone = NewFunction(cx, SkipScopeParent(parent), kind);
     if (!clone)
         return NULL;
 
     clone->nargs = fun->nargs;
-    clone->flags = fun->flags;
+    clone->flags = fun->flags & ~JSFUN_EXTENDED;
     clone->u = fun->toFunction()->u;
     clone->atom = fun->atom;
 
+    if (kind == JSFunction::ExtendedFinalizeKind) {
+        clone->flags |= JSFUN_EXTENDED;
+        clone->clearExtended();
+    }
+
     if (clone->isInterpreted())
         clone->setCallScope(parent);
 
     if (cx->compartment == fun->compartment()) {
         /*
          * We can use the same type as the original function provided that (a)
          * its prototype is correct, and (b) its type is not a singleton. The
          * first case will hold in all compileAndGo code, and the second case
@@ -2423,86 +2449,78 @@ js_CloneFunctionObject(JSContext *cx, JS
 
             js_CallNewScriptHook(cx, clone->script(), clone);
             Debugger::onNewScript(cx, clone->script(), clone, NULL);
         }
     }
     return clone;
 }
 
-#ifdef JS_TRACER
-JS_DEFINE_CALLINFO_4(extern, FUNCTION, js_CloneFunctionObject, CONTEXT, FUNCTION, OBJECT, OBJECT, 0,
-                     nanojit::ACCSET_STORE_ANY)
-#endif
-
 /*
  * Create a new flat closure, but don't initialize the imported upvar
  * values. The tracer calls this function and then initializes the upvar
  * slots on trace.
  */
-JSObject * JS_FASTCALL
+JSFunction * JS_FASTCALL
 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
 {
     JS_ASSERT(fun->isFlatClosure());
     JS_ASSERT(JSScript::isValidOffset(fun->script()->upvarsOffset) ==
               fun->script()->bindings.hasUpvars());
     JS_ASSERT_IF(JSScript::isValidOffset(fun->script()->upvarsOffset),
                  fun->script()->upvars()->length == fun->script()->bindings.countUpvars());
 
-    JSObject *closure = CloneFunctionObject(cx, fun, scopeChain, true);
+    JSFunction *closure = CloneFunctionObject(cx, fun, scopeChain, JSFunction::ExtendedFinalizeKind);
     if (!closure)
         return closure;
 
     uint32 nslots = fun->script()->bindings.countUpvars();
     if (nslots == 0)
         return closure;
 
     Value *upvars = (Value *) cx->malloc_(nslots * sizeof(Value));
     if (!upvars)
         return NULL;
 
     closure->setFlatClosureUpvars(upvars);
     return closure;
 }
 
-JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
-                     CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
-
-JSObject *
+JSFunction *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
 {
     /*
      * Flat closures cannot yet be partial, that is, all upvars must be copied,
      * or the closure won't be flattened. Therefore they do not need to search
      * enclosing scope objects via JSOP_NAME, etc.
      *
      * FIXME: bug 545759 proposes to enable partial flat closures. Fixing this
      * bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK
      * annotations on this function's prototype and definition.
      */
     VOUCH_DOES_NOT_REQUIRE_STACK();
     JSObject *scopeChain = &cx->fp()->scopeChain();
 
-    JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
+    JSFunction *closure = js_AllocFlatClosure(cx, fun, scopeChain);
     if (!closure || !fun->script()->bindings.hasUpvars())
         return closure;
 
     Value *upvars = closure->getFlatClosureUpvars();
     uintN level = fun->script()->staticLevel;
     JSUpvarArray *uva = fun->script()->upvars();
 
     for (uint32 i = 0, n = uva->length; i < n; i++)
         upvars[i] = GetUpvar(cx, level, uva->vector[i]);
 
     return closure;
 }
 
 JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, Native native,
-                  uintN nargs, uintN attrs)
+                  uintN nargs, uintN attrs, AllocKind kind)
 {
     PropertyOp gop;
     StrictPropertyOp sop;
     JSFunction *fun;
 
     if (attrs & JSFUN_STUB_GSOPS) {
         /*
          * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
@@ -2516,17 +2534,18 @@ js_DefineFunction(JSContext *cx, JSObjec
     } else {
         gop = NULL;
         sop = NULL;
     }
 
     fun = js_NewFunction(cx, NULL, native, nargs,
                          attrs & (JSFUN_FLAGS_MASK | JSFUN_TRCINFO),
                          obj,
-                         JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL);
+                         JSID_IS_ATOM(id) ? JSID_TO_ATOM(id) : NULL,
+                         kind);
     if (!fun)
         return NULL;
 
     if (!obj->defineProperty(cx, id, ObjectValue(*fun), gop, sop, attrs & ~JSFUN_FLAGS_MASK))
         return NULL;
 
     return fun;
 }
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -87,44 +87,39 @@
 #define JSFUN_JOINABLE      0x0001  /* function is null closure that does not
                                        appear to call itself via its own name
                                        or arguments.callee */
 
 #define JSFUN_PROTOTYPE     0x0800  /* function is Function.prototype for some
                                        global object */
 
 #define JSFUN_EXPR_CLOSURE  0x1000  /* expression closure: function(x) x*x */
-                                    /* 0x2000 is JSFUN_TRCINFO:
-                                       u.n.trcinfo is non-null */
+#define JSFUN_EXTENDED      0x2000  /* structure is FunctionExtended */
 #define JSFUN_INTERPRETED   0x4000  /* use u.i if kind >= this value else u.n */
 #define JSFUN_FLAT_CLOSURE  0x8000  /* flat (aka "display") closure */
 #define JSFUN_NULL_CLOSURE  0xc000  /* null closure entrains no scope chain */
 #define JSFUN_KINDMASK      0xc000  /* encode interp vs. native and closure
                                        optimization level -- see above */
 
-struct JSFunction : public JSObject_Slots2
+namespace js { class FunctionExtended; }
+
+struct JSFunction : public JSObject
 {
-    /* Functions always have two fixed slots (FUN_CLASS_RESERVED_SLOTS). */
-
     uint16          nargs;        /* maximum number of specified arguments,
                                      reflected as f.length/f.arity */
     uint16          flags;        /* flags, see JSFUN_* below and in jsapi.h */
     union U {
         struct {
             js::Native  native;   /* native method pointer or null */
             js::Class   *clasp;   /* class of objects constructed
                                      by this function */
-            JSNativeTraceInfo *trcinfo;
         } n;
         struct Scripted {
             JSScript    *script_; /* interpreted bytecode descriptor or null;
                                      use the setter! */
-            uint16       skipmin; /* net skip amount up (toward zero) from
-                                     script_->staticLevel to nearest upvar,
-                                     including upvars in nested functions */
             JSObject    *scope;   /* scope to use when calling this function */
         } i;
         void            *nativeOrScript;
     } u;
     JSAtom          *atom;        /* name for diagnostics and decompiling */
 
     bool optimizedClosure()  const { return kind() > JSFUN_INTERPRETED; }
     bool isInterpreted()     const { return kind() >= JSFUN_INTERPRETED; }
@@ -169,44 +164,16 @@ struct JSFunction : public JSObject_Slot
      * function. The parent link for such functions points to the scope chain's
      * global object.
      */
     inline JSObject *callScope() const;
     inline void setCallScope(JSObject *obj);
 
     static inline size_t offsetOfCallScope() { return offsetof(JSFunction, u.i.scope); }
 
-    /*
-     * FunctionClass reserves two slots, which are free in JSObject::fslots
-     * without requiring dslots allocation. Null closures that can be joined to
-     * a compiler-created function object use the first one to hold a mutable
-     * methodAtom() state variable, needed for correct foo.caller handling.
-     */
-    static const uint32 JSSLOT_FUN_METHOD_ATOM = 0;
-    static const uint32 JSSLOT_FUN_METHOD_OBJ  = 1;
-
-    /* Whether this is a function cloned from a method. */
-    inline bool isClonedMethod() const;
-
-    /* For a cloned method, pointer to the object the method was cloned for. */
-    inline bool hasMethodObj(const JSObject& obj) const;
-    inline void setMethodObj(JSObject& obj);
-
-    /*
-     * Method name imputed from property uniquely assigned to or initialized,
-     * where the function does not need to be cloned to carry a scope chain or
-     * flattened upvars. This is set on both the original and cloned function.
-     */
-    JSAtom *methodAtom() const {
-        return (joinable() && getSlot(JSSLOT_FUN_METHOD_ATOM).isString())
-               ? &getSlot(JSSLOT_FUN_METHOD_ATOM).toString()->asAtom()
-               : NULL;
-    }
-    inline void setMethodAtom(JSAtom *atom);
-
     inline void setJoinable();
 
     JSScript *script() const {
         JS_ASSERT(isInterpreted());
         return u.i.script_;
     }
 
     void setScript(JSScript *script) {
@@ -229,35 +196,87 @@ struct JSFunction : public JSObject_Slot
     }
 
     static uintN offsetOfNativeOrScript() {
         JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script_));
         JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript));
         return offsetof(JSFunction, u.nativeOrScript);
     }
 
-    /* Number of extra fixed function object slots. */
-    static const uint32 CLASS_RESERVED_SLOTS = JSObject::FUN_CLASS_RESERVED_SLOTS;
-
-
     js::Class *getConstructorClass() const {
         JS_ASSERT(isNative());
         return u.n.clasp;
     }
 
     void setConstructorClass(js::Class *clasp) {
         JS_ASSERT(isNative());
         u.n.clasp = clasp;
     }
 
-    JSNativeTraceInfo *getTraceInfo() const {
-        JS_ASSERT(isNative());
-        JS_ASSERT(flags & JSFUN_TRCINFO);
-        return u.n.trcinfo;
+#if JS_BITS_PER_WORD == 32
+    static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2;
+    static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4;
+#else
+    static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4;
+    static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8;
+#endif
+
+    inline void trace(JSTracer *trc);
+
+  private:
+    inline js::FunctionExtended *toExtended();
+    inline const js::FunctionExtended *toExtended() const;
+
+    inline bool isExtended() const {
+        JS_STATIC_ASSERT(FinalizeKind != ExtendedFinalizeKind);
+        JS_ASSERT(!!(flags & JSFUN_EXTENDED) == (getAllocKind() == ExtendedFinalizeKind));
+        return !!(flags & JSFUN_EXTENDED);
     }
+
+  public:
+    /* Accessors for data stored in extended functions. */
+
+    inline void clearExtended();
+
+    inline void setNativeReserved(size_t which, const js::Value &val);
+    inline const js::Value &getNativeReserved(size_t which);
+
+    static inline size_t getFlatClosureUpvarsOffset();
+
+    inline js::Value *getFlatClosureUpvars() const;
+    inline js::Value getFlatClosureUpvar(uint32 i) const;
+    inline const js::Value &getFlatClosureUpvar(uint32 i);
+    inline void setFlatClosureUpvar(uint32 i, const js::Value &v);
+    inline void setFlatClosureUpvars(js::Value *upvars);
+
+    /* See comments in fun_finalize. */
+    inline void finalizeUpvars();
+
+    inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg,
+                                  const js::Value *args, uintN argslen);
+
+    inline JSObject *getBoundFunctionTarget() const;
+    inline const js::Value &getBoundFunctionThis() const;
+    inline const js::Value &getBoundFunctionArgument(uintN which) const;
+    inline size_t getBoundFunctionArgumentCount() const;
+
+    /* Whether this is a function cloned from a method. */
+    inline bool isClonedMethod() const;
+
+    /* For a cloned method, pointer to the object the method was cloned for. */
+    inline JSObject *methodObj() const;
+    inline void setMethodObj(JSObject& obj);
+
+    /*
+     * Method name imputed from property uniquely assigned to or initialized,
+     * where the function does not need to be cloned to carry a scope chain or
+     * flattened upvars. This is set on both the original and cloned function.
+     */
+    inline JSAtom *methodAtom() const;
+    inline void setMethodAtom(JSAtom *atom);
 };
 
 inline JSFunction *
 JSObject::toFunction()
 {
     JS_ASSERT(JS_ObjectIsFunction(NULL, this));
     return static_cast<JSFunction *>(this);
 }
@@ -284,34 +303,36 @@ JSObject::toFunction() const
     JS_FN(name, fastcall, nargs, flags)
 #endif
 
 extern JSString *
 fun_toStringHelper(JSContext *cx, JSObject *obj, uintN indent);
 
 extern JSFunction *
 js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
-               uintN flags, JSObject *parent, JSAtom *atom);
+               uintN flags, JSObject *parent, JSAtom *atom,
+               js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 extern void
 js_FinalizeFunction(JSContext *cx, JSFunction *fun);
 
 extern JSFunction * JS_FASTCALL
-js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
-                       JSObject *proto);
+js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent, JSObject *proto,
+                       js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
-extern JSObject * JS_FASTCALL
+extern JSFunction * JS_FASTCALL
 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
 
-extern JSObject *
+extern JSFunction *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
 
 extern JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, JSNative native,
-                  uintN nargs, uintN flags);
+                  uintN nargs, uintN flags,
+                  js::gc::AllocKind kind = JSFunction::FinalizeKind);
 
 /*
  * Flags for js_ValueToFunction and js_ReportIsNotFunction.
  */
 #define JSV2F_CONSTRUCT         INITIAL_CONSTRUCT
 #define JSV2F_SEARCH_STACK      0x10000
 
 extern JSFunction *
@@ -354,18 +375,71 @@ extern JSBool
 SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
 
 extern JSBool
 SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
 
 extern JSBool
 SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, js::Value *vp);
 
+/*
+ * Function extended with extra fields used by specific kinds of functions.
+ * Most functions do not have these extensions, but enough are that efficient
+ * storage is required (no malloc'ed reserved slots).
+ */
+class FunctionExtended : public JSFunction
+{
+    friend struct JSFunction;
+
+    union {
+
+        /* Reserved slots available for storage by particular native functions. */
+        Value nativeReserved[2];
+
+        /*
+         * Flat closures with one or more upvars snapshot the upvars' values
+         * into a vector of js::Values referenced from here.
+         */
+        Value *flatClosureUpvars;
+
+        /* State for original and cloned method functions. */
+        struct {
+            /* Associated method property, needed for foo.caller handling. */
+            JSAtom *property;
+
+            /* If this is a clone, the object this was cloned as a property from. */
+            JSObject *obj;
+        } methodFunction;
+
+    } extu;
+};
+
 } // namespace js
 
+inline js::FunctionExtended *
+JSFunction::toExtended()
+{
+    JS_ASSERT(isExtended());
+    return static_cast<js::FunctionExtended *>(this);
+}
+
+inline const js::FunctionExtended *
+JSFunction::toExtended() const
+{
+    JS_ASSERT(isExtended());
+    return static_cast<const js::FunctionExtended *>(this);
+}
+
+inline void
+JSFunction::clearExtended()
+{
+    JS_ASSERT(isExtended());
+    memset((char *)&this[1], 0, sizeof(js::FunctionExtended) - sizeof(JSFunction));
+}
+
 extern JSBool
 js_GetArgsValue(JSContext *cx, js::StackFrame *fp, js::Value *vp);
 
 extern JSBool
 js_GetArgsProperty(JSContext *cx, js::StackFrame *fp, jsid id, js::Value *vp);
 
 /*
  * Get the arguments object for the given frame.  If the frame is strict mode
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -62,44 +62,128 @@ JSFunction::setCallScope(JSObject *obj)
     JS_ASSERT(isInterpreted());
     u.i.scope = obj;
 }
 
 inline void
 JSFunction::setJoinable()
 {
     JS_ASSERT(isInterpreted());
-    setSlot(JSSLOT_FUN_METHOD_ATOM, js::NullValue());
     flags |= JSFUN_JOINABLE;
 }
 
 inline bool
 JSFunction::isClonedMethod() const
 {
-    return getFixedSlot(JSSLOT_FUN_METHOD_OBJ).isObject();
+    return joinable() && isExtended() && toExtended()->extu.methodFunction.obj != NULL;
+}
+
+inline JSAtom *
+JSFunction::methodAtom() const
+{
+    return (joinable() && isExtended()) ? toExtended()->extu.methodFunction.property : NULL;
 }
 
 inline void
 JSFunction::setMethodAtom(JSAtom *atom)
 {
     JS_ASSERT(joinable());
-    setSlot(JSSLOT_FUN_METHOD_ATOM, js::StringValue(atom));
+    toExtended()->extu.methodFunction.property = atom;
 }
 
-inline bool
-JSFunction::hasMethodObj(const JSObject& obj) const
+inline JSObject *
+JSFunction::methodObj() const
 {
-    return getFixedSlot(JSSLOT_FUN_METHOD_OBJ).isObject() &&
-           getFixedSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == obj;
+    JS_ASSERT(joinable());
+    return isExtended() ? toExtended()->extu.methodFunction.obj : NULL;
 }
 
 inline void
 JSFunction::setMethodObj(JSObject& obj)
 {
-    setFixedSlot(JSSLOT_FUN_METHOD_OBJ, js::ObjectValue(obj));
+    JS_ASSERT(joinable());
+    toExtended()->extu.methodFunction.obj = &obj;
+}
+
+inline void
+JSFunction::setNativeReserved(size_t which, const js::Value &val)
+{
+    JS_ASSERT(isNative());
+    JS_ASSERT(which < JS_ARRAY_LENGTH(toExtended()->extu.nativeReserved));
+    toExtended()->extu.nativeReserved[which] = val;
+}
+
+inline const js::Value &
+JSFunction::getNativeReserved(size_t which)
+{
+    JS_ASSERT(isNative());
+    JS_ASSERT(which < JS_ARRAY_LENGTH(toExtended()->extu.nativeReserved));
+    return toExtended()->extu.nativeReserved[which];
+}
+
+inline js::Value *
+JSFunction::getFlatClosureUpvars() const
+{
+    JS_ASSERT(isFlatClosure());
+    JS_ASSERT(script()->bindings.countUpvars() == script()->upvars()->length);
+    return toExtended()->extu.flatClosureUpvars;
+}
+
+inline void
+JSFunction::finalizeUpvars()
+{
+    /*
+     * Cloned function objects may be flat closures with upvars to free.
+     *
+     * We must not access JSScript here that is stored in JSFunction. The
+     * script can be finalized before the function or closure instances. So we
+     * just check if JSSLOT_FLAT_CLOSURE_UPVARS holds a private value encoded
+     * as a double. We must also ignore newborn closures that do not have the
+     * private pointer set.
+     *
+     * FIXME bug 648320 - allocate upvars on the GC heap to avoid doing it
+     * here explicitly.
+     */
+    JS_ASSERT(isFlatClosure());
+    if (isExtended() && toExtended()->extu.flatClosureUpvars)
+        js::Foreground::free_(toExtended()->extu.flatClosureUpvars);
+}
+
+inline js::Value
+JSFunction::getFlatClosureUpvar(uint32 i) const
+{
+    JS_ASSERT(i < script()->bindings.countUpvars());
+    return getFlatClosureUpvars()[i];
+}
+
+inline const js::Value &
+JSFunction::getFlatClosureUpvar(uint32 i)
+{
+    JS_ASSERT(i < script()->bindings.countUpvars());
+    return getFlatClosureUpvars()[i];
+}
+
+inline void
+JSFunction::setFlatClosureUpvar(uint32 i, const js::Value &v)
+{
+    JS_ASSERT(i < script()->bindings.countUpvars());
+    getFlatClosureUpvars()[i] = v;
+}
+
+inline void
+JSFunction::setFlatClosureUpvars(js::Value *upvars)
+{
+    JS_ASSERT(isFlatClosure());
+    toExtended()->extu.flatClosureUpvars = upvars;
+}
+
+/* static */ inline size_t
+JSFunction::getFlatClosureUpvarsOffset()
+{
+    return offsetof(js::FunctionExtended, extu.flatClosureUpvars);
 }
 
 namespace js {
 
 static JS_ALWAYS_INLINE bool
 IsFunctionObject(const js::Value &v)
 {
     return v.isObject() && v.toObject().isFunction();
@@ -244,39 +328,44 @@ SkipScopeParent(JSObject *parent)
         return NULL;
     while (parent->isScope())
         parent = parent->getParentMaybeScope();
     return parent;
 }
 
 inline JSFunction *
 CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
-                    bool ignoreSingletonClone = false)
+                    gc::AllocKind kind = JSFunction::FinalizeKind)
 {
     JS_ASSERT(parent);
     JSObject *proto;
     if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
         return NULL;
 
+    return js_CloneFunctionObject(cx, fun, parent, proto, kind);
+}
+
+inline JSFunction *
+CloneFunctionObjectIfNotSingleton(JSContext *cx, JSFunction *fun, JSObject *parent)
+{
     /*
      * For attempts to clone functions at a function definition opcode or from
      * a method barrier, don't perform the clone if the function has singleton
-     * type. CloneFunctionObject was called pessimistically, and we need to
-     * preserve the type's property that if it is singleton there is only a
-     * single object with its type in existence.
+     * type. This was called pessimistically, and we need to preserve the
+     * type's property that if it is singleton there is only a single object
+     * with its type in existence.
      */
-    if (ignoreSingletonClone && fun->hasSingletonType()) {
-        JS_ASSERT(fun->getProto() == proto);
+    if (fun->hasSingletonType()) {
         if (!fun->setParent(cx, SkipScopeParent(parent)))
             return NULL;
         fun->setCallScope(parent);
         return fun;
     }
 
-    return js_CloneFunctionObject(cx, fun, parent, proto);
+    return CloneFunctionObject(cx, fun, parent);
 }
 
 inline JSFunction *
 CloneFunctionObject(JSContext *cx, JSFunction *fun)
 {
     /*
      * Variant which makes an exact clone of fun, preserving parent and proto.
      * Calling the above version CloneFunctionObject(cx, fun, fun->getParent())
@@ -284,14 +373,15 @@ CloneFunctionObject(JSContext *cx, JSFun
      * objects so that fun->getGlobal() != fun->getProto()->getGlobal().
      * See ReparentWrapperIfFound.
      */
     JS_ASSERT(fun->getParent() && fun->getProto());
 
     if (fun->hasSingletonType())
         return fun;
 
-    return js_CloneFunctionObject(cx, fun, fun->callScope(), fun->getProto());
+    return js_CloneFunctionObject(cx, fun, fun->callScope(), fun->getProto(),
+                                  JSFunction::ExtendedFinalizeKind);
 }
 
 } /* namespace js */
 
 #endif /* jsfuninlines_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -130,17 +130,16 @@ const uint32 Arena::ThingSizes[] = {
     sizeof(JSObject_Slots4),    /* FINALIZE_OBJECT4             */
     sizeof(JSObject_Slots4),    /* FINALIZE_OBJECT4_BACKGROUND  */
     sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8             */
     sizeof(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
     sizeof(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     sizeof(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
-    sizeof(JSFunction),         /* FINALIZE_FUNCTION            */
     sizeof(JSScript),           /* FINALIZE_SCRIPT              */
     sizeof(Shape),              /* FINALIZE_SHAPE               */
     sizeof(BaseShape),          /* FINALIZE_BASE_SHAPE          */
     sizeof(types::TypeObject),  /* FINALIZE_TYPE_OBJECT         */
 #if JS_HAS_XML_SUPPORT
     sizeof(JSXML),              /* FINALIZE_XML                 */
 #endif
     sizeof(JSShortString),      /* FINALIZE_SHORT_STRING        */
@@ -158,17 +157,16 @@ const uint32 Arena::FirstThingOffsets[] 
     OFFSET(JSObject_Slots4),    /* FINALIZE_OBJECT4             */
     OFFSET(JSObject_Slots4),    /* FINALIZE_OBJECT4_BACKGROUND  */
     OFFSET(JSObject_Slots8),    /* FINALIZE_OBJECT8             */
     OFFSET(JSObject_Slots8),    /* FINALIZE_OBJECT8_BACKGROUND  */
     OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12            */
     OFFSET(JSObject_Slots12),   /* FINALIZE_OBJECT12_BACKGROUND */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16            */
     OFFSET(JSObject_Slots16),   /* FINALIZE_OBJECT16_BACKGROUND */
-    OFFSET(JSFunction),         /* FINALIZE_FUNCTION            */
     OFFSET(JSScript),           /* FINALIZE_SCRIPT              */
     OFFSET(Shape),              /* FINALIZE_SHAPE               */
     OFFSET(BaseShape),          /* FINALIZE_BASE_SHAPE          */
     OFFSET(types::TypeObject),  /* FINALIZE_TYPE_OBJECT         */
 #if JS_HAS_XML_SUPPORT
     OFFSET(JSXML),              /* FINALIZE_XML                 */
 #endif
     OFFSET(JSShortString),      /* FINALIZE_SHORT_STRING        */
@@ -383,17 +381,16 @@ FinalizeArenas(JSContext *cx, ArenaLists
       case FINALIZE_OBJECT4:
       case FINALIZE_OBJECT4_BACKGROUND:
       case FINALIZE_OBJECT8:
       case FINALIZE_OBJECT8_BACKGROUND:
       case FINALIZE_OBJECT12:
       case FINALIZE_OBJECT12_BACKGROUND:
       case FINALIZE_OBJECT16:
       case FINALIZE_OBJECT16_BACKGROUND:
-      case FINALIZE_FUNCTION:
         FinalizeTypedArenas<JSObject>(cx, al, thingKind, background);
         break;
       case FINALIZE_SCRIPT:
 	FinalizeTypedArenas<JSScript>(cx, al, thingKind, background);
         break;
       case FINALIZE_SHAPE:
 	FinalizeTypedArenas<Shape>(cx, al, thingKind, background);
         break;
@@ -1375,17 +1372,16 @@ inline void
 ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind)
 {
     JS_ASSERT(thingKind == FINALIZE_OBJECT0_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT2_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT4_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT8_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT12_BACKGROUND ||
               thingKind == FINALIZE_OBJECT16_BACKGROUND ||
-              thingKind == FINALIZE_FUNCTION            ||
               thingKind == FINALIZE_SHORT_STRING        ||
               thingKind == FINALIZE_STRING);
 
 #ifdef JS_THREADSAFE
     JS_ASSERT(!cx->runtime->gcHelperThread.sweeping());
 
     ArenaList *al = &arenaLists[thingKind];
     if (!al->head) {
@@ -1479,23 +1475,16 @@ ArenaLists::finalizeObjects(JSContext *c
     finalizeLater(cx, FINALIZE_OBJECT0_BACKGROUND);
     finalizeLater(cx, FINALIZE_OBJECT2_BACKGROUND);
     finalizeLater(cx, FINALIZE_OBJECT4_BACKGROUND);
     finalizeLater(cx, FINALIZE_OBJECT8_BACKGROUND);
     finalizeLater(cx, FINALIZE_OBJECT12_BACKGROUND);
     finalizeLater(cx, FINALIZE_OBJECT16_BACKGROUND);
 #endif
 
-    /*
-     * We must finalize Function instances after finalizing any other objects
-     * even if we use the background finalization for the latter. See comments
-     * in JSObject::finalizeUpvarsIfFlatClosure.
-     */
-    finalizeLater(cx, FINALIZE_FUNCTION);
-
 #if JS_HAS_XML_SUPPORT
     finalizeNow(cx, FINALIZE_XML);
 #endif
 }
 
 void
 ArenaLists::finalizeStrings(JSContext *cx)
 {
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -868,17 +868,16 @@ MapAllocToTraceKind(AllocKind thingKind)
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT4 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT4_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT8_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT12_BACKGROUND */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16 */
         JSTRACE_OBJECT,     /* FINALIZE_OBJECT16_BACKGROUND */
-        JSTRACE_OBJECT,     /* FINALIZE_FUNCTION */
         JSTRACE_SCRIPT,     /* FINALIZE_SCRIPT */
         JSTRACE_SHAPE,      /* FINALIZE_SHAPE */
         JSTRACE_BASE_SHAPE, /* FINALIZE_BASE_SHAPE */
         JSTRACE_TYPE_OBJECT,/* FINALIZE_TYPE_OBJECT */
 #if JS_HAS_XML_SUPPORT      /* FINALIZE_XML */
         JSTRACE_XML,
 #endif
         JSTRACE_STRING,     /* FINALIZE_SHORT_STRING */
--- a/js/src/jsgcinlines.h
+++ b/js/src/jsgcinlines.h
@@ -76,17 +76,17 @@ GetGCObjectKind(size_t numSlots)
         return FINALIZE_OBJECT16;
     return slotsToThingKind[numSlots];
 }
 
 static inline AllocKind
 GetGCObjectKind(Class *clasp)
 {
     if (clasp == &FunctionClass)
-        return FINALIZE_FUNCTION;
+        return JSFunction::FinalizeKind;
     uint32 nslots = JSCLASS_RESERVED_SLOTS(clasp);
     if (clasp->flags & JSCLASS_HAS_PRIVATE)
         nslots++;
     return GetGCObjectKind(nslots);
 }
 
 /* As for GetGCObjectKind, but for dense array allocation. */
 static inline AllocKind
@@ -113,17 +113,17 @@ GetGCObjectFixedSlotsKind(size_t numFixe
 
     JS_ASSERT(numFixedSlots < SLOTS_TO_THING_KIND_LIMIT);
     return slotsToThingKind[numFixedSlots];
 }
 
 static inline bool
 IsBackgroundAllocKind(AllocKind kind)
 {
-    JS_ASSERT(kind <= FINALIZE_FUNCTION);
+    JS_ASSERT(kind <= FINALIZE_OBJECT_LAST);
     return kind % 2 == 1;
 }
 
 static inline AllocKind
 GetBackgroundAllocKind(AllocKind kind)
 {
     JS_ASSERT(!IsBackgroundAllocKind(kind));
     return (AllocKind) (kind + 1);
@@ -149,17 +149,16 @@ GetGCKindSlots(AllocKind thingKind)
 {
     /* Using a switch in hopes that thingKind will usually be a compile-time constant. */
     switch (thingKind) {
       case FINALIZE_OBJECT0:
       case FINALIZE_OBJECT0_BACKGROUND:
         return 0;
       case FINALIZE_OBJECT2:
       case FINALIZE_OBJECT2_BACKGROUND:
-      case FINALIZE_FUNCTION:
         return 2;
       case FINALIZE_OBJECT4:
       case FINALIZE_OBJECT4_BACKGROUND:
         return 4;
       case FINALIZE_OBJECT8:
       case FINALIZE_OBJECT8_BACKGROUND:
         return 8;
       case FINALIZE_OBJECT12:
@@ -173,20 +172,30 @@ GetGCKindSlots(AllocKind thingKind)
         return 0;
     }
 }
 
 static inline size_t
 GetGCKindSlots(AllocKind thingKind, Class *clasp)
 {
     size_t nslots = GetGCKindSlots(thingKind);
+
+    /* An object's private data uses the space taken by its last fixed slot. */
     if (clasp->flags & JSCLASS_HAS_PRIVATE) {
         JS_ASSERT(nslots > 0);
         nslots--;
     }
+
+    /*
+     * Functions have a larger finalize kind than FINALIZE_OBJECT to reserve
+     * space for the extra fields in JSFunction, but have no fixed slots.
+     */
+    if (clasp == &FunctionClass)
+        nslots = 0;
+
     return nslots;
 }
 
 static inline void
 GCPoke(JSContext *cx, Value oldval)
 {
     /*
      * Since we're forcing a GC from JS_GC anyway, don't bother wasting cycles
@@ -387,17 +396,17 @@ NewGCThing(JSContext *cx, js::gc::AllocK
 
     void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize);
     return static_cast<T *>(t ? t : js::gc::ArenaLists::refillFreeList(cx, kind));
 }
 
 inline JSObject *
 js_NewGCObject(JSContext *cx, js::gc::AllocKind kind)
 {
-    JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_FUNCTION);
+    JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST);
     JSObject *obj = NewGCThing<JSObject>(cx, kind, js::gc::Arena::thingSize(kind));
     if (obj)
         obj->earlyInit();
     return obj;
 }
 
 inline JSString *
 js_NewGCString(JSContext *cx)
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -6295,14 +6295,14 @@ JS_GetTypeInferenceObjectStats(void *obj
             length++;
             if (init->kind == TypeNewScript::Initializer::DONE)
                 break;
         }
         stats->objects += sizeof(TypeNewScript) + (length * sizeof(TypeNewScript::Initializer));
     }
 
     if (object->emptyShapes)
-        stats->emptyShapes += sizeof(EmptyShape*) * gc::FINALIZE_FUNCTION_AND_OBJECT_LAST;
+        stats->emptyShapes += sizeof(EmptyShape*) * gc::FINALIZE_OBJECT_LAST;
 
     size_t bytes = object->dynamicSize();
     stats->objects += bytes;
     stats->temporary -= bytes;
 }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -4256,18 +4256,17 @@ END_SET_CASE(JSOP_SETLOCAL)
 
 BEGIN_CASE(JSOP_GETFCSLOT)
 BEGIN_CASE(JSOP_CALLFCSLOT)
 {
     JS_ASSERT(regs.fp()->isNonEvalFunctionFrame());
     uintN index = GET_UINT16(regs.pc);
     JSObject *obj = &argv[-2].toObject();
 
-    JS_ASSERT(index < obj->toFunction()->script()->bindings.countUpvars());
-    PUSH_COPY(obj->getFlatClosureUpvar(index));
+    PUSH_COPY(obj->toFunction()->getFlatClosureUpvar(index));
     TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
     if (op == JSOP_CALLFCSLOT)
         PUSH_UNDEFINED();
 }
 END_CASE(JSOP_GETFCSLOT)
 
 BEGIN_CASE(JSOP_UNUSED0)
 BEGIN_CASE(JSOP_UNUSED1)
@@ -4352,17 +4351,17 @@ BEGIN_CASE(JSOP_DEFFUN)
      * current scope via parent. We do this to enable sharing of compiled
      * functions among multiple equivalent scopes, amortizing the cost of
      * compilation over a number of executions.  Examples include XUL scripts
      * and event handlers shared among Firefox or other Mozilla app chrome
      * windows, and user-defined JS functions precompiled and then shared among
      * requests in server-side JS.
      */
     if (obj->toFunction()->callScope() != obj2) {
-        obj = CloneFunctionObject(cx, fun, obj2, true);
+        obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2);
         if (!obj)
             goto error;
         JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
     }
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
@@ -4470,31 +4469,31 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
      */
     JSFunction *fun;
     LOAD_FUNCTION(SLOTNO_LEN);
     JS_ASSERT(fun->isInterpreted());
     JS_ASSERT(!fun->isFlatClosure());
     JSObject *obj = fun;
 
     if (fun->isNullClosure()) {
-        obj = CloneFunctionObject(cx, fun, &regs.fp()->scopeChain(), true);
+        obj = CloneFunctionObjectIfNotSingleton(cx, fun, &regs.fp()->scopeChain());
         if (!obj)
             goto error;
     } else {
         JSObject *parent = GetScopeChainFast(cx, regs.fp(), JSOP_DEFLOCALFUN,
                                              JSOP_DEFLOCALFUN_LENGTH);
         if (!parent)
             goto error;
 
         if (obj->toFunction()->callScope() != parent) {
 #ifdef JS_TRACER
             if (TRACE_RECORDER(cx))
                 AbortRecording(cx, "DEFLOCALFUN for closure");
 #endif
-            obj = CloneFunctionObject(cx, fun, parent, true);
+            obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent);
             if (!obj)
                 goto error;
         }
     }
 
     JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
 
     uint32 slot = GET_SLOTNO(regs.pc);
@@ -4548,29 +4547,28 @@ BEGIN_CASE(JSOP_LAMBDA)
                  */
                 if (op2 == JSOP_INITMETHOD) {
 #ifdef DEBUG
                     const Value &lref = regs.sp[-1];
                     JS_ASSERT(lref.isObject());
                     JSObject *obj2 = &lref.toObject();
                     JS_ASSERT(obj2->isObject());
 #endif
-
-                    fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
+                    JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
                     break;
                 }
 
                 if (op2 == JSOP_SETMETHOD) {
 #ifdef DEBUG
                     op2 = js_GetOpcode(cx, script, pc2 + JSOP_SETMETHOD_LENGTH);
                     JS_ASSERT(op2 == JSOP_POP || op2 == JSOP_POPV);
 #endif
                     const Value &lref = regs.sp[-1];
                     if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
-                        fun->setMethodAtom(script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
+                        JS_ASSERT(fun->methodAtom() == script->getAtom(GET_FULL_INDEX(pc2 - regs.pc)));
                         break;
                     }
                 } else if (op2 == JSOP_CALL) {
                     /*
                      * Array.prototype.sort and String.prototype.replace are
                      * optimized as if they are special form. We know that they
                      * won't leak the joined function object in obj, therefore
                      * we don't need to clone that compiler-created function
@@ -4603,17 +4601,17 @@ BEGIN_CASE(JSOP_LAMBDA)
                 }
             }
         } else {
             parent = GetScopeChainFast(cx, regs.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
             if (!parent)
                 goto error;
         }
 
-        obj = CloneFunctionObject(cx, fun, parent, true);
+        obj = CloneFunctionObjectIfNotSingleton(cx, fun, parent);
         if (!obj)
             goto error;
     } while (0);
 
     JS_ASSERT(obj->getProto());
     JS_ASSERT_IF(script->hasGlobal(), obj->getProto() == fun->getProto());
 
     PUSH_OBJECT(*obj);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4540,41 +4540,23 @@ JSObject::updateSlotsForSpan(size_t oldS
     }
 
     if (oldSpan < newSpan)
         clearSlotRange(oldSpan, newSpan - oldSpan);
     else
         invalidateSlotRange(newSpan, oldSpan - newSpan);
 }
 
-#ifdef DEBUG
-size_t
-JSObject::numFixedSlotsFromAllocationKind(Class *clasp) const
-{
-    /*
-     * For checking that the fixed slot information in a shape is consistent
-     * with the allocation kind of this object.
-     */
-    gc::AllocKind kind = getAllocKind();
-    size_t slots = gc::GetGCKindSlots(kind);
-    if (clasp->flags & JSCLASS_HAS_PRIVATE) {
-        JS_ASSERT(slots > 0);
-        slots--;
-    }
-    return slots;
-}
-#endif
-
 bool
 JSObject::setInitialProperty(JSContext *cx, const js::Shape *shape)
 {
     JS_ASSERT(isNewborn());
     JS_ASSERT(shape->compartment() == compartment());
     JS_ASSERT(!shape->inDictionary());
-    JS_ASSERT(numFixedSlotsFromAllocationKind(shape->getObjectClass()) == shape->numFixedSlots());
+    JS_ASSERT(gc::GetGCKindSlots(getAllocKind(), shape->getObjectClass()) == shape->numFixedSlots());
 
     size_t span = shape->slotSpan();
 
     if (span) {
         size_t count = dynamicSlotsCount(shape->numFixedSlots(), span);
         if (count && !growSlots(cx, 0, count))
             return false;
     }
@@ -4590,17 +4572,17 @@ JSObject::setInitialProperty(JSContext *
 }
 
 void
 JSObject::setInitialPropertyInfallible(const js::Shape *shape)
 {
     JS_ASSERT(isNewborn());
     JS_ASSERT(shape->compartment() == compartment());
     JS_ASSERT(!shape->inDictionary());
-    JS_ASSERT(numFixedSlotsFromAllocationKind(shape->getObjectClass()) == shape->numFixedSlots());
+    JS_ASSERT(gc::GetGCKindSlots(getAllocKind(), shape->getObjectClass()) == shape->numFixedSlots());
     JS_ASSERT_IF(shape->getObjectClass()->ext.equality && !hasSingletonType(),
                  type()->hasAnyFlags(js::types::OBJECT_FLAG_SPECIAL_EQUALITY));
 
     JS_ASSERT(dynamicSlotsCount(shape->numFixedSlots(), shape->slotSpan()) == 0);
 
     shape_ = const_cast<js::Shape *>(shape);
     if (hasPrivate())
         setPrivate(NULL);
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -618,20 +618,16 @@ struct JSObject : js::gc::Cell
 
     inline bool hasPropertyTable() const;
 
     inline size_t structSize() const;
     inline size_t slotsAndStructSize() const;
 
     inline size_t numFixedSlots() const;
 
-#ifdef DEBUG
-    size_t numFixedSlotsFromAllocationKind(js::Class *clasp) const;
-#endif
-
     static const uint32 MAX_FIXED_SLOTS = 16;
 
   private:
     inline js::Value* fixedSlots() const;
   public:
 
     /* Accessors for properties. */
 
@@ -1043,60 +1039,21 @@ struct JSObject : js::gc::Cell
 
     inline const js::Value &getDateUTCTime() const;
     inline void setDateUTCTime(const js::Value &pthis);
 
     /*
      * Function-specific getters and setters.
      */
 
-  private:
     friend struct JSFunction;
 
-    /*
-     * Flat closures with one or more upvars snapshot the upvars' values into a
-     * vector of js::Values referenced from this slot.
-     */
-    static const uint32 JSSLOT_FLAT_CLOSURE_UPVARS = 0;
-
-    /*
-     * Null closures set or initialized as methods have these slots. See the
-     * "method barrier" comments and methods.
-     */
-
-    static const uint32 JSSLOT_BOUND_FUNCTION_THIS       = 0;
-    static const uint32 JSSLOT_BOUND_FUNCTION_ARGS_COUNT = 1;
-
-  public:
-    static const uint32 FUN_CLASS_RESERVED_SLOTS = 2;
-
-    static size_t getFlatClosureUpvarsOffset() {
-        return getFixedSlotOffset(JSSLOT_FLAT_CLOSURE_UPVARS);
-    }
-
     inline JSFunction *toFunction();
     inline const JSFunction *toFunction() const;
 
-    inline js::Value *getFlatClosureUpvars() const;
-    inline js::Value getFlatClosureUpvar(uint32 i) const;
-    inline const js::Value &getFlatClosureUpvar(uint32 i);
-    inline void setFlatClosureUpvar(uint32 i, const js::Value &v);
-    inline void setFlatClosureUpvars(js::Value *upvars);
-
-    /* See comments in fun_finalize. */
-    inline void finalizeUpvarsIfFlatClosure();
-
-    inline bool initBoundFunction(JSContext *cx, const js::Value &thisArg,
-                                  const js::Value *args, uintN argslen);
-
-    inline JSObject *getBoundFunctionTarget() const;
-    inline const js::Value &getBoundFunctionThis() const;
-    inline const js::Value &getBoundFunctionArgument(uintN which) const;
-    inline size_t getBoundFunctionArgumentCount() const;
-
   public:
     /*
      * Iterator-specific getters and setters.
      */
 
     static const uint32 ITER_CLASS_NFIXED_SLOTS = 1;
 
     inline js::NativeIterator *getNativeIterator() const;
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -687,79 +687,16 @@ JSObject::getDateUTCTime() const
 
 inline void 
 JSObject::setDateUTCTime(const js::Value &time)
 {
     JS_ASSERT(isDate());
     setFixedSlot(JSSLOT_DATE_UTC_TIME, time);
 }
 
-inline js::Value *
-JSObject::getFlatClosureUpvars() const
-{
-#ifdef DEBUG
-    const JSFunction *fun = toFunction();
-    JS_ASSERT(fun->isFlatClosure());
-    JS_ASSERT(fun->script()->bindings.countUpvars() == fun->script()->upvars()->length);
-#endif
-    const js::Value &slot = getFixedSlot(JSSLOT_FLAT_CLOSURE_UPVARS);
-    return (js::Value *) (slot.isUndefined() ? NULL : slot.toPrivate());
-}
-
-inline void
-JSObject::finalizeUpvarsIfFlatClosure()
-{
-    /*
-     * Cloned function objects may be flat closures with upvars to free.
-     *
-     * We must not access JSScript here that is stored in JSFunction. The
-     * script can be finalized before the function or closure instances. So we
-     * just check if JSSLOT_FLAT_CLOSURE_UPVARS holds a private value encoded
-     * as a double. We must also ignore newborn closures that do not have the
-     * private pointer set.
-     *
-     * FIXME bug 648320 - allocate upvars on the GC heap to avoid doing it
-     * here explicitly.
-     */
-    if (toFunction()->isFlatClosure()) {
-        const js::Value &v = getSlot(JSSLOT_FLAT_CLOSURE_UPVARS);
-        if (v.isDouble())
-            js::Foreground::free_(v.toPrivate());
-    }
-}
-
-inline js::Value
-JSObject::getFlatClosureUpvar(uint32 i) const
-{
-    JS_ASSERT(i < toFunction()->script()->bindings.countUpvars());
-    return getFlatClosureUpvars()[i];
-}
-
-inline const js::Value &
-JSObject::getFlatClosureUpvar(uint32 i)
-{
-    JS_ASSERT(i < toFunction()->script()->bindings.countUpvars());
-    return getFlatClosureUpvars()[i];
-}
-
-inline void
-JSObject::setFlatClosureUpvar(uint32 i, const js::Value &v)
-{
-    JS_ASSERT(i < toFunction()->script()->bindings.countUpvars());
-    getFlatClosureUpvars()[i] = v;
-}
-
-inline void
-JSObject::setFlatClosureUpvars(js::Value *upvars)
-{
-    JS_ASSERT(isFunction());
-    JS_ASSERT(toFunction()->isFlatClosure());
-    setFixedSlot(JSSLOT_FLAT_CLOSURE_UPVARS, js::PrivateValue(upvars));
-}
-
 inline js::NativeIterator *
 JSObject::getNativeIterator() const
 {
     return (js::NativeIterator *) getPrivate();
 }
 
 inline void
 JSObject::setNativeIterator(js::NativeIterator *ni)
@@ -1472,17 +1409,17 @@ InitScopeForNonNativeObject(JSContext *c
     obj->setInitialPropertyInfallible(empty);
     return true;
 }
 
 static inline bool
 CanBeFinalizedInBackground(gc::AllocKind kind, Class *clasp)
 {
 #ifdef JS_THREADSAFE
-    JS_ASSERT(kind <= gc::FINALIZE_FUNCTION);
+    JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST);
     /* If the class has no finalizer or a finalizer that is safe to call on
      * a different thread, we change the finalize kind. For example,
      * FINALIZE_OBJECT0 calls the finalizer on the main thread,
      * FINALIZE_OBJECT0_BACKGROUND calls the finalizer on the gcHelperThread.
      * IsBackgroundAllocKind is called to prevent recursively incrementing
      * the finalize kind; kind may already be a background finalize kind.
      */
     if (!gc::IsBackgroundAllocKind(kind) && !clasp->finalize)
@@ -1663,17 +1600,18 @@ NewObject(JSContext *cx, js::Class *clas
     /*
      * Allocate an object from the GC heap and initialize all its fields before
      * doing any operation that can potentially trigger GC. Functions have a
      * larger non-standard allocation size.
      *
      * The should be specialized by the template.
      */
 
-    JS_ASSERT((clasp == &FunctionClass) == (kind == gc::FINALIZE_FUNCTION));
+    JS_ASSERT_IF(clasp == &FunctionClass,
+                 kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
 
     if (CanBeFinalizedInBackground(kind, clasp))
         kind = GetBackgroundAllocKind(kind);
 
     JSObject* obj = js_NewGCObject(cx, kind);
     if (!obj)
         goto out;
 
@@ -1701,20 +1639,19 @@ template <WithProto::e withProto>
 static JS_ALWAYS_INLINE JSObject *
 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(clasp);
     return NewObject<withProto>(cx, clasp, proto, parent, kind);
 }
 
 static JS_ALWAYS_INLINE JSFunction *
-NewFunction(JSContext *cx, JSObject *parent)
+NewFunction(JSContext *cx, JSObject *parent, gc::AllocKind kind)
 {
-    JSObject *obj = NewObject<WithProto::Class>(cx, &FunctionClass, NULL, parent,
-                                                gc::FINALIZE_FUNCTION);
+    JSObject *obj = NewObject<WithProto::Class>(cx, &FunctionClass, NULL, parent, kind);
     return static_cast<JSFunction *>(obj);
 }
 
 template <WithProto::e withProto>
 static JS_ALWAYS_INLINE JSObject *
 NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
                gc::AllocKind kind)
 {
--- a/js/src/jsprobes.cpp
+++ b/js/src/jsprobes.cpp
@@ -228,17 +228,17 @@ FunctionName(JSContext *cx, const JSFunc
     return bytes->encode(cx, atom) ? bytes->ptr() : Probes::nullName;
 }
 
 static const char *
 FunctionClassname(const JSFunction *fun)
 {
     if (!fun || fun->isInterpreted())
         return Probes::nullName;
-    if (!(fun->flags & JSFUN_TRCINFO) && fun->getConstructorClass())
+    if (fun->getConstructorClass())
         return fun->getConstructorClass()->name;
     return Probes::nullName;
 }
 
 /*
  * These functions call the DTrace macros for the JavaScript USDT probes.
  * Originally this code was inlined in the JavaScript code; however since
  * a number of operations are called, these have been placed into functions
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -948,38 +948,36 @@ struct EmptyShape : public js::Shape
     }
 };
 
 /* Heap-allocated array of shapes for each object allocation size. */
 class ShapeKindArray
 {
   public:
     static const uint32 SHAPE_COUNT =
-        ((js::gc::FINALIZE_FUNCTION - js::gc::FINALIZE_OBJECT0) / 2) + 1;
+        ((js::gc::FINALIZE_OBJECT_LAST - js::gc::FINALIZE_OBJECT0) / 2) + 1;
 
     ShapeKindArray() { PodZero(this); }
 
     Shape *&get(gc::AllocKind kind) {
-        JS_ASSERT(kind >= gc::FINALIZE_OBJECT0 &&
-                  kind <= gc::FINALIZE_FUNCTION_AND_OBJECT_LAST);
+        JS_ASSERT(kind >= gc::FINALIZE_OBJECT0 && kind <= gc::FINALIZE_OBJECT_LAST);
         int i = (kind - gc::FINALIZE_OBJECT0) / 2;
         return shapes[i];
     }
 
     Shape *&getIndex(size_t i) {
         JS_ASSERT(i < SHAPE_COUNT);
         return shapes[i];
     }
 
   private:
     Shape *shapes[SHAPE_COUNT];
 
     void staticAsserts() {
         JS_STATIC_ASSERT(gc::FINALIZE_OBJECT0 % 2 == 0);
-        JS_STATIC_ASSERT(gc::FINALIZE_FUNCTION == gc::FINALIZE_OBJECT_LAST + 1);
     }
 };
 
 } /* namespace js */
 
 /* js::Shape pointer tag bit indicating a collision. */
 #define SHAPE_COLLISION                 (jsuword(1))
 #define SHAPE_REMOVED                   ((js::Shape *) SHAPE_COLLISION)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -303,17 +303,17 @@ CheckScript(JSScript *script, JSScript *
         crash::StackBuffer<sizeof(JSScript), 0x88> buf2(prev);
         JS_OPT_ASSERT(false);
     }
 }
 
 void
 CheckScriptOwner(JSScript *script, JSObject *owner)
 {
-    JS_OPT_ASSERT(script->ownerObject == owner);
+    // JS_OPT_ASSERT(script->ownerObject == owner);
     if (owner != JS_NEW_SCRIPT && owner != JS_CACHED_SCRIPT)
         JS_OPT_ASSERT(script->compartment() == owner->compartment());
 }
 
 #endif /* JS_CRASH_DIAGNOSTICS */
 
 } /* namespace js */
 
@@ -1226,20 +1226,26 @@ JSScript::NewScriptFromCG(JSContext *cx,
         if (JSScript::isValidOffset(script->upvarsOffset))
             JS_ASSERT(script->upvars()->length == script->bindings.countUpvars());
         else
             JS_ASSERT(script->bindings.countUpvars() == 0);
 #endif
         if (cg->flags & TCF_FUN_HEAVYWEIGHT)
             fun->flags |= JSFUN_HEAVYWEIGHT;
 
-        /* Watch for scripts whose functions will not be cloned. These are singletons. */
+        /*
+         * Mark functions which will only be executed once as singletons.
+         * Skip this for flat closures, which must be copied on executing.
+         */
         bool singleton =
-            cx->typeInferenceEnabled() && cg->parent && cg->parent->compiling() &&
-            cg->parent->asCodeGenerator()->checkSingletonContext();
+            cx->typeInferenceEnabled() &&
+            cg->parent &&
+            cg->parent->compiling() &&
+            cg->parent->asCodeGenerator()->checkSingletonContext() &&
+            !fun->isFlatClosure();
 
         if (!script->typeSetFunction(cx, fun, singleton))
             return NULL;
 
         fun->setScript(script);
     } else {
         /*
          * Initialize script->object, if necessary, so that the debugger has a
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2169,17 +2169,17 @@ js::str_replace(JSContext *cx, uintN arg
                  * encode large scripts.  We only handle the code patterns generated
                  * by such packers here.
                  */
                 JSScript *script = fun->script();
                 jsbytecode *pc = script->code;
 
                 Value table = UndefinedValue();
                 if (JSOp(*pc) == JSOP_GETFCSLOT) {
-                    table = rdata.lambda->getFlatClosureUpvar(GET_UINT16(pc));
+                    table = fun->getFlatClosureUpvar(GET_UINT16(pc));
                     pc += JSOP_GETFCSLOT_LENGTH;
                 }
 
                 if (table.isObject() &&
                     JSOp(*pc) == JSOP_GETARG && GET_SLOTNO(pc) == 0 &&
                     JSOp(*(pc + JSOP_GETARG_LENGTH)) == JSOP_GETELEM &&
                     JSOp(*(pc + JSOP_GETARG_LENGTH + JSOP_GETELEM_LENGTH)) == JSOP_RETURN) {
                     Class *clasp = table.toObject().getClass();
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -11455,27 +11455,30 @@ TraceRecorder::callNative(uintN argc, JS
                 set(&vp[0], w.ui2d(w.cmovi(cmp, a, b)));
                 pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
                 return RECORD_CONTINUE;
             }
         }
         break;
     }
 
+    JS_NOT_REACHED("FIXME");
+#if 0
     if (fun->flags & JSFUN_TRCINFO) {
         JSNativeTraceInfo *trcinfo = fun->getTraceInfo();
         JS_ASSERT(trcinfo && fun->u.n.native == trcinfo->native);
 
         /* Try to call a type specialized version of the native. */
         if (trcinfo->specializations) {
             RecordingStatus status = callSpecializedNative(trcinfo, argc, mode == JSOP_NEW);
             if (status != RECORD_STOP)
                 return status;
         }
     }
+#endif
 
     if (native == js_fun_apply || native == js_fun_call)
         RETURN_STOP("trying to call native apply or call");
 
     // Allocate the vp vector and emit code to root it.
     uintN vplen = 2 + argc;
     LIns* invokevp_ins = w.allocp(vplen * sizeof(Value));
 
@@ -13411,26 +13414,29 @@ TraceRecorder::stackLoad(Address addr, u
         JS_NOT_REACHED("found jsval type in an upvar type map entry");
         return NULL;
     }
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETFCSLOT()
 {
+    JS_NOT_REACHED("FIXME");
+#if 0
     JSObject& callee = cx->fp()->callee();
     LIns* callee_ins = get(&cx->fp()->calleev());
 
     LIns* upvars_ins = w.getObjPrivatizedSlot(callee_ins, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
 
     unsigned index = GET_UINT16(cx->regs().pc);
     LIns *v_ins = unbox_value(callee.getFlatClosureUpvar(index),
                               FCSlotsAddress(upvars_ins, index),
                               snapshot(BRANCH_EXIT));
     stack(0, v_ins);
+#endif
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_CALLFCSLOT()
 {
     CHECK_STATUS_A(record_JSOP_GETFCSLOT());
     stack(1, w.immiUndefined());
@@ -15378,49 +15384,49 @@ TraceRecorder::record_JSOP_LAMBDA()
 
     if (GetBlockChainFast(cx, cx->fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH))
         RETURN_STOP_A("Unable to trace creating lambda in let");
 
     LIns *proto_ins;
     CHECK_STATUS_A(getClassPrototype(JSProto_Function, proto_ins));
     LIns* scopeChain_ins = scopeChain();
     JS_ASSERT(scopeChain_ins);
-    LIns* args[] = { proto_ins, scopeChain_ins, w.nameImmpNonGC(fun), cx_ins };
-    LIns* call_ins = w.call(&js_CloneFunctionObject_ci, args);
+    //LIns* args[] = { proto_ins, scopeChain_ins, w.nameImmpNonGC(fun), cx_ins };
+
+    LIns* call_ins = NULL; // w.call(&js_CloneFunctionObject_ci, args);
     guard(false,
           w.name(w.eqp0(call_ins), "guard(js_CloneFunctionObject)"),
           OOM_EXIT);
     stack(0, call_ins);
-
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_LAMBDA_FC()
 {
     JSFunction* fun;
     fun = cx->fp()->script()->getFunction(getFullIndex());
 
     if (fun->getParent() != globalObj)
         return ARECORD_STOP;
 
     if (GetBlockChainFast(cx, cx->fp(), JSOP_LAMBDA_FC, JSOP_LAMBDA_FC_LENGTH))
         RETURN_STOP_A("Unable to trace creating lambda in let");
 
-    LIns* args[] = { scopeChain(), w.immpFunGC(fun), cx_ins };
-    LIns* closure_ins = w.call(&js_AllocFlatClosure_ci, args);
+    // LIns* args[] = { scopeChain(), w.immpFunGC(fun), cx_ins };
+    LIns* closure_ins = NULL; // w.call(&js_AllocFlatClosure_ci, args);
     guard(false,
           w.name(w.eqp(closure_ins, w.immpNull()), "guard(js_AllocFlatClosure)"),
           OOM_EXIT);
 
     JSScript *script = fun->script();
     if (script->bindings.hasUpvars()) {
         JSUpvarArray *uva = script->upvars();
-        LIns* upvars_ins = w.getObjPrivatizedSlot(closure_ins,
-                                                  JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
+        LIns* upvars_ins = NULL; // w.getObjPrivatizedSlot(closure_ins,
+                                 //                        JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
 
         for (uint32 i = 0, n = uva->length; i < n; i++) {
             Value v;
             LIns* v_ins = upvar(script, uva, i, v);
             if (!v_ins)
                 return ARECORD_STOP;
 
             box_value_into(v, v_ins, FCSlotsAddress(upvars_ins, i));
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -2651,17 +2651,17 @@ mjit::Compiler::generateMethod()
             uintN index = GET_UINT16(PC);
 
             // Load the callee's payload into a register.
             frame.pushCallee();
             RegisterID reg = frame.copyDataIntoReg(frame.peek(-1));
             frame.pop();
 
             // obj->getFlatClosureUpvars()
-            Address upvarAddress(reg, JSObject::getFlatClosureUpvarsOffset());
+            Address upvarAddress(reg, JSFunction::getFlatClosureUpvarsOffset());
             masm.loadPrivate(upvarAddress, reg);
             // push ((Value *) reg)[index]
 
             BarrierState barrier = pushAddressMaybeBarrier(Address(reg, index * sizeof(Value)),
                                                            knownPushedType(0), true);
             finishBarrier(barrier, REJOIN_GETTER, 0);
 
             if (op == JSOP_CALLFCSLOT)
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -689,17 +689,17 @@ stubs::DefFun(VMFrame &f, JSFunction *fu
      * current scope via parent. We do this to enable sharing of compiled
      * functions among multiple equivalent scopes, amortizing the cost of
      * compilation over a number of executions.  Examples include XUL scripts
      * and event handlers shared among Firefox or other Mozilla app chrome
      * windows, and user-defined JS functions precompiled and then shared among
      * requests in server-side JS.
      */
     if (obj->toFunction()->callScope() != obj2) {
-        obj = CloneFunctionObject(cx, fun, obj2, true);
+        obj = CloneFunctionObjectIfNotSingleton(cx, fun, obj2);
         if (!obj)
             THROW();
         JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
     }
 
     /*
      * ECMA requires functions defined when entering Eval code to be
      * impermanent.
@@ -1324,27 +1324,27 @@ stubs::DefLocalFun(VMFrame &f, JSFunctio
      * JSOP_DEFFUN that avoids requiring a call object for the outer function's
      * activation.
      */
     JS_ASSERT(fun->isInterpreted());
     JS_ASSERT(!fun->isFlatClosure());
     JSObject *obj = fun;
 
     if (fun->isNullClosure()) {
-        obj = CloneFunctionObject(f.cx, fun, &f.fp()->scopeChain(), true);
+        obj = CloneFunctionObjectIfNotSingleton(f.cx, fun, &f.fp()->scopeChain());
         if (!obj)
             THROWV(NULL);
     } else {
         JSObject *parent = GetScopeChainFast(f.cx, f.fp(), JSOP_DEFLOCALFUN,
                                              JSOP_DEFLOCALFUN_LENGTH);
         if (!parent)
             THROWV(NULL);
 
         if (obj->toFunction()->callScope() != parent) {
-            obj = CloneFunctionObject(f.cx, fun, parent, true);
+            obj = CloneFunctionObjectIfNotSingleton(f.cx, fun, parent);
             if (!obj)
                 THROWV(NULL);
         }
     }
 
     JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
 
     return obj;
@@ -1380,28 +1380,28 @@ stubs::RegExp(VMFrame &f, JSObject *rege
     f.regs.sp[0].setObject(*obj);
 }
 
 JSObject * JS_FASTCALL
 stubs::LambdaJoinableForInit(VMFrame &f, JSFunction *fun)
 {
     jsbytecode *nextpc = (jsbytecode *) f.scratch;
     JS_ASSERT(fun->joinable());
-    fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(nextpc)));
+    JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc)));
     return fun;
 }
 
 JSObject * JS_FASTCALL
 stubs::LambdaJoinableForSet(VMFrame &f, JSFunction *fun)
 {
     JS_ASSERT(fun->joinable());
     jsbytecode *nextpc = (jsbytecode *) f.scratch;
     const Value &lref = f.regs.sp[-1];
     if (lref.isObject() && lref.toObject().canHaveMethodBarrier()) {
-        fun->setMethodAtom(f.fp()->script()->getAtom(GET_SLOTNO(nextpc)));
+        JS_ASSERT(fun->methodAtom() == f.script()->getAtom(GET_SLOTNO(nextpc)));
         return fun;
     }
     return Lambda(f, fun);
 }
 
 JSObject * JS_FASTCALL
 stubs::LambdaJoinableForCall(VMFrame &f, JSFunction *fun)
 {
@@ -1451,17 +1451,17 @@ stubs::Lambda(VMFrame &f, JSFunction *fu
     if (fun->isNullClosure()) {
         parent = &f.fp()->scopeChain();
     } else {
         parent = GetScopeChainFast(f.cx, f.fp(), JSOP_LAMBDA, JSOP_LAMBDA_LENGTH);
         if (!parent)
             THROWV(NULL);
     }
 
-    JSObject *obj = CloneFunctionObject(f.cx, fun, parent, true);
+    JSObject *obj = CloneFunctionObjectIfNotSingleton(f.cx, fun, parent);
     if (!obj)
         THROWV(NULL);
 
     JS_ASSERT_IF(f.script()->compileAndGo, obj->getGlobal() == fun->getGlobal());
     return obj;
 }
 
 static bool JS_ALWAYS_INLINE
--- a/js/src/tracejit/Writer.cpp
+++ b/js/src/tracejit/Writer.cpp
@@ -552,17 +552,17 @@ void ValidateWriter::checkAccSet(LOpcode
              base->isop(LIR_addp);
         break;
 
       case ACCSET_FCSLOTS:
         // This check is imperfect.
         //
         // base = <const private ptr slots[JSSLOT_FLAT_CLOSURE_UPVARS]>
         // ins = {ld,st}X.fcslots base[...]
-        ok = isConstPrivatePtr(base, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
+        ok = false; // isConstPrivatePtr(base, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
         break;
 
       case ACCSET_ARGS_DATA:
         // This check is imperfect.
         //
         // base = <const private ptr slots[JSSLOT_ARGS_DATA]>
         // ins = st{i,p,d}.argsdata base[...]
         //   OR
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -2521,17 +2521,17 @@ Class DebuggerArguments_class = {
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
 };
 
 /* The getter used for each element of frame.arguments. See DebuggerFrame_getArguments. */
 static JSBool
 DebuggerArguments_getArg(JSContext *cx, uintN argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    int32 i = args.callee().getReservedSlot(0).toInt32();
+    int32 i = (int32) args.callee().toFunction()->getNativeReserved(0).toInt32();
 
     /* Check that the this value is an Arguments object. */
     if (!args.thisv().isObject()) {
         ReportObjectRequired(cx);
         return false;
     }
     JSObject *argsobj = &args.thisv().toObject();
     if (argsobj->getClass() != &DebuggerArguments_class) {
@@ -2594,26 +2594,27 @@ DebuggerFrame_getArguments(JSContext *cx
         if (!DefineNativeProperty(cx, argsobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
                                   Int32Value(fargc), NULL, NULL,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0))
         {
             return false;
         }
 
         for (int32 i = 0; i < fargc; i++) {
-            JSObject *getobj =
-                js_NewFunction(cx, NULL, DebuggerArguments_getArg, 0, 0, global, NULL);
+            JSFunction *getobj =
+                js_NewFunction(cx, NULL, DebuggerArguments_getArg, 0, 0, global, NULL,
+                               JSFunction::ExtendedFinalizeKind);
             if (!getobj ||
-                !js_SetReservedSlot(cx, getobj, 0, Int32Value(i)) ||
                 !DefineNativeProperty(cx, argsobj, INT_TO_JSID(i), UndefinedValue(),
                                       JS_DATA_TO_FUNC_PTR(PropertyOp, getobj), NULL,
                                       JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER, 0, 0))
             {
                 return false;
             }
+            getobj->setNativeReserved(0, Int32Value(i));
         }
     } else {
         argsobj = NULL;
     }
     args.rval() = ObjectOrNullValue(argsobj);
     thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS, args.rval());
     return true;
 }
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -355,19 +355,19 @@ GlobalObject::isRuntimeCodeGenEnabled(JS
         v = BooleanValue((!callbacks || !callbacks->contentSecurityPolicyAllows) ||
                          callbacks->contentSecurityPolicyAllows(cx));
     }
     return !v.isFalse();
 }
 
 JSFunction *
 GlobalObject::createConstructor(JSContext *cx, Native ctor, Class *clasp, JSAtom *name,
-                                uintN length)
+                                uintN length, gc::AllocKind kind)
 {
-    JSFunction *fun = js_NewFunction(cx, NULL, ctor, length, JSFUN_CONSTRUCTOR, this, name);
+    JSFunction *fun = js_NewFunction(cx, NULL, ctor, length, JSFUN_CONSTRUCTOR, this, name, kind);
     if (!fun)
         return NULL;
 
     /*
      * Remember the class this function is a constructor for so that we know to
      * create an object of this class when we call the constructor.
      */
     fun->setConstructorClass(clasp);
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -162,17 +162,18 @@ class GlobalObject : public ::JSObject {
   public:
     static GlobalObject *create(JSContext *cx, Class *clasp);
 
     /*
      * Create a constructor function with the specified name and length using
      * ctor, a method which creates objects with the given class.
      */
     JSFunction *
-    createConstructor(JSContext *cx, JSNative ctor, Class *clasp, JSAtom *name, uintN length);
+    createConstructor(JSContext *cx, JSNative ctor, Class *clasp, JSAtom *name, uintN length,
+                      gc::AllocKind kind = JSFunction::FinalizeKind);
 
     /*
      * Create an object to serve as [[Prototype]] for instances of the given
      * class, using |Object.prototype| as its [[Prototype]].  Users creating
      * prototype objects with particular internal structure (e.g. reserved
      * slots guaranteed to contain values of particular types) must immediately
      * complete the minimal initialization to make the returned object safe to
      * touch.
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -3864,36 +3864,33 @@ nsXPCComponents_Utils::CreateObjectIn(co
         return NS_ERROR_FAILURE;
     *rval = OBJECT_TO_JSVAL(obj);
     return NS_OK;
 }
 
 JSBool
 FunctionWrapper(JSContext *cx, uintN argc, jsval *vp)
 {
-    jsval v;
-    if (!JS_GetReservedSlot(cx, JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0, &v))
-        return JS_FALSE;
+    jsval v = js::GetFunctionNativeReserved(JSVAL_TO_OBJECT(JS_CALLEE(cx, vp)), 0);
     NS_ASSERTION(JSVAL_IS_OBJECT(v), "weird function");
 
     return JS_CallFunctionValue(cx, JS_THIS_OBJECT(cx, vp), v,
                                 argc, JS_ARGV(cx, vp), vp);
 }
 
 JSBool
 WrapCallable(JSContext *cx, JSObject *obj, jsid id, JSObject *propobj, jsval *vp)
 {
     JSFunction *fun = JS_NewFunctionById(cx, FunctionWrapper, 0, 0,
                                          JS_GetGlobalForObject(cx, obj), id);
     if (!fun)
         return JS_FALSE;
 
     JSObject *funobj = JS_GetFunctionObject(fun);
-    if (!JS_SetReservedSlot(cx, funobj, 0, OBJECT_TO_JSVAL(propobj)))
-        return JS_FALSE;
+    js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(propobj));
     *vp = OBJECT_TO_JSVAL(funobj);
     return JS_TRUE;
 }
 
 /* void makeObjectPropsNormal(jsval vobj); */
 NS_IMETHODIMP
 nsXPCComponents_Utils::MakeObjectPropsNormal(const jsval &vobj, JSContext *cx)
 {
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -1660,16 +1660,23 @@ ReportCompartmentStats(const Compartment
                                               "gc-heap/shapes"),
                        JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_SHAPE],
                        "Memory on the compartment's garbage-collected JavaScript heap that holds "
                        "shapes. A shape is an internal data structure that makes JavaScript "
                        "property accesses fast.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
+                                              "gc-heap/base-shapes"),
+                       JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_BASE_SHAPE],
+                       "Memory on the compartment's garbage-collected JavaScript heap that collates "
+                       "data common to many shapes.",
+                       callback, closure);
+
+    ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "gc-heap/type-objects"),
                        JS_GC_HEAP_KIND, stats.gcHeapKinds[JSTRACE_TYPE_OBJECT],
                        "Memory on the compartment's garbage-collected JavaScript heap that holds "
                        "type inference information.",
                        callback, closure);
 
     ReportMemoryBytes0(MakeMemoryReporterPath(pathPrefix, stats.name,
                                               "gc-heap/xml"),
--- a/js/xpconnect/src/XPCQuickStubs.cpp
+++ b/js/xpconnect/src/XPCQuickStubs.cpp
@@ -143,25 +143,23 @@ PropertyOpForwarder(JSContext *cx, uintN
     //   this = our this
     //   property op to call = callee reserved slot 0
     //   name of the property = callee reserved slot 1
 
     JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, vp));
     JSObject *obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
         return JS_FALSE;
-    jsval v;
 
-    if (!JS_GetReservedSlot(cx, callee, 0, &v))
-        return JS_FALSE;
+    jsval v = js::GetFunctionNativeReserved(callee, 0);
+
     JSObject *ptrobj = JSVAL_TO_OBJECT(v);
     Op *popp = static_cast<Op *>(JS_GetPrivate(cx, ptrobj));
 
-    if (!JS_GetReservedSlot(cx, callee, 1, &v))
-        return JS_FALSE;
+    v = js::GetFunctionNativeReserved(callee, 1);
 
     jsval argval = (argc > 0) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
     jsid id;
     if (!JS_ValueToId(cx, argval, &id))
         return JS_FALSE;
     JS_SET_RVAL(cx, vp, argval);
     return ApplyPropertyOp<Op>(cx, *popp, obj, id, vp);
 }
@@ -202,18 +200,18 @@ GeneratePropertyOp(JSContext *cx, JSObje
     if (!ptrobj)
         return JS_FALSE;
     Op *popp = new Op;
     if (!popp)
         return JS_FALSE;
     *popp = pop;
     JS_SetPrivate(cx, ptrobj, popp);
 
-    JS_SetReservedSlot(cx, funobj, 0, OBJECT_TO_JSVAL(ptrobj));
-    JS_SetReservedSlot(cx, funobj, 1, js::IdToJsval(id));
+    js::SetFunctionNativeReserved(funobj, 0, OBJECT_TO_JSVAL(ptrobj));
+    js::SetFunctionNativeReserved(funobj, 1, js::IdToJsval(id));
     return funobj;
 }
 
 static JSBool
 ReifyPropertyOps(JSContext *cx, JSObject *obj, jsid id, uintN orig_attrs,
                  JSPropertyOp getter, JSStrictPropertyOp setter,
                  JSObject **getterobjp, JSObject **setterobjp)
 {
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -67,40 +67,36 @@ xpc_CloneJSFunction(XPCCallContext &ccx,
     }
 
     // Make sure to break the prototype chain to the function object
     // we cloned to prevent its scope from leaking into the clones
     // scope.
     JS_SetPrototype(ccx, clone, scope->GetPrototypeJSFunction());
 
     // Copy the reserved slots to the clone.
-    jsval ifaceVal, memberVal;
-    if (!JS_GetReservedSlot(ccx, funobj, 0, &ifaceVal) ||
-        !JS_GetReservedSlot(ccx, funobj, 1, &memberVal))
-        return nsnull;
-
-    if (!JS_SetReservedSlot(ccx, clone, 0, ifaceVal) ||
-        !JS_SetReservedSlot(ccx, clone, 1, memberVal))
-        return nsnull;
+    jsval ifaceVal = js::GetFunctionNativeReserved(funobj, 0);
+    jsval memberVal = js::GetFunctionNativeReserved(funobj, 1);
+    js::SetFunctionNativeReserved(clone, 0, ifaceVal);
+    js::SetFunctionNativeReserved(clone, 1, memberVal);
 
     return clone;
 }
 
 // XPCNativeMember
 
 // static
 JSBool
 XPCNativeMember::GetCallInfo(XPCCallContext& ccx,
                              JSObject* funobj,
                              XPCNativeInterface** pInterface,
                              XPCNativeMember**    pMember)
 {
     funobj = js::UnwrapObject(funobj);
-    jsval ifaceVal = js::GetReservedSlot(funobj, 0);
-    jsval memberVal = js::GetReservedSlot(funobj, 1);
+    jsval ifaceVal = js::GetFunctionNativeReserved(funobj, 0);
+    jsval memberVal = js::GetFunctionNativeReserved(funobj, 1);
 
     *pInterface = (XPCNativeInterface*) JSVAL_TO_PRIVATE(ifaceVal);
     *pMember = (XPCNativeMember*) JSVAL_TO_PRIVATE(memberVal);
 
     return JS_TRUE;
 }
 
 JSBool
@@ -167,19 +163,18 @@ XPCNativeMember::Resolve(XPCCallContext&
     JSFunction *fun = JS_NewFunctionById(ccx, callback, argc, 0, parent, GetName());
     if (!fun)
         return JS_FALSE;
 
     JSObject* funobj = JS_GetFunctionObject(fun);
     if (!funobj)
         return JS_FALSE;
 
-    if (!JS_SetReservedSlot(ccx, funobj, 0, PRIVATE_TO_JSVAL(iface))||
-        !JS_SetReservedSlot(ccx, funobj, 1, PRIVATE_TO_JSVAL(this)))
-        return JS_FALSE;
+    js::SetFunctionNativeReserved(funobj, 0, PRIVATE_TO_JSVAL(iface));
+    js::SetFunctionNativeReserved(funobj, 1, PRIVATE_TO_JSVAL(this));
 
     *vp = OBJECT_TO_JSVAL(funobj);
 
     return JS_TRUE;
 }
 
 /***************************************************************************/
 // XPCNativeInterface