If your Firefox plugin is scriptable, you should probably be validating the NPVariant arguments that you are passed in your invoke() callback. Unfortunately, this gets tedious and error-prone if you allow arguments to your methods to be of more than a single type (e.g. you accept a date as either a string or integer) or if you have optional arguments.
As I was debugging a crash in Moonlight, I discovered that we weren't properly checking argument-types properly in all cases (this particular bug was caused because the javascript had passed a bad argument to one of our methods which accepted either a string or null). To protect against this in a more robust way, I came up with the following helper functions:
typedef enum {
MethodArgTypeNone = (0),
MethodArgTypeVoid = (1 << NPVariantType_Void),
MethodArgTypeNull = (1 << NPVariantType_Null),
MethodArgTypeBool = (1 << NPVariantType_Bool),
MethodArgTypeInt32 = (1 << NPVariantType_Int32),
MethodArgTypeDouble = (1 << NPVariantType_Double),
MethodArgTypeString = (1 << NPVariantType_String),
MethodArgTypeObject = (1 << NPVariantType_Object),
MethodArgTypeAny = (0xff)
} MethodArgType;
static MethodArgType
decode_arg_ctype (char c)
{
switch (c) {
case 'v': return MethodArgTypeVoid;
case 'n': return MethodArgTypeNull;
case 'b': return MethodArgTypeBool;
case 'i': return MethodArgTypeInt32;
case 'd': return MethodArgTypeDouble;
case 's': return MethodArgTypeString;
case 'o': return MethodArgTypeObject;
case '*': return MethodArgTypeAny;
default:
return MethodArgTypeNone;
}
}
static MethodArgType
decode_arg_type (const char **in)
{
MethodArgType type = MethodArgTypeNone;
register const char *inptr = *in;
if (*inptr == '(') {
inptr++;
while (*inptr && *inptr != ')') {
type |= decode_arg_ctype (*inptr);
inptr++;
}
} else {
type = decode_arg_ctype (*inptr);
}
inptr++;
*in = inptr;
return type;
}
/**
* check_arg_list:
* @arglist: a string representing an arg-list token (see grammar below)
* @args: NPVariant argument count
* @argv: NPVariant argument vector
*
* Checks that the NPVariant arguments satisfy the argument count and
* types expected (provided via @typestr).
*
* The @typestr argument should follow the following syntax:
*
* simple-arg-type ::= "v" / "n" / "b" / "i" / "d" / "s" / "o" / "*"
* ; each char represents one of the following
* ; NPVariant types: Void, Null, Bool, Int32,
* ; Double, String, Object and wildcard
*
* arg-type ::= simple-arg-type / "(" 1*(simple-arg-type) ")"
*
* optional-args ::= "[" *(arg-type) "]"
*
* arg-list ::= *(arg-type) (optional-args)
*
*
* Returns: %true if @argv matches the arg-list criteria specified in
* @arglist or %false otherwise.
**/
static bool
check_arg_list (const char *arglist, uint32_t argc, const NPVariant *argv)
{
const char *inptr = arglist;
MethodArgType mask;
uint32_t i = 0;
/* check all of the required arguments */
while (*inptr && *inptr != '[' && i < argc) {
mask = decode_arg_type (&inptr);
if (!(mask & (1 << argv[i].type))) {
/* argv[i] does not match any of the expected types */
return false;
}
i++;
}
if (*inptr && *inptr != '[' && i < argc) {
/* we were not provided enough arguments */
return false;
}
/* now check all of the optional arguments */
inptr++;
while (*inptr && *inptr != ']' && i < argc) {
mask = decode_arg_type (&inptr);
if (!(mask & (1 << argv[i].type))) {
// argv[i] does not match any of the expected types
return false;
}
i++;
}
if (i < argc) {
/* we were provided too many arguments */
return false;
}
return true;
}
An example usage might be check_arg_list ("(so)i(nso)[bb]", argc, argv). In this example, the first argument is expected to be either a string or object. The second argument would be an int32. The third argument would be null, a string, or an object. The 4th and 5th arguments are both of type bool if they are present.
Since the NPVariant struct is a widely-used concept in C programming, you might find my hack useful for other projects as well.
Enjoy!
1 comment:
One improvement might be to add argument unpacking too, similar to Python's PyArg_ParseTuple(). Might help remove even more boilerplate code.
Post a Comment