Bug 685688 - Don't use standard-class-style storage for the Generator class: just store the generator [[Prototype]] in a one-off slot in the global object. r=mrbkap
authorJeff Walden <jwalden@mit.edu>
Thu, 08 Sep 2011 16:36:51 -0500
changeset 78098 f96612c06d853ac5ad0626eacd3075b4c46aee03
parent 78097 ed1946447c5ea2998c826f190d3694989ccf8344
child 78099 5aa439407de852dd886dfb5f1cec6319a62d78af
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmrbkap
bugs685688
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 685688 - Don't use standard-class-style storage for the Generator class: just store the generator [[Prototype]] in a one-off slot in the global object. r=mrbkap
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsinfer.cpp
js/src/jsiter.cpp
js/src/jsproto.tbl
js/src/jsxdrapi.h
js/src/vm/GlobalObject.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1643,17 +1643,16 @@ static JSStdName standard_class_names[] 
 
 #if JS_HAS_XML_SUPPORT
     {js_InitXMLClass,           LAZY_ATOM(XMLList), CLASP(XML)},
     {js_InitXMLClass,           LAZY_ATOM(isXMLName), CLASP(XML)},
 #endif
 
 #if JS_HAS_GENERATORS
     {js_InitIteratorClasses,    EAGER_ATOM_AND_CLASP(Iterator)},
-    {js_InitIteratorClasses,    EAGER_ATOM_AND_CLASP(Generator)},
 #endif
 
     /* Typed Arrays */
     {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(ArrayBuffer),  &ArrayBufferClass},
     {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int8Array),    TYPED_ARRAY_CLASP(TYPE_INT8)},
     {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint8Array),   TYPED_ARRAY_CLASP(TYPE_UINT8)},
     {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Int16Array),   TYPED_ARRAY_CLASP(TYPE_INT16)},
     {js_InitTypedArrayClasses,  EAGER_CLASS_ATOM(Uint16Array),  TYPED_ARRAY_CLASP(TYPE_UINT16)},
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2047,17 +2047,17 @@ struct JSClass {
  * member initial value.  The "original ... value" verbiage is there because
  * in ECMA-262, global properties naming class objects are read/write and
  * deleteable, for the most part.
  *
  * Implementing this efficiently requires that global objects have classes
  * with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was
  * prevously allowed, but is now an ES5 violation and thus unsupported.
  */
-#define JSCLASS_GLOBAL_SLOT_COUNT      (JSProto_LIMIT * 3 + 7)
+#define JSCLASS_GLOBAL_SLOT_COUNT      (JSProto_LIMIT * 3 + 8)
 #define JSCLASS_GLOBAL_FLAGS                                                  \
     (JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT))
 
 /* Fast access to the original value of each standard class's prototype. */
 #define JSCLASS_CACHED_PROTO_SHIFT      (JSCLASS_HIGH_FLAGS_SHIFT + 8)
 #define JSCLASS_CACHED_PROTO_WIDTH      8
 #define JSCLASS_CACHED_PROTO_MASK       JS_BITMASK(JSCLASS_CACHED_PROTO_WIDTH)
 #define JSCLASS_HAS_CACHED_PROTO(key)   ((key) << JSCLASS_CACHED_PROTO_SHIFT)
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -3962,17 +3962,20 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       case JSOP_UNBRAND:
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GENERATOR:
         if (script->hasFunction) {
             if (script->hasGlobal()) {
-                TypeObject *object = TypeScript::StandardType(cx, script, JSProto_Generator);
+                JSObject *proto = script->global()->getOrCreateGeneratorPrototype(cx);
+                if (!proto)
+                    return false;
+                TypeObject *object = proto->getNewType(cx);
                 if (!object)
                     return false;
                 TypeScript::ReturnTypes(script)->addType(cx, Type::ObjectType(object));
             } else {
                 TypeScript::ReturnTypes(script)->addType(cx, Type::UnknownType());
             }
         }
         break;
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1106,19 +1106,18 @@ generator_trace(JSTracer *trc, JSObject 
      * this code and save someone an hour later.
      */
     MarkStackRangeConservatively(trc, gen->floatingStack, fp->formalArgsEnd());
     js_TraceStackFrame(trc, fp);
     MarkStackRangeConservatively(trc, fp->slots(), gen->regs.sp);
 }
 
 Class js::GeneratorClass = {
-    js_Generator_str,
-    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator) |
-    JSCLASS_IS_ANONYMOUS,
+    "Generator",
+    JSCLASS_HAS_PRIVATE,
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub,
     generator_finalize,
@@ -1144,25 +1143,29 @@ Class js::GeneratorClass = {
  * JSGenerator object, which contains its own StackFrame that we populate
  * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
  * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
  * if they are non-null.
  */
 JS_REQUIRES_STACK JSObject *
 js_NewGenerator(JSContext *cx)
 {
-    JSObject *obj = NewBuiltinClassInstance(cx, &GeneratorClass);
-    if (!obj)
-        return NULL;
-
     FrameRegs &stackRegs = cx->regs();
     StackFrame *stackfp = stackRegs.fp();
     JS_ASSERT(stackfp->base() == cx->regs().sp);
     JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
 
+    GlobalObject *global = stackfp->scopeChain().getGlobal();
+    JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
+    if (!proto)
+        return NULL;
+    JSObject *obj = NewNonFunction<WithProto::Given>(cx, &GeneratorClass, proto, global);
+    if (!obj)
+        return NULL;
+
     /* Load and compute stack slot counts. */
     Value *stackvp = stackfp->actualArgs() - 2;
     uintN vplen = stackfp->formalArgsEnd() - stackvp;
 
     /* Compute JSGenerator size. */
     uintN nbytes = sizeof(JSGenerator) +
                    (-1 + /* one Value included in JSGenerator */
                     vplen +
@@ -1438,32 +1441,26 @@ InitIteratorClass(JSContext *cx, GlobalO
         return false;
 
     if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
         return false;
 
     return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
 }
 
-static bool
-InitGeneratorClass(JSContext *cx, GlobalObject *global)
+bool
+GlobalObject::initGeneratorClass(JSContext *cx)
 {
 #if JS_HAS_GENERATORS
-    JSObject *proto = global->createBlankPrototype(cx, &GeneratorClass);
-    if (!proto)
-        return false;
-
-    if (!DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
+    JSObject *proto = createBlankPrototype(cx, &GeneratorClass);
+    if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
         return false;
-
-    /* This should use a non-JSProtoKey'd slot, but this is easier for now. */
-    return DefineConstructorAndPrototype(cx, global, JSProto_Generator, proto, proto);
-#else
+    setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
+#endif
     return true;
-#endif
 }
 
 static JSObject *
 InitStopIterationClass(JSContext *cx, GlobalObject *global)
 {
     JSObject *proto = global->createBlankPrototype(cx, &StopIterationClass);
     if (!proto || !proto->freeze(cx))
         return NULL;
@@ -1491,12 +1488,12 @@ js_InitIteratorClasses(JSContext *cx, JS
      * Iterator class a second time will assert.
      */
     JSObject *iter;
     if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
         return NULL;
     if (iter)
         return iter;
 
-    if (!InitIteratorClass(cx, global) || !InitGeneratorClass(cx, global))
+    if (!InitIteratorClass(cx, global) || !global->initGeneratorClass(cx))
         return NULL;
     return InitStopIterationClass(cx, global);
 }
--- a/js/src/jsproto.tbl
+++ b/js/src/jsproto.tbl
@@ -45,22 +45,16 @@
 # define XMLFILTER_INIT                 js_InitXMLFilterClass
 #else
 # define XML_INIT                       js_InitNullClass
 # define NAMESPACE_INIT                 js_InitNullClass
 # define QNAME_INIT                     js_InitNullClass
 # define XMLFILTER_INIT                 js_InitNullClass
 #endif
 
-#if JS_HAS_GENERATORS
-# define GENERATOR_INIT                 js_InitIteratorClasses
-#else
-# define GENERATOR_INIT                 js_InitNullClass
-#endif
-
 /*
  * Enumerator codes in the second column must not change -- they are part of
  * the JS XDR API.  Client modules including jsproto.tbl should consider
  * wrapping the inclusion with JS_BEGIN_EXTERN_C and JS_END_EXTERN_C.
  */
 JS_PROTO(Null,                   0,     js_InitNullClass)
 JS_PROTO(Object,                 1,     js_InitFunctionAndObjectClasses)
 JS_PROTO(Function,               2,     js_InitFunctionAndObjectClasses)
@@ -78,29 +72,27 @@ JS_PROTO(QName,                 13,     
 JS_PROTO(Error,                 14,     js_InitExceptionClasses)
 JS_PROTO(InternalError,         15,     js_InitExceptionClasses)
 JS_PROTO(EvalError,             16,     js_InitExceptionClasses)
 JS_PROTO(RangeError,            17,     js_InitExceptionClasses)
 JS_PROTO(ReferenceError,        18,     js_InitExceptionClasses)
 JS_PROTO(SyntaxError,           19,     js_InitExceptionClasses)
 JS_PROTO(TypeError,             20,     js_InitExceptionClasses)
 JS_PROTO(URIError,              21,     js_InitExceptionClasses)
-JS_PROTO(Generator,             22,     GENERATOR_INIT)
-JS_PROTO(Iterator,              23,     js_InitIteratorClasses)
-JS_PROTO(StopIteration,         24,     js_InitIteratorClasses)
-JS_PROTO(ArrayBuffer,           25,     js_InitTypedArrayClasses)
-JS_PROTO(Int8Array,             26,     js_InitTypedArrayClasses)
-JS_PROTO(Uint8Array,            27,     js_InitTypedArrayClasses)
-JS_PROTO(Int16Array,            28,     js_InitTypedArrayClasses)
-JS_PROTO(Uint16Array,           29,     js_InitTypedArrayClasses)
-JS_PROTO(Int32Array,            30,     js_InitTypedArrayClasses)
-JS_PROTO(Uint32Array,           31,     js_InitTypedArrayClasses)
-JS_PROTO(Float32Array,          32,     js_InitTypedArrayClasses)
-JS_PROTO(Float64Array,          33,     js_InitTypedArrayClasses)
-JS_PROTO(Uint8ClampedArray,     34,     js_InitTypedArrayClasses)
-JS_PROTO(Proxy,                 35,     js_InitProxyClass)
-JS_PROTO(AnyName,               36,     js_InitNullClass)
-JS_PROTO(WeakMap,               37,     js_InitWeakMapClass)
+JS_PROTO(Iterator,              22,     js_InitIteratorClasses)
+JS_PROTO(StopIteration,         23,     js_InitIteratorClasses)
+JS_PROTO(ArrayBuffer,           24,     js_InitTypedArrayClasses)
+JS_PROTO(Int8Array,             25,     js_InitTypedArrayClasses)
+JS_PROTO(Uint8Array,            26,     js_InitTypedArrayClasses)
+JS_PROTO(Int16Array,            27,     js_InitTypedArrayClasses)
+JS_PROTO(Uint16Array,           28,     js_InitTypedArrayClasses)
+JS_PROTO(Int32Array,            29,     js_InitTypedArrayClasses)
+JS_PROTO(Uint32Array,           30,     js_InitTypedArrayClasses)
+JS_PROTO(Float32Array,          31,     js_InitTypedArrayClasses)
+JS_PROTO(Float64Array,          32,     js_InitTypedArrayClasses)
+JS_PROTO(Uint8ClampedArray,     33,     js_InitTypedArrayClasses)
+JS_PROTO(Proxy,                 34,     js_InitProxyClass)
+JS_PROTO(AnyName,               35,     js_InitNullClass)
+JS_PROTO(WeakMap,               36,     js_InitWeakMapClass)
 
 #undef XML_INIT
 #undef NAMESPACE_INIT
 #undef QNAME_INIT
-#undef GENERATOR_INIT
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -217,17 +217,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 93)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 94)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -37,16 +37,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef GlobalObject_h___
 #define GlobalObject_h___
 
 #include "jsfun.h"
+#include "jsiter.h"
 #include "jsvector.h"
 
 extern JSObject *
 js_InitFunctionAndObjectClasses(JSContext *cx, JSObject *obj);
 
 namespace js {
 
 class Debugger;
@@ -82,17 +83,18 @@ class GlobalObject : public ::JSObject {
     /*
      * Count of slots to store built-in constructors, prototypes, and initial
      * visible properties for the constructors.
      */
     static const uintN STANDARD_CLASS_SLOTS  = JSProto_LIMIT * 3;
 
     /* One-off properties stored after slots for built-ins. */
     static const uintN THROWTYPEERROR          = STANDARD_CLASS_SLOTS;
-    static const uintN REGEXP_STATICS          = THROWTYPEERROR + 1;
+    static const uintN GENERATOR_PROTO         = THROWTYPEERROR + 1;
+    static const uintN REGEXP_STATICS          = GENERATOR_PROTO + 1;
     static const uintN FUNCTION_NS             = REGEXP_STATICS + 1;
     static const uintN RUNTIME_CODEGEN_ENABLED = FUNCTION_NS + 1;
     static const uintN EVAL                    = RUNTIME_CODEGEN_ENABLED + 1;
     static const uintN FLAGS                   = EVAL + 1;
     static const uintN DEBUGGERS               = FLAGS + 1;
 
     /* Total reserved-slot count for global objects. */
     static const uintN RESERVED_SLOTS = DEBUGGERS + 1;
@@ -145,16 +147,24 @@ class GlobalObject : public ::JSObject {
         // JS_ASSERT(getSlot(THROWTYPEERROR).isUndefined());
         setSlot(THROWTYPEERROR, ObjectValue(*fun));
     }
 
     JSObject *getThrowTypeError() const {
         return &getSlot(THROWTYPEERROR).toObject();
     }
 
+    JSObject *getOrCreateGeneratorPrototype(JSContext *cx) {
+        Value &v = getSlotRef(GENERATOR_PROTO);
+        if (!v.isObject() && !js_InitIteratorClasses(cx, this))
+            return NULL;
+        JS_ASSERT(v.toObject().isGenerator());
+        return &v.toObject();
+    }
+
     Value getRegExpStatics() const {
         return getSlot(REGEXP_STATICS);
     }
 
     void clear(JSContext *cx);
 
     bool isCleared() const {
         return getSlot(FLAGS).toInt32() & FLAGS_CLEARED;
@@ -171,16 +181,17 @@ class GlobalObject : public ::JSObject {
         // confidently assert this.
         // JS_ASSERT(v.isUndefined());
         // JS_ASSERT(getSlot(EVAL).isUndefined());
         setSlot(EVAL, ObjectValue(*evalobj));
     }
 
     bool getFunctionNamespace(JSContext *cx, Value *vp);
 
+    bool initGeneratorClass(JSContext *cx);
     bool initStandardClasses(JSContext *cx);
 
     typedef js::Vector<js::Debugger *, 0, js::SystemAllocPolicy> DebuggerVector;
 
     /*
      * The collection of Debugger objects debugging this global. If this global
      * is not a debuggee, this returns either NULL or an empty vector.
      */