Errors and the introspection format

Havoc Pennington hp at redhat.com
Sun Feb 19 21:15:46 PST 2006


Hi,

I don't think we're disagreeing, I just wasn't very clear -

On Sun, 2006-02-19 at 20:59 +0100, Thiago Macieira wrote:
> Havoc Pennington wrote:
> >In other words, if a method claims to return a String, and the callee
> >returns an integer, then it's the responsibility of the caller-side
> >bindings to convert that into an error.
> 
> Since Introspection of the target is an optional feature, even though part 
> of the specification, I don't see how enforcing that in all bindings is 
> the correct thing to do.

What I'm trying to say is more general than that (and would be true even
if we didn't have the introspection stuff).

If you have an IPC mechanism, the caller must validate the results
returned by the server in order to use those results.

This can happen at different levels. With the lowlevel dbus bindings,
the way it works is that you'd do:

 dbus_message_get_args(message, &error, DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID);

If the message does not have a DBUS_TYPE_STRING as the first arg, then
it's an error. Here we've validated the return type of the method
(assuming "message" is a method return).

A binding that was 100% dynamic would push validation into application
code. For example, in Java I could write the binding like:

Object makeDBusCall(String method, Object... args) throws DBusException;

And then the app would have to do:

Object result = dbus.makeDBusCall("Frobate", a, b, c);
if (!(result instanceof String))
  throw new SomeException("Remote app is trying to screw us");
String s = (String) result;

In a language like Python, this approach would be somewhat more natural
than in Java, but it's possible either way.

In the same way, the implementor of a method on the server side has to
validate the expected incoming arguments.

When a binding statically generates code, it simply moves the type
validation from the application code into the generated binding code.
For example a binding might generate:

String Frobate() {
        Object result = dbus.makeDBusCall("Frobate", a, b, c);
        if (!(result instanceof String))
          throw new SomeException("Remote app is trying to screw us");
        return (String) result;
}

Anyhoo... the situation with exceptions is identical, in that some code
has to verify them. But the difference is that all languages except Java
do the exception verification dynamically, and even Java does it
dynamically for _certain_ exceptions ("unchecked exceptions").

The point of putting exception annotations on specific dbus methods
would be to have the verification in some autogenerated code, either in
the binding, or I guess in the bus.

You had mentioned an annotation for "this never throws anything," and my
point was that such an annotation only affects the caller app - nothing
is stopping the callee/server app from saying it never throws anything,
and then throwing something anyhow. The caller/client is the only place
you can "enforce" what a method throws.

It depends on the details of a binding whether the caller/client
enforcement of types (or exceptions) happens inside autogenerated
binding code, or in application code.

> So, while I think this kind of detail should be left out of the D-Bus 
> specification (since introspecting the target before calling is not 
> mandated)

Indeed, the dbus spec has nothing to say on this matter; I'm not making
a point about spec requirements, I'm making a point about what is
logically possible vs. not.

> >So we would have to introduce some kind of "is this error checked or
> >unchecked" concept, I don't even know where that bit would be stored,
> >and then all bindings would need to do the appropriate signature
> >verification to be sure only expected checked exceptions made it out.
> 
> As I illustrated above, I believe it's up to the binding implementation to 
> decide whether to enforce this or not. Unless, of course, the 
> specification is modified to mandate this.

Let me try to be clearer what I was saying about exceptions. First,
definitions:

 - "checked exceptions" are in the throws clause of a method and 
   some mechanism prevents them from being thrown up to application 
   code if they aren't declared. This mechanism could be the 
   compiler, a dbus binding, or even the bus itself.
   The only widely-used language I know of with checked exceptions 
   is Java.

 - "unchecked exceptions" are not explicitly listed, and any 
   "unchecked exception" can be thrown from any method all the 
   way up to the application code, without upsetting any 
   compilers or bindings. The application code must handle 
   the unchecked exception in some way, if only by crashing/exiting
   (a default handler does this in C++ and Java).

Definitions in hand, what I was trying to say -

First point:

 - it's seems very unlikely we would have _only_ checked exceptions. 
   We'll have unchecked exceptions also.

Some reasons why I think the first point is true:

 - if you make out of memory, invalid arguments, network failure, 
   etc. into checked exceptions, programmers will go nuts.
 - you can only check the checked exceptions at runtime, and 
   if an unexpected checked exception is thrown, the only 
   sane thing I can think of to do is throw an unchecked exception
   or a "wrapper" checked exception like InvocationTargetException.
   In other words, IPC prevents the compiler from doing a full static
   analysis.
 - I can't think of a language or system with _only_ checked 
   exceptions, and I don't care to be the first to experiment with 
   that.

Second point, following from first point:

 - the question is not whether to list exceptions in annotations, but
   whether to distinguish "checked" from "unchecked" exceptions and 
   list the "checked" exceptions in annotations as a "throws clause"

Conclusions:

 - if we add "throws clauses" then we also need a "checked vs.
   unchecked" flag for each error, we have to figure out how to 
   implement that, and we have to explain to Python/C/C++ programmers
   when to use each kind of error

 - "throws clauses" are only useful if bindings are going to use them
   to throw checked exceptions in their target language/framework; 
   if the binding has only "unchecked exceptions" then the list of 
   unchecked exceptions a given call can throw is both very long, and 
   subject to change

 - "throws clauses" are only useful in Java, since no other binding
   even has the option to throw checked exceptions, and throws
   clauses are only useful for checked exceptions

In short form:

 - a "checked vs. unchecked" flag is a complicated new 
   feature
 - only Java would even hypothetically benefit
 - why don't we just stick to unchecked exceptions

> Yes, I am required to do that because there are many DCOP methods that are 
> asynchronous. So it's not a question of whether there will be such an 
> annotation, but rather if it should be an org.freedesktop.* one. And, 
> again, since return values are not part of the method signature, an 
> implementation can choose to ignore this completely.

See my other mail on the no reply thing.

Havoc




More information about the dbus mailing list