Part 1 of fix for bug 459452 (Add support for optional arg count for IDL methods): add support for [optional_argc] to xpidl and XPConnect. r=sicking.
authorJohnny Stenback <jst@mozilla.com>
Mon, 10 Aug 2009 16:41:07 +0200
changeset 34225 cfa396fc6a5da14ee54ab001b08961b82328d291
parent 34224 57e42052ea96b886eaa8149ae74d273485726b2a
child 34226 aeb6801640e6554765f062fc393b2d5c57c1f49c
push idunknown
push userunknown
push dateunknown
reviewerssicking
bugs459452
milestone1.9.3a1pre
Part 1 of fix for bug 459452 (Add support for optional arg count for IDL methods): add support for [optional_argc] to xpidl and XPConnect. r=sicking.
js/src/xpconnect/src/xpcwrappednative.cpp
xpcom/idl-parser/xpidl.py
xpcom/reflect/xptinfo/public/xptinfo.h
xpcom/typelib/xpidl/xpidl_header.c
xpcom/typelib/xpidl/xpidl_typelib.c
xpcom/typelib/xpidl/xpidl_util.c
xpcom/typelib/xpt/public/xpt_struct.h
xpcom/typelib/xpt/tools/xpt_dump.c
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -2222,16 +2222,18 @@ XPCWrappedNative::CallMethod(XPCCallCont
 
     JSBool retval = JS_FALSE;
 
     nsXPTCVariant* dispatchParams = nsnull;
     uint8 i;
     const nsXPTMethodInfo* methodInfo;
     uint8 requiredArgs;
     uint8 paramCount;
+    uint8 wantsOptArgc;
+    uint8 optArgcIndex = PR_UINT8_MAX;
     jsval src;
     nsresult invokeResult;
     nsID param_iid;
     uintN err;
     nsIXPCSecurityManager* sm;
     JSBool foundDependentParam;
 
     XPCJSRuntime* rt = ccx.GetRuntime();
@@ -2334,37 +2336,43 @@ XPCWrappedNative::CallMethod(XPCCallCont
     }
 
     if(NS_FAILED(ifaceInfo->GetMethodInfo(vtblIndex, &methodInfo)))
     {
         Throw(NS_ERROR_XPC_CANT_GET_METHOD_INFO, ccx);
         goto done;
     }
 
+    wantsOptArgc = methodInfo->WantsOptArgc() ? 1 : 0;
+
     // XXX ASSUMES that retval is last arg. The xpidl compiler ensures this.
     paramCount = methodInfo->GetParamCount();
     requiredArgs = paramCount;
     if(paramCount && methodInfo->GetParam(paramCount-1).IsRetval())
         requiredArgs--;
-    if(argc < requiredArgs)
+
+    if(argc < requiredArgs || wantsOptArgc)
     {
+        if(wantsOptArgc)
+            optArgcIndex = requiredArgs;
+
         // skip over any optional arguments
         while(requiredArgs && methodInfo->GetParam(requiredArgs-1).IsOptional())
-          requiredArgs--;
+            requiredArgs--;
 
         if(argc < requiredArgs) {
             Throw(NS_ERROR_XPC_NOT_ENOUGH_ARGS, ccx);
             goto done;
         }
     }
 
     // setup variant array pointer
-    if(paramCount > PARAM_BUFFER_COUNT)
+    if(paramCount + wantsOptArgc > PARAM_BUFFER_COUNT)
     {
-        if(!(dispatchParams = new nsXPTCVariant[paramCount]))
+        if(!(dispatchParams = new nsXPTCVariant[paramCount + wantsOptArgc]))
         {
             JS_ReportOutOfMemory(ccx);
             goto done;
         }
     }
     else
         dispatchParams = paramBuffer;
 
@@ -2685,21 +2693,39 @@ XPCWrappedNative::CallMethod(XPCCallCont
                 {
                     ThrowBadParam(err, i, ccx);
                     goto done;
                 }
             }
         }
     }
 
+    // Fill in the optional_argc argument
+    if(wantsOptArgc)
+    {
+        nsXPTCVariant* dp = &dispatchParams[optArgcIndex];
+
+        if(optArgcIndex != paramCount)
+        {
+            // The method has a return value, the return value must be
+            // last so push it out one so that we'll have room to
+            // insert the optional argc argument.
+            dispatchParams[paramCount] = *dp;
+        }
+
+        dp->ClearFlags();
+        dp->type = nsXPTType::T_U8;
+        dp->val.u8 = argc - requiredArgs;
+    }
 
     // do the invoke
     {
         AutoJSSuspendNonMainThreadRequest req(ccx.GetJSContext());
-        invokeResult = NS_InvokeByIndex(callee, vtblIndex, paramCount,
+        invokeResult = NS_InvokeByIndex(callee, vtblIndex,
+                                        paramCount + wantsOptArgc,
                                         dispatchParams);
     }
 
     xpcc->SetLastResult(invokeResult);
 
     if(NS_FAILED(invokeResult))
     {
         ThrowBadResult(invokeResult, ccx);
@@ -2708,22 +2734,27 @@ XPCWrappedNative::CallMethod(XPCCallCont
     else if(JS_IsExceptionPending(ccx))
     {
         goto done;
     }
 
     // now we iterate through the native params to gather and convert results
     for(i = 0; i < paramCount; i++)
     {
+        uint8 dispatchParamIndex = i;
+
+        if (i >= optArgcIndex)
+            dispatchParamIndex++;
+
         const nsXPTParamInfo& paramInfo = methodInfo->GetParam(i);
         if(!paramInfo.IsOut() && !paramInfo.IsDipper())
             continue;
 
         const nsXPTType& type = paramInfo.GetType();
-        nsXPTCVariant* dp = &dispatchParams[i];
+        nsXPTCVariant* dp = &dispatchParams[dispatchParamIndex];
         jsval v = JSVAL_NULL;
         AUTO_MARK_JSVAL(ccx, &v);
         JSUint32 array_count;
         nsXPTType datum_type;
         PRBool isArray = type.IsArray();
         PRBool isSizedString = isArray ?
                 JS_FALSE :
                 type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
@@ -2816,17 +2847,22 @@ XPCWrappedNative::CallMethod(XPCCallCont
     retval = JS_TRUE;
 done:
     // iterate through the params (again!) and clean up
     // any alloc'd stuff and release wrappers of params
     if(dispatchParams)
     {
         for(i = 0; i < paramCount; i++)
         {
-            nsXPTCVariant* dp = &dispatchParams[i];
+            uint8 dispatchParamIndex = i;
+
+            if (i >= optArgcIndex)
+                dispatchParamIndex++;
+
+            nsXPTCVariant* dp = &dispatchParams[dispatchParamIndex];
             void* p = dp->val.p;
             if(!p)
                 continue;
 
             if(dp->IsValArray())
             {
                 // going to have to cleanup the array and perhaps its contents
                 if(dp->IsValAllocated() || dp->IsValInterface())
--- a/xpcom/idl-parser/xpidl.py
+++ b/xpcom/idl-parser/xpidl.py
@@ -716,16 +716,17 @@ class Attribute(object):
         return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
                                           self.type, self.name)
 
 class Method(object):
     kind = 'method'
     noscript = False
     notxpcom = False
     binaryname = None
+    optional_argc = False
 
     def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
         self.type = type
         self.name = name
         self.attlist = attlist
         self.params = paramlist
         self.location = location
         self.doccomments = doccomments
@@ -742,16 +743,18 @@ class Method(object):
 
             if value is not None:
                 raise IDLError("Unexpected attribute value", aloc)
 
             if name == 'noscript':
                 self.noscript = True
             elif name == 'notxpcom':
                 self.notxpcom = True
+            elif name == 'optional_argc':
+                self.optional_argc = True
             else:
                 raise IDLError("Unexpected attribute '%s'", aloc)
 
         self.namemap = NameMap()
         for p in paramlist:
             self.namemap.set(p)
 
     def resolve(self, iface):
--- a/xpcom/reflect/xptinfo/public/xptinfo.h
+++ b/xpcom/reflect/xptinfo/public/xptinfo.h
@@ -176,16 +176,17 @@ public:
     nsXPTMethodInfo(const XPTMethodDescriptor& desc)
         {*(XPTMethodDescriptor*)this = desc;}
 
     PRBool IsGetter()      const {return 0 != (XPT_MD_IS_GETTER(flags) );}
     PRBool IsSetter()      const {return 0 != (XPT_MD_IS_SETTER(flags) );}
     PRBool IsNotXPCOM()    const {return 0 != (XPT_MD_IS_NOTXPCOM(flags));}
     PRBool IsConstructor() const {return 0 != (XPT_MD_IS_CTOR(flags)   );}
     PRBool IsHidden()      const {return 0 != (XPT_MD_IS_HIDDEN(flags) );}
+    PRBool WantsOptArgc()  const {return 0 != (XPT_MD_WANTS_OPT_ARGC(flags));}
     const char* GetName()  const {return name;}
     PRUint8 GetParamCount()  const {return num_args;}
     /* idx was index before I got _sick_ of the warnings on Unix, sorry jband */
     const nsXPTParamInfo GetParam(PRUint8 idx) const
         {
             NS_PRECONDITION(idx < GetParamCount(),"bad arg");
             return params[idx];
         }
--- a/xpcom/typelib/xpidl/xpidl_header.c
+++ b/xpcom/typelib/xpidl/xpidl_header.c
@@ -1034,16 +1034,18 @@ forward_dcl(TreeState *state)
 static gboolean
 write_method_signature(IDL_tree method_tree, FILE *outfile, int mode,
                        const char *className)
 {
     struct _IDL_OP_DCL *op = &IDL_OP_DCL(method_tree);
     gboolean no_generated_args = TRUE;
     gboolean op_notxpcom =
         (IDL_tree_property_get(op->ident, "notxpcom") != NULL);
+    gboolean op_opt_argc =
+        (IDL_tree_property_get(op->ident, "optional_argc") != NULL);
     const char *name;
     const char *binaryname;
     IDL_tree iter;
 
     if (mode == AS_DECL) {
         if (IDL_tree_property_get(op->ident, "deprecated"))
             fputs("NS_DEPRECATED ", outfile);
         if (is_method_scriptable(method_tree, op->ident))
@@ -1090,16 +1092,29 @@ write_method_signature(IDL_tree method_t
                   outfile);
         }
         if ((IDL_LIST(iter).next ||
              (!op_notxpcom && op->op_type_spec) || op->f_varargs))
             fputs(", ", outfile);
         no_generated_args = FALSE;
     }
 
+    if (op_opt_argc) {
+        if ((op_notxpcom || !op->op_type_spec) && !op->f_varargs)
+            fputs(", ", outfile);
+
+        if (mode == AS_DECL || mode == AS_IMPL)
+            fputs("PRUint8 ", outfile);
+
+        fputs("_argc", outfile);
+
+        if ((!op_notxpcom && op->op_type_spec) || op->f_varargs)
+            fputs(", ", outfile);
+    }
+
     /* make IDL return value into trailing out argument */
     if (op->op_type_spec && !op_notxpcom) {
         IDL_tree fake_param = IDL_param_dcl_new(IDL_PARAM_OUT,
                                                 op->op_type_spec,
                                                 IDL_ident_new("_retval"));
         if (!fake_param)
             return FALSE;
         if (mode == AS_DECL || mode == AS_IMPL) {
--- a/xpcom/typelib/xpidl/xpidl_typelib.c
+++ b/xpcom/typelib/xpidl/xpidl_typelib.c
@@ -1078,16 +1078,18 @@ typelib_op_dcl(TreeState *state)
     struct _IDL_OP_DCL *op = &IDL_OP_DCL(state->tree);
     IDL_tree iter;
     uint16 num_args = 0;
     uint8 op_flags = 0;
     gboolean op_notxpcom = (IDL_tree_property_get(op->ident, "notxpcom")
                             != NULL);
     gboolean op_noscript = (IDL_tree_property_get(op->ident, "noscript")
                             != NULL);
+    gboolean op_opt_argc = (IDL_tree_property_get(op->ident, "optional_argc")
+                            != NULL);
 
     if (!verify_method_declaration(state->tree))
         return FALSE;
 
     if (!XPT_InterfaceDescriptorAddMethods(ARENA(state), id, 1))
         return FALSE;
 
     meth = &id->method_descriptors[NEXT_METH(state)];
@@ -1096,16 +1098,18 @@ typelib_op_dcl(TreeState *state)
         num_args++;             /* count params */
     if (op->op_type_spec && !op_notxpcom)
         num_args++;             /* fake param for _retval */
 
     if (op_noscript)
         op_flags |= XPT_MD_HIDDEN;
     if (op_notxpcom)
         op_flags |= XPT_MD_NOTXPCOM;
+    if (op_opt_argc)
+        op_flags |= XPT_MD_OPT_ARGC;
 
     /* XXXshaver constructor? */
 
 #ifdef DEBUG_shaver_method
     fprintf(stdout, "DBG: adding method %s (nargs %d)\n",
             IDL_IDENT(op->ident).str, num_args);
 #endif
     if (!XPT_FillMethodDescriptor(ARENA(state), meth, op_flags, 
--- a/xpcom/typelib/xpidl/xpidl_util.c
+++ b/xpcom/typelib/xpidl/xpidl_util.c
@@ -714,17 +714,17 @@ verify_method_declaration(IDL_tree metho
          * arguments are marked as optional or retval.
          */
         if (IDL_tree_property_get(simple_decl, "optional") != NULL) {
             hasoptional = PR_TRUE;
         }
         else if (hasoptional && IDL_tree_property_get(simple_decl, "retval") == NULL) {
             IDL_tree_error(method_tree,
                            "non-optional non-retval parameter used after one marked [optional]");
-                return FALSE;
+            return FALSE;
         }
 
         /*
          * inout is not allowed with "domstring", "UTF8String", "CString" 
          * and "AString" types
          */
         if (IDL_PARAM_DCL(param).attr == IDL_PARAM_INOUT &&
             UP_IS_NATIVE(param_type) &&
@@ -763,16 +763,24 @@ verify_method_declaration(IDL_tree metho
          * Run additional error checks on the parameter type if targetting an 
          * older version of XPConnect.
          */
 
         if (!verify_type_fits_version(param_type, method_tree))
             return FALSE;
         
     }
+
+    if (IDL_tree_property_get(op->ident, "optional_argc") != NULL &&
+        !hasoptional) {
+        IDL_tree_error(method_tree,
+                       "[optional_argc] method must contain [optional] "
+                       "arguments");
+        return FALSE;
+    }
     
     /* XXX q: can return type be nsid? */
     /* Native return type? */
     if (scriptable_method &&
         op->op_type_spec != NULL && UP_IS_NATIVE(op->op_type_spec) &&
         IDL_tree_property_get(op->op_type_spec, "nsid") == NULL &&
         IDL_tree_property_get(op->op_type_spec, "domstring") == NULL &&
         IDL_tree_property_get(op->op_type_spec, "utf8string") == NULL &&
--- a/xpcom/typelib/xpt/public/xpt_struct.h
+++ b/xpcom/typelib/xpt/public/xpt_struct.h
@@ -480,23 +480,25 @@ struct XPTMethodDescriptor {
 };
 
 /* flag bits -- jband and fur were right, and I was miserably wrong */
 #define XPT_MD_GETTER   0x80
 #define XPT_MD_SETTER   0x40
 #define XPT_MD_NOTXPCOM 0x20
 #define XPT_MD_CTOR     0x10
 #define XPT_MD_HIDDEN   0x08
-#define XPT_MD_FLAGMASK 0xf8
+#define XPT_MD_OPT_ARGC 0x04
+#define XPT_MD_FLAGMASK 0xfc
 
-#define XPT_MD_IS_GETTER(flags)     (flags & XPT_MD_GETTER)
-#define XPT_MD_IS_SETTER(flags)     (flags & XPT_MD_SETTER)
-#define XPT_MD_IS_NOTXPCOM(flags)   (flags & XPT_MD_NOTXPCOM)
-#define XPT_MD_IS_CTOR(flags)       (flags & XPT_MD_CTOR)
-#define XPT_MD_IS_HIDDEN(flags)     (flags & XPT_MD_HIDDEN)
+#define XPT_MD_IS_GETTER(flags)      (flags & XPT_MD_GETTER)
+#define XPT_MD_IS_SETTER(flags)      (flags & XPT_MD_SETTER)
+#define XPT_MD_IS_NOTXPCOM(flags)    (flags & XPT_MD_NOTXPCOM)
+#define XPT_MD_IS_CTOR(flags)        (flags & XPT_MD_CTOR)
+#define XPT_MD_IS_HIDDEN(flags)      (flags & XPT_MD_HIDDEN)
+#define XPT_MD_WANTS_OPT_ARGC(flags) (flags & XPT_MD_OPT_ARGC)
 
 extern XPT_PUBLIC_API(PRBool)
 XPT_FillMethodDescriptor(XPTArena *arena, 
                          XPTMethodDescriptor *meth, PRUint8 flags, char *name,
                          PRUint8 num_args);
 
 /*
  * Annotation records are variable-size records used to store secondary 
--- a/xpcom/typelib/xpt/tools/xpt_dump.c
+++ b/xpcom/typelib/xpt/tools/xpt_dump.c
@@ -566,17 +566,23 @@ XPT_DumpMethodDescriptor(XPTHeader *head
         else 
             fprintf(stdout, "FALSE\n");
         
         fprintf(stdout, "%*sIs Hidden?        ", indent, " ");
         if (XPT_MD_IS_HIDDEN(md->flags))
             fprintf(stdout, "TRUE\n");
         else 
             fprintf(stdout, "FALSE\n");
-        
+
+        fprintf(stdout, "%*sWants Optional Argc?        ", indent, " ");
+        if (XPT_MD_WANTS_OPT_ARGC(md->flags))
+            fprintf(stdout, "TRUE\n");
+        else 
+            fprintf(stdout, "FALSE\n");
+
         fprintf(stdout, "%*s# of arguments:   %d\n", indent, " ", md->num_args);
         fprintf(stdout, "%*sParameter Descriptors:\n", indent, " ");
         
         for (i=0; i<md->num_args; i++) {
             fprintf(stdout, "%*sParameter #%d:\n", new_indent, " ", i);
             
             if (!XPT_DumpParamDescriptor(header, &md->params[i], id, 
                                          more_indent, verbose_mode, PR_FALSE))
@@ -590,22 +596,23 @@ XPT_DumpMethodDescriptor(XPTHeader *head
         }
     } else {
         char *param_type;
         XPTParamDescriptor *pd;
 
         if (!XPT_GetStringForType(header, &md->result->type, id, &param_type)) {
             return PR_FALSE;
         }
-        fprintf(stdout, "%*s%c%c%c%c%c %s %s(", indent - 6, " ",
+        fprintf(stdout, "%*s%c%c%c%c%c%c %s %s(", indent - 6, " ",
                 XPT_MD_IS_GETTER(md->flags) ? 'G' : ' ',
                 XPT_MD_IS_SETTER(md->flags) ? 'S' : ' ',
                 XPT_MD_IS_HIDDEN(md->flags) ? 'H' : ' ',
                 XPT_MD_IS_NOTXPCOM(md->flags) ? 'N' : ' ',
                 XPT_MD_IS_CTOR(md->flags) ? 'C' : ' ',
+                XPT_MD_WANTS_OPT_ARGC(md->flags) ? 'O' : ' ',
                 param_type, md->name);
         for (i=0; i<md->num_args; i++) {
             if (i!=0) {
                 fprintf(stdout, ", ");
             }
             pd = &md->params[i];
             if (XPT_PD_IS_IN(pd->flags)) {
                 fprintf(stdout, "in");