From cb6cd5134f121212a6a15ba60aef768043944296 Mon Sep 17 00:00:00 2001 From: "reed%reedloden.com" Date: Wed, 12 Mar 2008 11:00:59 +0000 Subject: [PATCH] Bug 414063 - "annotate xpidl-generated c++ headers with attributes for scriptable and deprecated methods" [p=Pidgeot18@gmail.com (Joshua Cranmer) r=dbradley a1.9=damons] git-svn-id: svn://10.0.0.236/trunk@247621 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/xpcom/base/nscore.h | 20 ++++ mozilla/xpcom/tests/Makefile.in | 10 ++ mozilla/xpcom/tests/TestScriptable.idl | 21 +++++ mozilla/xpcom/typelib/xpidl/xpidl.h | 3 + mozilla/xpcom/typelib/xpidl/xpidl_header.c | 28 +++++- mozilla/xpcom/typelib/xpidl/xpidl_java.c | 71 ++++++++++++++ mozilla/xpcom/typelib/xpidl/xpidl_util.c | 102 +++++++++++---------- 7 files changed, 202 insertions(+), 53 deletions(-) create mode 100644 mozilla/xpcom/tests/TestScriptable.idl diff --git a/mozilla/xpcom/base/nscore.h b/mozilla/xpcom/base/nscore.h index 7329fd11a70..3ed7024505e 100644 --- a/mozilla/xpcom/base/nscore.h +++ b/mozilla/xpcom/base/nscore.h @@ -244,6 +244,26 @@ ret (NS_STDCALL class::*name) args #endif +/** + * Deprecated declarations. + */ +#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +# define NS_DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) && (_MSC_VER >= 1300) +# define NS_DEPRECATED __declspec(deprecated) +#else +# define NS_DEPRECATED +#endif + +/** + * Attributes defined to help Dehydra GCC analysis. + */ +#ifdef DEHYDRA_GCC +# define NS_SCRIPTABLE __attribute__((user("script"))) +#else +# define NS_SCRIPTABLE +#endif + /** * Generic API modifiers which return the standard XPCOM nsresult type */ diff --git a/mozilla/xpcom/tests/Makefile.in b/mozilla/xpcom/tests/Makefile.in index 89c3a515f9f..1978d49dbf1 100644 --- a/mozilla/xpcom/tests/Makefile.in +++ b/mozilla/xpcom/tests/Makefile.in @@ -148,3 +148,13 @@ check:: for f in $(CPP_UNIT_TESTS); do \ XPCOM_DEBUG_BREAK=stack-and-abort $(RUN_TEST_PROGRAM) $(DIST)/bin/$$f; \ done + @echo "Running XPIDL tests" + $(XPIDL_COMPILE) -m header $(srcdir)/TestScriptable.idl + @if grep Notscriptable TestScriptable.h | grep -q NS_SCRIPTABLE ; then \ + echo "Nonscriptable object marked scriptable by xpidl"; \ + exit 1; \ + fi + @if test $$( grep 'NS_IMETHOD[^I].*Scriptable' TestScriptable.h | grep -v -c NS_SCRIPTABLE ) != 0 ; then \ + echo "Scriptable object marked nonscriptable by xpidl"; \ + exit 1; \ + fi diff --git a/mozilla/xpcom/tests/TestScriptable.idl b/mozilla/xpcom/tests/TestScriptable.idl new file mode 100644 index 00000000000..35da79f3244 --- /dev/null +++ b/mozilla/xpcom/tests/TestScriptable.idl @@ -0,0 +1,21 @@ + +[scriptable, uuid(76d74662-0eae-404c-9d1f-697c0e321c0a)] +interface testScriptableInterface { + readonly attribute long scriptable_attr1; + attribute long scriptable_attr2; + [noscript] readonly attribute long notscriptable_attr1; + [noscript] attribute long notscriptable_attr2; + + void scriptable_method1(); + [noscript] void notscriptable_method1(); + [notxpcom] void notscriptable_method2(); + [noscript, notxpcom] void notscriptable_method3(); +}; + +[uuid(76d74662-0eae-404c-9d1f-697c0e321c0a)] +interface testNotscriptableInterface { + readonly attribute long notscriptable_attr1; + attribute long notscriptable_attr2; + + void notscriptable_method1(); +}; diff --git a/mozilla/xpcom/typelib/xpidl/xpidl.h b/mozilla/xpcom/typelib/xpidl/xpidl.h index bc8a9aa6e93..7a2f1322a33 100644 --- a/mozilla/xpcom/typelib/xpidl/xpidl.h +++ b/mozilla/xpcom/typelib/xpidl/xpidl.h @@ -275,4 +275,7 @@ check_native(TreeState *state); void printlist(FILE *outfile, GSList *slist); +gboolean +is_method_scriptable(IDL_tree method_tree, IDL_tree ident); + #endif /* __xpidl_h */ diff --git a/mozilla/xpcom/typelib/xpidl/xpidl_header.c b/mozilla/xpcom/typelib/xpidl/xpidl_header.c index 9d4247ec4c8..d9b8fdcc28a 100644 --- a/mozilla/xpcom/typelib/xpidl/xpidl_header.c +++ b/mozilla/xpcom/typelib/xpidl/xpidl_header.c @@ -146,7 +146,7 @@ interface(TreeState *state) char *classNameImpl = NULL; char *cp; gboolean ok = TRUE; - gboolean keepvtable; + gboolean keepvtable, scriptable, deprecated; const char *iid; const char *name_space; struct nsID id; @@ -233,11 +233,20 @@ interface(TreeState *state) if (IDL_NODE_TYPE(data) == IDLN_CODEFRAG) keepvtable = TRUE; } - + + scriptable = deprecated = FALSE; + if (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "scriptable")) + scriptable = TRUE; + if (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "deprecated")) + deprecated = TRUE; + /* The interface declaration itself. */ fprintf(state->file, - "class %s%s", - (keepvtable ? "" : "NS_NO_VTABLE "), className); + "class %s%s%s%s", + (keepvtable ? "" : "NS_NO_VTABLE "), + (scriptable ? "NS_SCRIPTABLE " : ""), + (deprecated ? "NS_DEPRECATED " : ""), + className); if ((iter = IDL_INTERFACE(iface).inheritance_spec)) { fputs(" : ", state->file); @@ -746,8 +755,14 @@ write_attr_accessor(IDL_tree attr_tree, FILE * outfile, { char *attrname = ATTR_IDENT(attr_tree).str; const char *binaryname; + IDL_tree ident = IDL_LIST(IDL_ATTR_DCL(attr_tree).simple_declarations).data; if (mode == AS_DECL) { + if (IDL_tree_property_get(ident, "deprecated")) + fputs("NS_DEPRECATED ", outfile); + if (is_method_scriptable(attr_tree, ident)) + fputs("NS_SCRIPTABLE ", outfile); + fputs("NS_IMETHOD ", outfile); } else if (mode == AS_IMPL) { fprintf(outfile, "NS_IMETHODIMP %s::", className); @@ -1023,6 +1038,11 @@ write_method_signature(IDL_tree method_tree, FILE *outfile, int mode, 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)) + fputs("NS_SCRIPTABLE ", outfile); + if (op_notxpcom) { fputs("NS_IMETHOD_(", outfile); if (!write_type(op->op_type_spec, FALSE, outfile)) diff --git a/mozilla/xpcom/typelib/xpidl/xpidl_java.c b/mozilla/xpcom/typelib/xpidl/xpidl_java.c index e36f53cea5d..68be7d29f13 100644 --- a/mozilla/xpcom/typelib/xpidl/xpidl_java.c +++ b/mozilla/xpcom/typelib/xpidl/xpidl_java.c @@ -144,6 +144,55 @@ write_indent(FILE *outfile) { fputs(" ", outfile); } +static GSList* +add_deprecated(GSList *comments) +{ + GSList *last; + char *buffer; + char *replaced; + const char deprecated[] = "* @deprecated */"; + + /* Handle the easy case: no documentation. */ + if (comments == NULL) { + buffer = malloc(sizeof(deprecated)+2); + buffer[0] = '/'; + buffer[1] = '*'; + strcpy(buffer+2, deprecated); + + return g_slist_append(comments, buffer); + } + + /* xpidl is so nice in that they give us the data as a single node. + * We are going to have to (very hackishly) strip out the the end of the + * documentation comment, add the tag, and then pop it back together. + */ + + /* Step 1: Move the comment into a larger buffer, so that we can have + * more space to work with. + */ + last = g_slist_last(comments); + buffer = last->data; + replaced = (char *)malloc(strlen(buffer) + sizeof(deprecated)); + strcpy(replaced, buffer); + last->data = replaced; + free(buffer); + + /* Now replaced has the comment, with a large enough buffer to put in the + * @deprecated tag. We search for the last / in hopes that the previous + * character is the * we're looking for... + */ + buffer = strrchr(replaced, '/'); + if (buffer == NULL || buffer == replaced || buffer[-1] == '*') { + /* We can't find a '/', so there's no comment, or this is not the end + * of a comment, so we'll ignore adding the deprecated tag. + */ + return comments; + } + /* buffer now points to '*' '/'. Overwrite both. */ + strcpy(buffer-1, deprecated); + return comments; +} + static gboolean write_classname_iid_define(FILE *file, const char *className) { @@ -330,6 +379,13 @@ interface_declaration(TreeState *state) return FALSE; } + /* + * Add deprecated tags if the interface is deprecated + */ + if (IDL_tree_property_get(IDL_INTERFACE(interface).ident, "deprecated")) { + doc_comments = add_deprecated(doc_comments); + } + /* * Write any interface comments */ @@ -776,6 +832,13 @@ method_declaration(TreeState *state) } #endif + /* + * Add deprecated tags if the interface is deprecated + */ + if (IDL_tree_property_get(method->ident, "deprecated")) { + doc_comments = add_deprecated(doc_comments); + } + if (doc_comments != NULL) { write_indent(state->file); printlist(state->file, doc_comments); @@ -1013,6 +1076,14 @@ attribute_declaration(TreeState *state) doc_comments = IDL_IDENT(IDL_LIST(IDL_ATTR_DCL (state->tree).simple_declarations).data).comments; + + /* + * Add deprecated tags if the interface is deprecated + */ + if (IDL_tree_property_get(ATTR_PROPS(state->tree), "deprecated")) { + doc_comments = add_deprecated(doc_comments); + } + if (doc_comments != NULL) { write_indent(state->file); printlist(state->file, doc_comments); diff --git a/mozilla/xpcom/typelib/xpidl/xpidl_util.c b/mozilla/xpcom/typelib/xpidl/xpidl_util.c index 0afed439929..65f8988636a 100644 --- a/mozilla/xpcom/typelib/xpidl/xpidl_util.c +++ b/mozilla/xpcom/typelib/xpidl/xpidl_util.c @@ -277,13 +277,59 @@ matches_nsIFoo(const char* attribute_name) return matches_IFoo(attribute_name + 3); } +/** + * Returns TRUE if the method is probably scriptable, FALSE otherwise. + * The first parameter may also be an attr_tree parameter, since these two are + * the same with respect to discovering the interface node. + */ +gboolean +is_method_scriptable(IDL_tree method_tree, IDL_tree ident) +{ + IDL_tree iface; + gboolean scriptable_interface; + + /* + * Look up the tree to find the interface. If we can't find the interface, + * then the caller is being called on an incorrect tree. If we find it, we + * see if it's [scriptable] and duly note it. + */ + if (IDL_NODE_UP(method_tree) && IDL_NODE_UP(IDL_NODE_UP(method_tree)) && + IDL_NODE_TYPE(iface = IDL_NODE_UP(IDL_NODE_UP(method_tree))) + == IDLN_INTERFACE) + { + scriptable_interface = + (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "scriptable") + != NULL); + } else { + IDL_tree_error(method_tree, + "is_method_scriptable called on a non-interface?"); + return FALSE; + } + + /* If the interface isn't scriptable, the method sure can't be... */ + if (!scriptable_interface) + return FALSE; + + /* [notxpcom] implies [noscript] */ + if (IDL_tree_property_get(ident, "notxpcom") != NULL) + return FALSE; + + /* [noscript] methods obviously aren't scriptable */ + if (IDL_tree_property_get(ident, "noscript") != NULL) + return FALSE; + + /* The interface is scriptable, so therefore the method is, if the + * interfaces are accessible. That's good enough for this method. + */ + return TRUE; +} + gboolean verify_attribute_declaration(IDL_tree attr_tree) { IDL_tree iface; IDL_tree ident; IDL_tree attr_type; - gboolean scriptable_interface; /* We don't support attributes named IID, conflicts with static GetIID * member. The conflict is due to certain compilers (VC++) choosing a @@ -299,22 +345,6 @@ verify_attribute_declaration(IDL_tree attr_tree) "ordering problems"); return FALSE; } - /* - * Verify that we've been called on an interface, and decide if the - * interface was marked [scriptable]. - */ - if (IDL_NODE_UP(attr_tree) && IDL_NODE_UP(IDL_NODE_UP(attr_tree)) && - IDL_NODE_TYPE(iface = IDL_NODE_UP(IDL_NODE_UP(attr_tree))) - == IDLN_INTERFACE) - { - scriptable_interface = - (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "scriptable") - != NULL); - } else { - IDL_tree_error(attr_tree, - "verify_attribute_declaration called on a non-interface?"); - return FALSE; - } /* * Grab the first of the list of idents and hope that it'll @@ -324,10 +354,10 @@ verify_attribute_declaration(IDL_tree attr_tree) /* * If the interface isn't scriptable, or the attribute is marked noscript, - * there's no need to check. + * there's no need to check. This also verifies that we've been called on + * an interface. */ - if (!scriptable_interface || - IDL_tree_property_get(ident, "noscript") != NULL) + if (!is_method_scriptable(attr_tree, ident)) return TRUE; /* @@ -533,7 +563,6 @@ check_param_attribute(IDL_tree method_tree, IDL_tree param, return TRUE; } - /* * Common method verification code, called by *op_dcl in the various backends. */ @@ -544,7 +573,6 @@ verify_method_declaration(IDL_tree method_tree) IDL_tree iface; IDL_tree iter; gboolean notxpcom; - gboolean scriptable_interface; gboolean scriptable_method; gboolean seen_retval = FALSE; gboolean hasoptional = PR_FALSE; @@ -568,36 +596,12 @@ verify_method_declaration(IDL_tree method_tree) } /* - * Verify that we've been called on an interface, and decide if the - * interface was marked [scriptable]. - */ - if (IDL_NODE_UP(method_tree) && IDL_NODE_UP(IDL_NODE_UP(method_tree)) && - IDL_NODE_TYPE(iface = IDL_NODE_UP(IDL_NODE_UP(method_tree))) - == IDLN_INTERFACE) - { - scriptable_interface = - (IDL_tree_property_get(IDL_INTERFACE(iface).ident, "scriptable") - != NULL); - } else { - IDL_tree_error(method_tree, - "verify_method_declaration called on a non-interface?"); - return FALSE; - } - - /* - * Require that any method in an interface marked as [scriptable], that - * *isn't* scriptable because it refers to some native type, be marked - * [noscript] or [notxpcom]. - * - * Also check that iid_is points to nsid, and length_is, size_is points - * to unsigned long. + * Decide if we are a scriptable method, or if we were are notxpcom. + * In doing so, we also verify that we've been called on an interface. */ + scriptable_method = is_method_scriptable(method_tree, op->ident); notxpcom = IDL_tree_property_get(op->ident, "notxpcom") != NULL; - scriptable_method = scriptable_interface && - !notxpcom && - IDL_tree_property_get(op->ident, "noscript") == NULL; - /* Loop through the parameters and check. */ for (iter = op->parameter_dcls; iter; iter = IDL_LIST(iter).next) { IDL_tree param = IDL_LIST(iter).data;