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]

This commit is contained in:
reed@reedloden.com 2008-03-12 04:00:24 -07:00
parent b544cb9223
commit 270d099f76
7 changed files with 202 additions and 53 deletions

View File

@ -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
*/

View File

@ -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

View File

@ -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();
};

View File

@ -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 */

View File

@ -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))

View File

@ -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);

View File

@ -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;