Fix for bug 716997 (Allow new DOM bindings with 0 methods or properties). r=jst.
authorPeter Van der Beken <peterv@propagandism.org>
Fri, 14 Oct 2011 23:21:39 +0200
changeset 85591 bc777c65c5ca7cf12f605489e63371b6c07024ff
parent 85590 570dd369e02a7ad1cc48e738a052543458fdcf3b
child 85592 12a483042e9ae8100e0b69f93aab212541f52b34
push id21940
push userjdrew@mozilla.com
push dateSun, 29 Jan 2012 02:43:03 +0000
treeherdermozilla-central@ec666b4c8d84 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjst
bugs716997
milestone12.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
Fix for bug 716997 (Allow new DOM bindings with 0 methods or properties). r=jst.
js/xpconnect/src/dombindings.cpp
js/xpconnect/src/dombindings.h
js/xpconnect/src/dombindingsgen.py
--- a/js/xpconnect/src/dombindings.cpp
+++ b/js/xpconnect/src/dombindings.cpp
@@ -59,16 +59,18 @@ namespace dom {
 namespace binding {
 
 
 static jsid s_constructor_id = JSID_VOID;
 static jsid s_prototype_id = JSID_VOID;
 
 static jsid s_length_id = JSID_VOID;
 
+static jsid s_VOID_id = JSID_VOID;
+
 bool
 DefineStaticJSVal(JSContext *cx, jsid &id, const char *string)
 {
     if (JSString *str = ::JS_InternString(cx, string)) {
         id = INTERNED_STRING_TO_JSID(cx, str);
         return true;
     }
     return false;
@@ -239,16 +241,30 @@ Unwrap(JSContext *cx, jsval v, NoType **
 // enum value in a reserved slot in every DOM prototype object. The value starts off as USE_CACHE.
 // If a property of a DOM prototype object is set to a different value, we set the value to
 // CHECK_CACHE. The next time we try to access the value of a property on that DOM prototype
 // object we check if all the DOM properties on that DOM prototype object still match the real DOM
 // properties. If they do we set the value to USE_CACHE again, if they're not we set the value to
 // DONT_USE_CACHE. If the value is USE_CACHE we do the fast lookup.
 
 template<class LC>
+typename ListBase<LC>::Properties ListBase<LC>::sProtoProperties[] = {
+    { s_VOID_id, NULL, NULL }
+};
+template<class LC>
+size_t ListBase<LC>::sProtoPropertiesCount = 0;
+
+template<class LC>
+typename ListBase<LC>::Methods ListBase<LC>::sProtoMethods[] = {
+    { s_VOID_id, NULL, 0 }
+};
+template<class LC>
+size_t ListBase<LC>::sProtoMethodsCount = 0;
+
+template<class LC>
 ListBase<LC> ListBase<LC>::instance;
 
 bool
 DefineConstructor(JSContext *cx, JSObject *obj, DefineInterface aDefine, nsresult *aResult)
 {
     bool enabled;
     bool defined = !!aDefine(cx, XPCWrappedNativeScope::FindInJSObjectScope(cx, obj), &enabled);
     NS_ASSERTION(!defined || enabled,
@@ -474,28 +490,28 @@ ListBase<LC>::getPrototype(JSContext *cx
     if (!proto)
         return NULL;
 
     JSObject *global = scope->GetGlobalJSObject();
     interfacePrototype = JS_NewObject(cx, Jsvalify(&sInterfacePrototypeClass), proto, global);
     if (!interfacePrototype)
         return NULL;
 
-    for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) {
+    for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
         JS_ASSERT(sProtoProperties[n].getter);
         jsid id = sProtoProperties[n].id;
         uintN attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
         if (!sProtoProperties[n].setter)
             attrs |= JSPROP_READONLY;
         if (!JS_DefinePropertyById(cx, interfacePrototype, id, JSVAL_VOID,
                                    sProtoProperties[n].getter, sProtoProperties[n].setter, attrs))
             return NULL;
     }
 
-    for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) {
+    for (size_t n = 0; n < sProtoMethodsCount; ++n) {
         jsid id = sProtoMethods[n].id;
         JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native, sProtoMethods[n].nargs,
                                              0, js::GetObjectParent(interfacePrototype), id);
         if (!fun)
             return NULL;
         JSObject *funobj = JS_GetFunctionObject(fun);
         if (!JS_DefinePropertyById(cx, interfacePrototype, id, OBJECT_TO_JSVAL(funobj),
                                    NULL, NULL, JSPROP_ENUMERATE))
@@ -892,30 +908,30 @@ ListBase<LC>::has(JSContext *cx, JSObjec
     return ok;
 }
 
 template<class LC>
 bool
 ListBase<LC>::protoIsClean(JSContext *cx, JSObject *proto, bool *isClean)
 {
     JSPropertyDescriptor desc;
-    for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) {
+    for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
         jsid id = sProtoProperties[n].id;
         if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc))
             return false;
         JSStrictPropertyOp setter =
             sProtoProperties[n].setter ? sProtoProperties[n].setter : InvalidateProtoShape_set;
         if (desc.obj != proto || desc.getter != sProtoProperties[n].getter ||
             desc.setter != setter) {
             *isClean = false;
             return true;
         }
     }
 
-    for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) {
+    for (size_t n = 0; n < sProtoMethodsCount; ++n) {
         jsid id = sProtoMethods[n].id;
         if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc))
             return false;
         if (desc.obj != proto || desc.getter || JSVAL_IS_PRIMITIVE(desc.value) ||
             n >= js::GetObjectSlotSpan(proto) || js::GetObjectSlot(proto, n + 1) != desc.value ||
             !JS_IsNativeFunction(JSVAL_TO_OBJECT(desc.value), sProtoMethods[n].native)) {
             *isClean = false;
             return true;
@@ -946,29 +962,29 @@ ListBase<LC>::shouldCacheProtoShape(JSCo
 }
 
 template<class LC>
 bool
 ListBase<LC>::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
 {
     JS_ASSERT(xpc::WrapperFactory::IsXrayWrapper(proxy));
 
-    for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) {
+    for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
         if (id == sProtoProperties[n].id) {
             desc->attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
             if (!sProtoProperties[n].setter)
                 desc->attrs |= JSPROP_READONLY;
             desc->obj = proxy;
             desc->setter = sProtoProperties[n].setter;
             desc->getter = sProtoProperties[n].getter;
             return true;
         }
     }
 
-    for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) {
+    for (size_t n = 0; n < sProtoMethodsCount; ++n) {
         if (id == sProtoMethods[n].id) {
             JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native,
                                                  sProtoMethods[n].nargs, 0, proxy, id);
             if (!fun)
                 return false;
             JSObject *funobj = JS_GetFunctionObject(fun);
             desc->value.setObject(*funobj);
             desc->attrs = JSPROP_ENUMERATE;
@@ -1002,26 +1018,26 @@ ListBase<LC>::nativeGet(JSContext *cx, J
     }
     else {
 #ifdef DEBUG
         bool isClean;
         JS_ASSERT(protoIsClean(cx, proto, &isClean) && isClean);
 #endif
     }
 
-    for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) {
+    for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
         if (id == sProtoProperties[n].id) {
             *found = true;
             if (!vp)
                 return true;
 
             return sProtoProperties[n].getter(cx, proxy, id, vp);
         }
     }
-    for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) {
+    for (size_t n = 0; n < sProtoMethodsCount; ++n) {
         if (id == sProtoMethods[n].id) {
             *found = true;
             if (!vp)
                 return true;
 
             *vp = js::GetObjectSlot(proto, n + 1);
             JS_ASSERT(JS_IsNativeFunction(&vp->toObject(), sProtoMethods[n].native));
             return true;
--- a/js/xpconnect/src/dombindings.h
+++ b/js/xpconnect/src/dombindings.h
@@ -178,17 +178,19 @@ private:
     };
     struct Methods {
         jsid &id;
         JSNative native;
         uintN nargs;
     };
 
     static Properties sProtoProperties[];
+    static size_t sProtoPropertiesCount;
     static Methods sProtoMethods[];
+    static size_t sProtoMethodsCount;
 
     static JSObject *ensureExpandoObject(JSContext *cx, JSObject *obj);
 
     static js::Shape *getProtoShape(JSObject *obj);
     static void setProtoShape(JSObject *obj, js::Shape *shape);
 
     static JSBool length_getter(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 
--- a/js/xpconnect/src/dombindingsgen.py
+++ b/js/xpconnect/src/dombindingsgen.py
@@ -438,17 +438,17 @@ listTemplate = (
 "    0,\n"
 "    JS_PropertyStub,        /* addProperty */\n"
 "    JS_PropertyStub,        /* delProperty */\n"
 "    JS_PropertyStub,        /* getProperty */\n"
 "    JS_StrictPropertyStub,  /* setProperty */\n"
 "    JS_EnumerateStub,\n"
 "    JS_ResolveStub,\n"
 "    JS_ConvertStub,\n"
-"    NULL,                   /* finalize */\n"
+"    JS_FinalizeStub,\n"
 "    NULL,                   /* reserved0 */\n"
 "    NULL,                   /* checkAccess */\n"
 "    NULL,                   /* call */\n"
 "    NULL,                   /* construct */\n"
 "    NULL,                   /* xdrObject */\n"
 "    interface_hasInstance\n"
 "};\n"
 "\n")
@@ -522,26 +522,37 @@ nameSetterTemplate = (
 "template<>\n"
 "bool\n"
 "${name}Wrapper::setNamedItem(JSContext *cx, ${nativeClass} *list, const nsAString& index, ${nameSetterType} item)\n"
 "{\n"
 "${nameSet}"
 "}\n"
 "\n")
 
-listTemplateFooter = (
+propertiesTemplate = (
 "template<>\n"
 "${name}Wrapper::Properties ${name}Wrapper::sProtoProperties[] = {\n"
 "${properties}\n"
 "};\n"
-"\n""template<>\n"
+"\n"
+"template<>\n"
+"size_t ${name}Wrapper::sProtoPropertiesCount = ArrayLength(${name}Wrapper::sProtoProperties);\n"
+"\n")
+
+methodsTemplate = (
+"template<>\n"
 "${name}Wrapper::Methods ${name}Wrapper::sProtoMethods[] = {\n"
 "${methods}\n"
 "};\n"
 "\n"
+"template<>\n"
+"size_t ${name}Wrapper::sProtoMethodsCount = ArrayLength(${name}Wrapper::sProtoMethods);\n"
+"\n")
+
+listTemplateFooter = (
 "template class ListBase<${name}Class>;\n"
 "\n"
 "JSObject*\n"
 "${name}::create(JSContext *cx, XPCWrappedNativeScope *scope, ${nativeClass} *list, nsWrapperCache *cache, bool *triedToWrap)\n"
 "{\n"
 "    return ${name}Wrapper::create(cx, scope, list, cache, triedToWrap);\n"
 "}\n"
 "\n"
@@ -713,17 +724,21 @@ def writeStubFile(filename, config, inte
                 isMethod = (member.kind == 'method')
                 assert isAttr or isMethod
 
                 if isMethod:
                     methodsList.append(writeMethodStub(f, clazz.name, member))
                 else:
                     propertiesList.append(writeAttrStubs(f, clazz.name, member))
 
-            f.write(string.Template(listTemplateFooter).substitute(clazz, methods=",\n".join(methodsList), properties=",\n".join(propertiesList)))
+            if len(propertiesList) > 0:
+                f.write(string.Template(propertiesTemplate).substitute(clazz, properties=",\n".join(propertiesList)))
+            if len(methodsList) > 0:
+                f.write(string.Template(methodsTemplate).substitute(clazz, methods=",\n".join(methodsList)))
+            f.write(string.Template(listTemplateFooter).substitute(clazz))
             
         f.write("// Register prototypes\n\n")
 
         f.write("void\n"
                 "Register(nsDOMClassInfoData *aData)\n"
                 "{\n"
                 "#define REGISTER_PROTO(_dom_class) \\\n"
                 "    aData[eDOMClassInfo_##_dom_class##_id].mDefineDOMInterface = _dom_class##Wrapper::getPrototype\n"