private void insertProtStores(final Block block, final HashSet tryPreds,
			final ResizeableArrayList defs) {
		final Tree tree = block.tree();

		// Visit all LocalExprs in block. Recall that LocalExpr
		// represents a reference to a local variable. If the LocalExpr
		// defines the variable, then added it to the defs array. defs is
		// indexed by local variable.
		tree.visitChildren(new TreeVisitor() {
			public void visitLocalExpr(final LocalExpr expr) {
				if (expr.isDef()) {
					final int index = expr.index();

					if (expr.type().isWide()) {
						defs.ensureSize(index + 2);
						defs.set(index, expr);
						defs.set(index + 1, null);

					} else {
						defs.ensureSize(index + 1);
						defs.set(index, expr);
					}
				}
			}
		});

		// If block ends in a jump to a block in a protected region, add
		// statements that make a copy of each local variable. This is
		// done to avoid redefining local variables used by the jump
		// statement. I'm not too sure about all of this.

		if (tryPreds.contains(block)) {
			// Examine all of the definitions of all the local variables
			for (int i = 0; i < defs.size(); i++) {
				final LocalExpr expr = (LocalExpr) defs.get(i);

				if (expr != null) {
					// Insert stores before the last stmt to ensure we don't
					// redefine locals used by(?) the branch stmt.
					final Stmt last = tree.lastStmt();

					// Visit the Exprs in the last statement block. Remember
					// that this statement ends in a jump to a protected block.
					// Insert a store of the a copy of all Expr into a stack
					// variable right before the jump. I think this saves all
					// of the expressions in the jump statement to the stack.
					// Why? I don't know.

					last.visitChildren(new TreeVisitor() {
						public void visitExpr(final Expr expr) {
							StackExpr var = tree.newStack(expr.type());
							var.setValueNumber(expr.valueNumber());

							final Node p = expr.parent();
							expr.setParent(null);
							p.visit(new ReplaceVisitor(expr, var));

							var = (StackExpr) var.clone();
							var.setDef(null);
							final StoreExpr store = new StoreExpr(var, expr,
									expr.type());
							store.setValueNumber(expr.valueNumber());

							final Stmt storeStmt = new ExprStmt(store);
							storeStmt.setValueNumber(expr.valueNumber());

							tree.addStmtBeforeJump(storeStmt);
						}

						public void visitStackExpr(final StackExpr expr) {
						}
					});

					// Add assignment statements (StoreExpr) that store a copy
					// of expr (a defining instance of LocalExpr) into itself.

					final LocalExpr copy1 = (LocalExpr) expr.clone();
					final LocalExpr copy2 = (LocalExpr) expr.clone();
					copy1.setDef(null);
					copy2.setDef(null);

					final StoreExpr store = new StoreExpr(copy1, copy2, expr
							.type());

					tree.addStmtBeforeJump(new ExprStmt(store));
				}
			}
		}

		final Iterator children = domChildren(block).iterator();

		// Examine all of the blocks that block dominates. Note that
		// local variables will have the same definitions as in block
		// unless they are overriden in the child.
		while (children.hasNext()) {
			final Block child = (Block) children.next();
			insertProtStores(child, tryPreds, new ResizeableArrayList(defs));
		}
	}