Implement "try..catch..finally" and "throw" statements, and array creation with bounds.

Enable "maven test".
diff --git a/pom.xml b/pom.xml
index 25a3a26..ad595c6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,6 +106,16 @@
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <version>2.15</version>
+        <configuration>
+          <includes>
+            <include>net/hydromatic/linq4j/test/Linq4jSuite.java</include>
+          </includes>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-source-plugin</artifactId>
         <version>2.2.1</version>
         <executions>
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/CatchBlock.java b/src/main/java/net/hydromatic/linq4j/expressions/CatchBlock.java
index 30ae279..ee7f54c 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/CatchBlock.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/CatchBlock.java
@@ -21,6 +21,14 @@
  * Represents a catch statement in a try block.
  */
 public class CatchBlock {
+  public final ParameterExpression parameter;
+  public final Statement body;
+
+  public CatchBlock(ParameterExpression parameter,
+      Statement body) {
+    this.parameter = parameter;
+    this.body = body;
+  }
 }
 
 // End CatchBlock.java
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/ExpressionWriter.java b/src/main/java/net/hydromatic/linq4j/expressions/ExpressionWriter.java
index d991530..3a12e33 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/ExpressionWriter.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/ExpressionWriter.java
@@ -188,6 +188,13 @@
     return this;
   }
 
+  public void backUp() {
+    if (buf.lastIndexOf("\n") == buf.length() - 1) {
+      buf.delete(buf.length() - 1, buf.length());
+      indentPending = false;
+    }
+  }
+
   private static class Indent extends ArrayList<String> {
     public Indent(int initialCapacity) {
       super(initialCapacity);
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java b/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
index 6c2db22..7a12eac 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Expressions.java
@@ -444,36 +444,9 @@
    * reference to the caught Exception object for use in the handler
    * body.
    */
-  public static CatchBlock catch_(ParameterExpression expression0,
-      Expression expression1) {
-    throw Extensions.todo();
-  }
-
-  /**
-   * Creates a CatchBlock representing a catch statement.
-   */
-  public static CatchBlock catch_(Type type, Expression expression) {
-    throw Extensions.todo();
-  }
-
-  /**
-   * Creates a CatchBlock representing a catch statement with an
-   * Exception filter and a reference to the caught Exception
-   * object.
-   */
-  public static CatchBlock catch_(ParameterExpression expression0,
-      Expression expression1, Expression expression2) {
-    throw Extensions.todo();
-  }
-
-  /**
-   * Creates a CatchBlock representing a catch statement with an
-   * Exception filter but no reference to the caught Exception
-   * object.
-   */
-  public static CatchBlock catch_(Type type, Expression expression0,
-      Expression expression1) {
-    throw Extensions.todo();
+  public static CatchBlock catch_(ParameterExpression parameter,
+      Statement statement) {
+    return new CatchBlock(parameter, statement);
   }
 
   /**
@@ -1811,8 +1784,13 @@
    */
   public static BinaryExpression multiplyAssignChecked(Expression left,
       Expression right, Method method, LambdaExpression lambdaExpression) {
-    return makeBinary(ExpressionType.MultiplyAssignChecked, left, right, false,
-        method, lambdaExpression);
+    return makeBinary(
+        ExpressionType.MultiplyAssignChecked,
+        left,
+        right,
+        false,
+        method,
+        lambdaExpression);
   }
 
   /**
@@ -1986,18 +1964,9 @@
    * Creates a NewArrayExpression that represents creating an array
    * that has a specified rank.
    */
-  public static NewArrayExpression newArrayBounds(Type type,
-      Iterable<? extends Expression> expressions) {
-    throw Extensions.todo();
-  }
-
-  /**
-   * Creates a NewArrayExpression that represents creating an array
-   * that has a specified rank, using varargs.
-   */
-  public static NewArrayExpression newArrayBounds(Type type,
-      Expression... expressions) {
-    return newArrayBounds(type, toList(expressions));
+  public static NewArrayExpression newArrayBounds(Type type, int dimension,
+      Expression bound) {
+    return new NewArrayExpression(type, dimension, bound, null);
   }
 
   /**
@@ -2009,7 +1978,7 @@
    */
   public static NewArrayExpression newArrayInit(Type type,
       Iterable<? extends Expression> expressions) {
-    return new NewArrayExpression(type, toList(expressions));
+    return new NewArrayExpression(type, 1, null, toList(expressions));
   }
 
   /**
@@ -2021,7 +1990,31 @@
    */
   public static NewArrayExpression newArrayInit(Type type,
       Expression... expressions) {
-    return newArrayInit(type, toList(expressions));
+    return new NewArrayExpression(type, 1, null, toList(expressions));
+  }
+
+  /**
+   * Creates a NewArrayExpression that represents creating a
+   * n-dimensional array and initializing it from a list of
+   * elements.
+   *
+   * @param type Element type of the array.
+   */
+  public static NewArrayExpression newArrayInit(Type type, int dimension,
+      Iterable<? extends Expression> expressions) {
+    return new NewArrayExpression(type, dimension, null, toList(expressions));
+  }
+
+  /**
+   * Creates a NewArrayExpression that represents creating an
+   * n-dimensional array and initializing it from a list of
+   * elements, using varargs.
+   *
+   * @param type Element type of the array.
+   */
+  public static NewArrayExpression newArrayInit(Type type, int dimension,
+      Expression... expressions) {
+    return new NewArrayExpression(type, dimension, null, toList(expressions));
   }
 
   /**
@@ -2732,19 +2725,10 @@
   }
 
   /**
-   * Creates a UnaryExpression that represents a throwing of an
-   * exception.
+   * Creates a statement that represents the throwing of an exception.
    */
-  public static UnaryExpression throw_(Expression expression) {
-    throw Extensions.todo();
-  }
-
-  /**
-   * Creates a UnaryExpression that represents a throwing of an
-   * exception with a given type.
-   */
-  public static UnaryExpression throw_(Expression expression, Type type) {
-    throw Extensions.todo();
+  public static ThrowStatement throw_(Expression expression) {
+    return new ThrowStatement(expression);
   }
 
   /**
@@ -2752,52 +2736,46 @@
    * number of catch statements and neither a fault nor finally
    * block.
    */
-  public static TryStatement tryCatch(Expression body,
+  public static TryStatement tryCatch(Statement body,
+      Iterable<? extends CatchBlock> handlers) {
+    return new TryStatement(body, toList(handlers), null);
+  }
+
+  /**
+   * Creates a TryExpression representing a try block with any
+   * number of catch statements and neither a fault nor finally
+   * block, with varargs.
+   */
+  public static TryStatement tryCatch(Statement body,
       CatchBlock... handlers) {
-    throw Extensions.todo();
+    return new TryStatement(body, toList(handlers), null);
   }
 
   /**
    * Creates a TryExpression representing a try block with any
    * number of catch statements and a finally block.
    */
-  public static TryStatement tryCatchFinally(Expression body,
-      CatchBlock... handlers) {
-    throw Extensions.todo();
+  public static TryStatement tryCatchFinally(Statement body,
+      Iterable<? extends CatchBlock> handlers, Statement finally_) {
+    return new TryStatement(body, toList(handlers), finally_);
   }
 
   /**
-   * Creates a TryExpression representing a try block with a fault
-   * block and no catch statements.
+   * Creates a TryExpression representing a try block with any
+   * number of catch statements and a finally block, with varargs.
    */
-  public static TryStatement tryFault(Expression body, Expression fault) {
-    throw Extensions.todo();
+  public static TryStatement tryCatchFinally(Statement body, Statement finally_,
+      CatchBlock... handlers) {
+    return new TryStatement(body, toList(handlers), finally_);
   }
 
   /**
    * Creates a TryExpression representing a try block with a
    * finally block and no catch statements.
    */
-  public static TryStatement tryFinally(Expression body, Expression fault) {
-    throw Extensions.todo();
-  }
-
-  /**
-   * Creates a Type object that represents a generic System.Action
-   * delegate type that has specific type arguments.
-   */
-  public static boolean tryGetActionType(Class[] typeArgs,
-      Class[] outActionType) {
-    throw Extensions.todo();
-  }
-
-  /**
-   * Creates a Type object that represents a generic System.Func
-   * delegate type that has specific type arguments. The last type
-   * argument specifies the return type of the created delegate.
-   */
-  public static boolean tryGetFuncType(Class[] typeArgs, Class[] outFuncType) {
-    throw Extensions.todo();
+  public static TryStatement tryFinally(Statement body, Statement finally_) {
+    return new TryStatement(body, Collections.<CatchBlock>emptyList(),
+        finally_);
   }
 
   /**
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/FunctionExpression.java b/src/main/java/net/hydromatic/linq4j/expressions/FunctionExpression.java
index 45892a5..09bb186 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/FunctionExpression.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/FunctionExpression.java
@@ -124,16 +124,14 @@
     List<String> boxBridgeArgs = new ArrayList<String>();
     for (ParameterExpression parameterExpression : parameterList) {
       final Type parameterType = parameterExpression.getType();
-      final String parameterTypeName = Types.className(parameterType);
-      final String parameterBoxTypeName = Types.boxClassName(parameterType);
-      params.add(parameterTypeName + " " + parameterExpression.name);
-
-      bridgeParams.add("Object " + parameterExpression.name);
+      final Type parameterBoxType = Types.box(parameterType);
+      final String parameterBoxTypeName = Types.className(parameterBoxType);
+      params.add(parameterExpression.declString());
+      bridgeParams.add(parameterExpression.declString(Object.class));
       bridgeArgs.add("(" + parameterBoxTypeName + ") "
           + parameterExpression.name);
 
-      boxBridgeParams.add(parameterBoxTypeName + " "
-          + parameterExpression.name);
+      boxBridgeParams.add(parameterExpression.declString(parameterBoxType));
       boxBridgeArgs.add(parameterExpression.name
           + (Primitive.is(parameterType)
           ? "." + Primitive.of(parameterType).primitiveName + "Value()"
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/MethodDeclaration.java b/src/main/java/net/hydromatic/linq4j/expressions/MethodDeclaration.java
index 306d46e..1d062db 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/MethodDeclaration.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/MethodDeclaration.java
@@ -57,13 +57,7 @@
     writer.append(resultType).append(' ').append(name).list("(", ", ", ")",
         new AbstractList<String>() {
           public String get(int index) {
-            ParameterExpression parameter = parameters.get(index);
-            final String modifiers = Modifier.toString(parameter.modifier);
-            return modifiers
-                   + (modifiers.isEmpty() ? "" : " ")
-                   + Types.className(parameter.getType())
-                   + " "
-                   + parameter.name;
+            return parameters.get(index).declString();
           }
 
           public int size() {
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/NewArrayExpression.java b/src/main/java/net/hydromatic/linq4j/expressions/NewArrayExpression.java
index 1cd3fcc..2b42a5f 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/NewArrayExpression.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/NewArrayExpression.java
@@ -25,23 +25,41 @@
  * new array.
  */
 public class NewArrayExpression extends Expression {
+  public final int dimension;
+  public final Expression bound;
   public final List<Expression> expressions;
 
-  public NewArrayExpression(Type type, List<Expression> expressions) {
-    super(ExpressionType.NewArrayInit, Types.arrayType(type));
+  public NewArrayExpression(Type type, int dimension, Expression bound,
+      List<Expression> expressions) {
+    super(ExpressionType.NewArrayInit, Types.arrayType(type, dimension));
+    this.dimension = dimension;
+    this.bound = bound;
     this.expressions = expressions;
   }
 
   @Override
   public Expression accept(Visitor visitor) {
-    List<Expression> expressions = Expressions.acceptExpressions(
-        this.expressions, visitor);
-    return visitor.visit(this, expressions);
+    List<Expression> expressions =
+        this.expressions == null
+            ? null
+            : Expressions.acceptExpressions(this.expressions, visitor);
+    Expression bound = Expressions.accept(this.bound, visitor);
+    return visitor.visit(this, dimension, bound, expressions);
   }
 
   @Override
   void accept(ExpressionWriter writer, int lprec, int rprec) {
-    writer.append("new ").append(type).list(" {\n", ",\n", "}", expressions);
+    writer.append("new ").append(Types.getComponentTypeN(type));
+    for (int i = 0; i < dimension; i++) {
+      if (i == 0 && bound != null) {
+        writer.append('[').append(bound).append(']');
+      } else {
+        writer.append("[]");
+      }
+    }
+    if (expressions != null) {
+      writer.list(" {\n", ",\n", "}", expressions);
+    }
   }
 }
 
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java b/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java
index 81bbe56..1fe6a50 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/ParameterExpression.java
@@ -17,6 +17,7 @@
 */
 package net.hydromatic.linq4j.expressions;
 
+import java.lang.reflect.Modifier;
 import java.lang.reflect.Type;
 
 /**
@@ -51,6 +52,19 @@
   void accept(ExpressionWriter writer, int lprec, int rprec) {
     writer.append(name);
   }
+
+  String declString() {
+    return declString(type);
+  }
+
+  String declString(Type type) {
+    final String modifiers = Modifier.toString(modifier);
+    return modifiers
+        + (modifiers.isEmpty() ? "" : " ")
+        + Types.className(type)
+        + " "
+        + name;
+  }
 }
 
 // End ParameterExpression.java
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/ThrowStatement.java b/src/main/java/net/hydromatic/linq4j/expressions/ThrowStatement.java
new file mode 100644
index 0000000..1129295
--- /dev/null
+++ b/src/main/java/net/hydromatic/linq4j/expressions/ThrowStatement.java
@@ -0,0 +1,42 @@
+/*
+// Licensed to Julian Hyde under one or more contributor license
+// agreements. See the NOTICE file distributed with this work for
+// additional information regarding copyright ownership.
+//
+// Julian Hyde licenses this file to you under the Apache License,
+// Version 2.0 (the "License"); you may not use this file except in
+// compliance with the License. You may obtain a copy of the License at:
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+package net.hydromatic.linq4j.expressions;
+
+/**
+ * Represents a {@code throw} statement.
+ */
+public class ThrowStatement extends Statement {
+  public final Expression expression;
+
+  public ThrowStatement(Expression expression) {
+    super(ExpressionType.Throw, Void.TYPE);
+    this.expression = expression;
+  }
+
+  @Override
+  public Statement accept(Visitor visitor) {
+    return visitor.visit(this);
+  }
+
+  @Override
+  void accept0(ExpressionWriter writer) {
+    writer.append("throw ").append(expression).append(';').newlineAndIndent();
+  }
+}
+
+// End ThrowStatement.java
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/TryStatement.java b/src/main/java/net/hydromatic/linq4j/expressions/TryStatement.java
index 35dac9f..4624956 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/TryStatement.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/TryStatement.java
@@ -17,18 +17,42 @@
 */
 package net.hydromatic.linq4j.expressions;
 
+import java.util.List;
+
 /**
- * Represents a try/catch/finally/fault block.
+ * Represents a {@code try ... catch ... finally} block.
  */
 public class TryStatement extends Statement {
-  public TryStatement(ExpressionType nodeType) {
-    super(nodeType, Void.TYPE);
+  public final Statement body;
+  public final List<CatchBlock> catchBlocks;
+  public final Statement finally_;
+
+  public TryStatement(Statement body, List<CatchBlock> catchBlocks,
+      Statement finally_) {
+    super(ExpressionType.Try, body.getType());
+    this.body = body;
+    this.catchBlocks = catchBlocks;
+    this.finally_ = finally_;
   }
 
   @Override
   public Statement accept(Visitor visitor) {
     return visitor.visit(this);
   }
+
+  @Override
+  void accept0(ExpressionWriter writer) {
+    writer.append("try ").append(Blocks.toBlock(body));
+    for (CatchBlock catchBlock : catchBlocks) {
+      writer.backUp();
+      writer.append(" catch (").append(catchBlock.parameter.declString())
+          .append(") ").append(Blocks.toBlock(catchBlock.body));
+    }
+    if (finally_ != null) {
+      writer.backUp();
+      writer.append(" finally ").append(Blocks.toBlock(finally_));
+    }
+  }
 }
 
 // End TryStatement.java
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Types.java b/src/main/java/net/hydromatic/linq4j/expressions/Types.java
index 8951e04..1e5e07c 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Types.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Types.java
@@ -198,6 +198,16 @@
     return null; // not an array type
   }
 
+  static Type getComponentTypeN(Type type) {
+    for (;;) {
+      final Type oldType = type;
+      type = getComponentType(type);
+      if (type == null) {
+        return oldType;
+      }
+    }
+  }
+
   /**
    * Boxes a type, if it is primitive, and returns the type name.
    * The type is abbreviated if it is in the "java.lang" package.
@@ -507,6 +517,13 @@
     return Array.newInstance(toClass(type), 0).getClass();
   }
 
+  static Type arrayType(Type type, int dimension) {
+    for (int i = 0; i < dimension; i++) {
+      type = arrayType(type);
+    }
+    return type;
+  }
+
   static Type arrayType(Type type) {
     if (type instanceof Class) {
       Class clazz = (Class) type;
diff --git a/src/main/java/net/hydromatic/linq4j/expressions/Visitor.java b/src/main/java/net/hydromatic/linq4j/expressions/Visitor.java
index 8e05276..e08ef7e 100644
--- a/src/main/java/net/hydromatic/linq4j/expressions/Visitor.java
+++ b/src/main/java/net/hydromatic/linq4j/expressions/Visitor.java
@@ -58,6 +58,10 @@
     return forStatement;
   }
 
+  public Statement visit(ThrowStatement throwStatement) {
+    return throwStatement;
+  }
+
   public DeclarationStatement visit(DeclarationStatement declarationStatement,
       ParameterExpression parameter, Expression initializer) {
     return declarationStatement.parameter == parameter
@@ -143,12 +147,21 @@
     return invocationExpression;
   }
 
-  public Expression visit(NewArrayExpression newArrayExpression,
-      List<Expression> expressions) {
-    return expressions.equals(newArrayExpression.expressions)
+  static <T> boolean eq(T t0, T t1) {
+    return t0 == t1 || t0 != null && t1 != null && t0.equals(t1);
+  }
+
+  public Expression visit(NewArrayExpression newArrayExpression, int dimension,
+      Expression bound, List<Expression> expressions) {
+    return eq(expressions, newArrayExpression.expressions)
+        && eq(bound, newArrayExpression.bound)
         ? newArrayExpression
-        : Expressions.newArrayInit(Types.getComponentType(
-            newArrayExpression.type), expressions);
+        : expressions == null
+        ? Expressions.newArrayBounds(
+            Types.getComponentTypeN(newArrayExpression.type), dimension, bound)
+        : Expressions.newArrayInit(
+            Types.getComponentTypeN(newArrayExpression.type),
+            dimension, expressions);
   }
 
   public Expression visit(ListInitExpression listInitExpression) {
diff --git a/src/test/java/net/hydromatic/linq4j/function/FunctionTest.java b/src/test/java/net/hydromatic/linq4j/function/FunctionTest.java
index db50930..50e2551 100644
--- a/src/test/java/net/hydromatic/linq4j/function/FunctionTest.java
+++ b/src/test/java/net/hydromatic/linq4j/function/FunctionTest.java
@@ -17,16 +17,11 @@
 */
 package net.hydromatic.linq4j.function;
 
-import net.hydromatic.linq4j.expressions.Types;
-
 import org.junit.Assert;
 import org.junit.Test;
 
 import java.util.*;
 
-import static org.junit.Assert.assertEquals;
-
-
 /**
  * Test for {@link Functions}.
  */
diff --git a/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java b/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
index 03f5a6c..4f1015a 100644
--- a/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
+++ b/src/test/java/net/hydromatic/linq4j/test/ExpressionTest.java
@@ -332,6 +332,7 @@
                     paramX, "length", Collections.<Expression>emptyList()),
                 Arrays.asList(paramX))));
 
+    // 1-dimensional array with initializer
     assertEquals(
         "new String[] {\n"
         + "  \"foo\",\n"
@@ -340,10 +341,48 @@
         Expressions.toString(
             Expressions.newArrayInit(
                 String.class,
-                Arrays.<Expression>asList(
-                    Expressions.constant("foo"),
-                    Expressions.constant(null),
-                    Expressions.constant("bar\"baz")))));
+                Expressions.constant("foo"),
+                Expressions.constant(null),
+                Expressions.constant("bar\"baz"))));
+
+    // 2-dimensional array with initializer
+    assertEquals(
+        "new String[][] {\n"
+        + "  new String[] {\n"
+        + "    \"foo\",\n"
+        + "    \"bar\"},\n"
+        + "  null,\n"
+        + "  new String[] {\n"
+        + "    null}}",
+        Expressions.toString(
+            Expressions.newArrayInit(
+                String.class,
+                2,
+                Expressions.constant(new String[] {"foo", "bar"}),
+                Expressions.constant(null),
+                Expressions.constant(new String[] {null}))));
+
+    // 1-dimensional array
+    assertEquals(
+        "new String[x + 1]",
+        Expressions.toString(
+            Expressions.newArrayBounds(
+                String.class,
+                1,
+                Expressions.add(
+                    Expressions.parameter(0, int.class, "x"),
+                    Expressions.constant(1)))));
+
+    // 3-dimensional array
+    assertEquals(
+        "new String[x + 1][][]",
+        Expressions.toString(
+            Expressions.newArrayBounds(
+                String.class,
+                3,
+                Expressions.add(
+                    Expressions.parameter(0, int.class, "x"),
+                    Expressions.constant(1)))));
 
     assertEquals(
         "(int) ((String) (Object) \"foo\").length()",
@@ -652,6 +691,95 @@
         Expressions.toString(node));
   }
 
+  @Test public void testWriteTryCatchFinally() {
+    final ParameterExpression cce_ =
+        Expressions.parameter(Modifier.FINAL, ClassCastException.class, "cce");
+    final ParameterExpression re_ =
+        Expressions.parameter(0, RuntimeException.class, "re");
+    Node node =
+        Expressions.tryCatchFinally(
+            Expressions.block(
+                Expressions.return_(null,
+                    Expressions.call(
+                        Expressions.constant("foo"),
+                        "length"))),
+            Expressions.statement(
+                Expressions.call(
+                    Expressions.constant("foo"),
+                    "toUpperCase")),
+        Expressions.catch_(cce_,
+            Expressions.return_(null, Expressions.constant(null))),
+        Expressions.catch_(re_,
+            Expressions.throw_(
+                Expressions.new_(IndexOutOfBoundsException.class))));
+    assertEquals(
+        "try {\n"
+        + "  return \"foo\".length();\n"
+        + "} catch (final ClassCastException cce) {\n"
+        + "  return null;\n"
+        + "} catch (RuntimeException re) {\n"
+        + "  throw new IndexOutOfBoundsException();\n"
+        + "} finally {\n"
+        + "  \"foo\".toUpperCase();\n"
+        + "}\n",
+        Expressions.toString(node));
+  }
+
+  @Test public void testWriteTryFinally() {
+    final ParameterExpression cce_ =
+        Expressions.parameter(Modifier.FINAL, ClassCastException.class, "cce");
+    final ParameterExpression re_ =
+        Expressions.parameter(0, RuntimeException.class, "re");
+    Node node =
+        Expressions.ifThen(
+            Expressions.constant(true),
+        Expressions.tryFinally(
+            Expressions.block(
+                Expressions.return_(null,
+                    Expressions.call(
+                        Expressions.constant("foo"),
+                        "length"))),
+            Expressions.statement(
+                Expressions.call(
+                    Expressions.constant("foo"),
+                    "toUpperCase"))));
+    assertEquals(
+        "if (true) {\n"
+        + "  try {\n"
+        + "    return \"foo\".length();\n"
+        + "  } finally {\n"
+        + "    \"foo\".toUpperCase();\n"
+        + "  }\n"
+        + "}\n",
+        Expressions.toString(node));
+  }
+
+  @Test public void testWriteTryCatch() {
+    final ParameterExpression cce_ =
+        Expressions.parameter(Modifier.FINAL, ClassCastException.class, "cce");
+    final ParameterExpression re_ =
+        Expressions.parameter(0, RuntimeException.class, "re");
+    Node node =
+        Expressions.tryCatch(
+            Expressions.block(
+                Expressions.return_(null,
+                    Expressions.call(Expressions.constant("foo"), "length"))),
+            Expressions.catch_(cce_,
+                Expressions.return_(null, Expressions.constant(null))),
+            Expressions.catch_(re_,
+                Expressions.return_(null,
+                    Expressions.call(re_, "toString"))));
+    assertEquals(
+        "try {\n"
+        + "  return \"foo\".length();\n"
+        + "} catch (final ClassCastException cce) {\n"
+        + "  return null;\n"
+        + "} catch (RuntimeException re) {\n"
+        + "  return re.toString();\n"
+        + "}\n",
+        Expressions.toString(node));
+  }
+
   @Test public void testType() {
     // Type of ternary operator is the gcd of its arguments.
     assertEquals(