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