[e-lang] Notes from latest Joe-E meeting

David Hopwood david.hopwood at industrial-designers.co.uk
Mon Apr 30 15:28:13 EDT 2007


Tyler Close wrote:
> Hi David,
> 
> The goal for the Error handling is to ensure that Joe-E code cannot
> cause any side-effects after an Error is thrown. [...]

I agree that this is the goal.

>>Remember, VirtualMachineErrors are *entirely* nondeterministic and can happen
>>at any time (at least as far as the Java spec is concerned, and we don't really
>>want to rely on properties of specific Java implementations). They do not
>>necessarily only occur at uses of 'new' or method calls.
> 
> I agree we really don't want to, but they've not left us with much
> alternative. If we don't find a hack that works in practice, we'll
> have to ban catching of Error *and* finally clauses. As a test, I've
> rewritten all my code to not use finally blocks, so it is doable, but
> it might make Joe-E a harder sell.

It should certainly be possible to provide the functionality of "finally":

  public interface Thunk {
      void run() throws Exception;
  }

  public class JoeE {
      ...
      public static void tryFinallyThrowsChecked(
                         Thunk tryThunk, Thunk finallyThunk)
                         throws Exception {
          bool succeeded = false;
          try {
              tryThunk.run();
              succeeded = true;
              finallyThunk.run();
          } catch (Exception e) {
              if (!succeeded) finallyThunk.run();
              throw e;
          }
      }


and then

  try {
      ... X ...
  } finally {
      ... Y ...
  }

becomes

  JoeE.tryFinallyThrowsChecked(new Thunk() { public void run() {
      ... X ...
  }}, new Thunk() { public void run() {
      ... Y ...
  }});

This won't win any beauty contests (because of the lack of closures, blocks or
macros in Java), but I think we can live with that given the infrequent use of
finally clauses.

Notice how the implementation does not actually use "finally". I threw away
several subtly wrong attempts that tried to use Java's "finally", before concluding
that it is irretrievably broken. The resulting Joe-E verifier rule is very simple:
don't allow "finally" in Joe-E programs at all.


Here is a more complete implementation that provides 'try/catch/finally' as well
as just 'try/finally', fixes the Java (and E) design flaw described at
<http://www.eros-os.org/pipermail/e-lang/2006-July/011371.html>, and provides
versions for use when the whole construct is not expected to throw a checked
exception. It compiles but has not been tested (and it needs to be properly
documented).


public interface Thunk {
    void run() throws Exception;
}

public interface Catch {
    void handle(Exception e) throws Exception;
}

public class FinallyClauseException extends RuntimeException {
    private final Throwable inFinallyClause;

    public FinallyClauseException(Throwable original, Throwable inFinallyClause) {
        super(original + "\nfollowed by " + inFinallyClause +
              " in a finally clause", original);
        this.inFinallyClause = inFinallyClause;
    }
    public Throwable getExceptionInFinallyClause() {
        return inFinallyClause;
    }
}

public class JoeE {
    public static void tryFinallyThrowsChecked(
                       Thunk tryThunk, Thunk finallyThunk)
                       throws Exception {
        boolean succeeded = false;
        try {
            tryThunk.run();
            succeeded = true;
            finallyThunk.run();
        } catch (Exception e) {
            if (!succeeded) {
                try {
                    finallyThunk.run();
                } catch (Exception e2) {
                    throw new FinallyClauseException(e, e2);
                }
            }
            throw e;
        }
    }

    public static void tryCatchFinallyThrowsChecked(
                       Thunk tryThunk, Catch handler, Thunk finallyThunk)
                       throws Exception {
        boolean succeeded = false;
        try {
            tryThunk.run();
            succeeded = true;
            finallyThunk.run();
        } catch (Exception e) {
            if (!succeeded) {
                boolean handled = false;
                try {
                    handler.handle(e);
                    handled = true;
                    finallyThunk.run();
                    return;  // e was caught by handler
                } catch (Exception e2) {
                    if (!handled) {
                        try {
                            finallyThunk.run();
                        } catch (Exception e3) {
                            throw new FinallyClauseException(e2, e3);
                        }
                    }
                    // e2 should replace e, whether or not the handler completed
                    throw e2;
                }
            }
            throw e;
        }
    }

    public static void tryFinally(
                       Thunk tryThunk, Thunk finallyThunk) {
        try {
            JoeE.tryFinallyThrowsChecked(tryThunk, finallyThunk);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void tryCatchFinally(
                       Thunk tryThunk, Catch handler, Thunk finallyThunk) {
        try {
            JoeE.tryCatchFinallyThrowsChecked(tryThunk, handler, finallyThunk);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

-- 
David Hopwood <david.hopwood at industrial-designers.co.uk>



More information about the e-lang mailing list