[clojure] 01/01: Imported Upstream version 1.6.0

Daigo Moriwaki daigo at moszumanska.debian.org
Sat Jun 14 06:16:41 UTC 2014


This is an automated email from the git hooks/post-receive script.

daigo pushed a commit to annotated tag upstream/1.6.0
in repository clojure.

commit d794b8510afc99eb8d6adf8148ac434f76fd2aee
Author: Daigo Moriwaki <daigo at debian.org>
Date:   Sat Jun 14 10:08:22 2014 +0900

    Imported Upstream version 1.6.0
---
 build.xml                                          |   32 +-
 changes.md                                         | 1323 +++++++++-
 clojure.iml                                        |    8 +-
 doc/clojure/pprint/CommonLispFormat.markdown       |    4 +-
 doc/clojure/pprint/PrettyPrinting.markdown         |    6 +-
 pom.xml                                            |   74 +-
 readme.txt                                         |  189 +-
 src/clj/clojure/core.clj                           |  629 +++--
 src/clj/clojure/core/protocols.clj                 |   39 +-
 src/clj/clojure/core/reducers.clj                  |  367 +++
 src/clj/clojure/core_deftype.clj                   |   89 +-
 src/clj/clojure/core_print.clj                     |   52 +-
 src/clj/clojure/core_proxy.clj                     |    8 +-
 src/clj/clojure/data.clj                           |   18 +-
 src/clj/clojure/edn.clj                            |   46 +
 src/clj/clojure/genclass.clj                       |   32 +-
 src/clj/clojure/gvec.clj                           |   13 +-
 src/clj/clojure/inspector.clj                      |   12 +-
 src/clj/clojure/instant.clj                        |    6 +-
 src/clj/clojure/java/browse.clj                    |   34 +-
 src/clj/clojure/java/browse_ui.clj                 |    2 +-
 src/clj/clojure/java/io.clj                        |   75 +-
 src/clj/clojure/java/javadoc.clj                   |    1 -
 src/clj/clojure/main.clj                           |   55 +-
 src/clj/clojure/pprint/cl_format.clj               |  101 +-
 src/clj/clojure/pprint/column_writer.clj           |    2 +
 src/clj/clojure/pprint/dispatch.clj                |   77 +-
 src/clj/clojure/pprint/pprint_base.clj             |    2 +-
 src/clj/clojure/pprint/pretty_writer.clj           |   10 +-
 src/clj/clojure/pprint/print_table.clj             |   27 +-
 src/clj/clojure/pprint/utilities.clj               |    3 +
 src/clj/clojure/reflect/java.clj                   |   20 +-
 src/clj/clojure/repl.clj                           |   35 +-
 src/clj/clojure/set.clj                            |   11 +-
 src/clj/clojure/stacktrace.clj                     |    6 +-
 src/clj/clojure/string.clj                         |  116 +-
 src/clj/clojure/test.clj                           |   35 +-
 src/clj/clojure/test/tap.clj                       |   49 +-
 src/clj/clojure/walk.clj                           |    2 +
 src/jvm/clojure/asm/AnnotationVisitor.java         |  169 ++
 src/jvm/clojure/asm/AnnotationWriter.java          |  318 +++
 src/jvm/clojure/asm/Attribute.java                 |  255 ++
 src/jvm/clojure/asm/ByteVector.java                |  306 +++
 src/jvm/clojure/asm/ClassReader.java               | 2202 ++++++++++++++++
 src/jvm/clojure/asm/ClassVisitor.java              |  286 +++
 src/jvm/clojure/asm/ClassWriter.java               | 1693 ++++++++++++
 src/jvm/clojure/asm/Context.java                   |  110 +
 src/jvm/clojure/asm/Edge.java                      |   75 +
 src/jvm/clojure/asm/FieldVisitor.java              |  121 +
 src/jvm/clojure/asm/FieldWriter.java               |  273 ++
 src/jvm/clojure/asm/Frame.java                     | 1453 +++++++++++
 src/jvm/clojure/asm/Handle.java                    |  167 ++
 src/jvm/clojure/asm/Handler.java                   |  121 +
 src/jvm/clojure/asm/Item.java                      |  311 +++
 src/jvm/clojure/asm/Label.java                     |  560 ++++
 src/jvm/clojure/asm/MethodVisitor.java             |  662 +++++
 src/jvm/clojure/asm/MethodWriter.java              | 2685 ++++++++++++++++++++
 src/jvm/clojure/asm/Opcodes.java                   |  358 +++
 src/jvm/clojure/asm/Type.java                      |  895 +++++++
 src/jvm/clojure/asm/commons/AdviceAdapter.java     |  625 +++++
 src/jvm/clojure/asm/commons/AnalyzerAdapter.java   |  920 +++++++
 src/jvm/clojure/asm/commons/CodeSizeEvaluator.java |  217 ++
 src/jvm/clojure/asm/commons/GeneratorAdapter.java  | 1623 ++++++++++++
 .../clojure/asm/commons/InstructionAdapter.java    | 1090 ++++++++
 .../clojure/asm/commons/LocalVariablesSorter.java  |  360 +++
 src/jvm/clojure/asm/commons/Method.java            |  282 ++
 .../clojure/asm/commons/SerialVersionUIDAdder.java |  533 ++++
 src/jvm/clojure/asm/commons/StaticInitMerger.java  |   96 +
 .../clojure/asm/commons/TableSwitchGenerator.java  |   57 +
 src/jvm/clojure/asm/commons/package.html           |   48 +
 src/jvm/clojure/asm/package.html                   |   87 +
 src/jvm/clojure/java/api/Clojure.java              |  100 +
 src/jvm/clojure/java/api/package.html              |   79 +
 src/jvm/clojure/lang/AFn.java                      |   12 +-
 src/jvm/clojure/lang/AFunction.java                |   25 +-
 src/jvm/clojure/lang/APersistentMap.java           |   23 +-
 src/jvm/clojure/lang/APersistentSet.java           |   70 +-
 src/jvm/clojure/lang/APersistentVector.java        |   56 +-
 src/jvm/clojure/lang/ARef.java                     |   30 +-
 src/jvm/clojure/lang/ASeq.java                     |   14 +-
 src/jvm/clojure/lang/Agent.java                    |   20 +-
 src/jvm/clojure/lang/ArrayChunk.java               |    6 +
 src/jvm/clojure/lang/ArraySeq.java                 |  175 +-
 src/jvm/clojure/lang/BigInt.java                   |   20 +-
 src/jvm/clojure/lang/Compile.java                  |    1 -
 src/jvm/clojure/lang/Compiler.java                 |  747 ++++--
 src/jvm/clojure/lang/Delay.java                    |   13 +-
 src/jvm/clojure/lang/EdnReader.java                |  737 ++++++
 src/jvm/clojure/lang/ExceptionInfo.java            |    2 +-
 src/jvm/clojure/lang/IExceptionInfo.java           |   20 +
 src/jvm/clojure/lang/IFn.java                      |    6 +
 src/jvm/clojure/lang/Intrinsics.java               |    2 +
 src/jvm/clojure/lang/Keyword.java                  |   12 +-
 src/jvm/clojure/lang/LazySeq.java                  |   20 +-
 .../clojure/lang/LineNumberingPushbackReader.java  |   22 +-
 src/jvm/clojure/lang/LispReader.java               |  215 +-
 src/jvm/clojure/lang/LockingTransaction.java       |   26 +-
 src/jvm/clojure/lang/MultiFn.java                  |  168 +-
 src/jvm/clojure/lang/Murmur3.java                  |  151 ++
 src/jvm/clojure/lang/Numbers.java                  |   54 +-
 src/jvm/clojure/lang/PersistentArrayMap.java       |   93 +-
 src/jvm/clojure/lang/PersistentHashMap.java        |  123 +-
 src/jvm/clojure/lang/PersistentList.java           |    9 +-
 src/jvm/clojure/lang/PersistentQueue.java          |   43 +-
 src/jvm/clojure/lang/PersistentTreeMap.java        |   19 +-
 src/jvm/clojure/lang/PersistentVector.java         |   55 +-
 src/jvm/clojure/lang/RT.java                       |   57 +-
 src/jvm/clojure/lang/{Delay.java => Reduced.java}  |   27 +-
 src/jvm/clojure/lang/Ref.java                      |   18 +-
 src/jvm/clojure/lang/Reflector.java                |   28 +-
 src/jvm/clojure/lang/Symbol.java                   |   17 +-
 src/jvm/clojure/lang/TransactionalHashMap.java     |   20 +-
 src/jvm/clojure/lang/Util.java                     |   57 +-
 src/jvm/clojure/lang/Var.java                      |  335 ++-
 src/jvm/clojure/lang/package.html                  |   21 +
 src/script/run_tests.clj                           |   65 +-
 test/clojure/test_clojure/agents.clj               |   33 +-
 test/clojure/test_clojure/annotations.clj          |    1 -
 test/clojure/test_clojure/annotations/java_5.clj   |   14 +
 test/clojure/test_clojure/annotations/java_6.clj   |   14 +
 test/clojure/test_clojure/api.clj                  |   54 +
 test/clojure/test_clojure/clojure_set.clj          |    5 +-
 test/clojure/test_clojure/clojure_walk.clj         |    6 +-
 test/clojure/test_clojure/compilation.clj          |  135 +-
 test/clojure/test_clojure/control.clj              |   18 +-
 test/clojure/test_clojure/data.clj                 |    3 +-
 test/clojure/test_clojure/data_structures.clj      |  271 +-
 test/clojure/test_clojure/def.clj                  |   51 +-
 test/clojure/test_clojure/delays.clj               |   21 +
 test/clojure/test_clojure/edn.clj                  |   38 +
 test/clojure/test_clojure/errors.clj               |   15 +-
 test/clojure/test_clojure/fn.clj                   |   55 +
 test/clojure/test_clojure/genclass.clj             |    8 +
 test/clojure/test_clojure/genclass/examples.clj    |   11 +
 test/clojure/test_clojure/generators.clj           |  132 +
 test/clojure/test_clojure/java/io.clj              |    8 +-
 test/clojure/test_clojure/java_interop.clj         |   45 +-
 test/clojure/test_clojure/load.clj                 |   32 -
 test/clojure/test_clojure/load/cyclic0.clj         |   12 -
 test/clojure/test_clojure/load/cyclic1.clj         |   12 -
 test/clojure/test_clojure/load/cyclic2.clj         |   12 -
 test/clojure/test_clojure/load/cyclic3.clj         |   12 -
 test/clojure/test_clojure/load/cyclic4.clj         |   12 -
 test/clojure/test_clojure/load/cyclic5.clj         |   12 -
 test/clojure/test_clojure/load/cyclic6.clj         |   12 -
 test/clojure/test_clojure/logic.clj                |    7 +
 test/clojure/test_clojure/macros.clj               |   51 +
 test/clojure/test_clojure/metadata.clj             |  144 +-
 test/clojure/test_clojure/multimethods.clj         |   74 +
 test/clojure/test_clojure/numbers.clj              |  171 +-
 test/clojure/test_clojure/other_functions.clj      |   38 +
 test/clojure/test_clojure/parallel.clj             |   11 +
 test/clojure/test_clojure/pprint.clj               |    3 +-
 .../clojure/test_clojure/pprint/test_cl_format.clj |  140 +
 test/clojure/test_clojure/pprint/test_pretty.clj   |  157 +-
 test/clojure/test_clojure/protocols.clj            |   49 +
 test/clojure/test_clojure/protocols/examples.clj   |    5 +
 test/clojure/test_clojure/reader.clj               |  117 +-
 test/clojure/test_clojure/reducers.clj             |   91 +
 test/clojure/test_clojure/reflect.clj              |    5 +-
 test/clojure/test_clojure/repl.clj                 |   28 +-
 test/clojure/test_clojure/rt.clj                   |   27 +-
 test/clojure/test_clojure/sequences.clj            |   18 +-
 test/clojure/test_clojure/special.clj              |   41 +
 test/clojure/test_clojure/string.clj               |   39 +-
 test/clojure/test_clojure/test.clj                 |   10 +-
 test/clojure/test_clojure/test_fixtures.clj        |   29 +-
 test/clojure/test_clojure/transients.clj           |   16 +-
 test/clojure/test_clojure/try_catch.clj            |    0
 test/clojure/test_clojure/vars.clj                 |    9 +
 test/clojure/test_clojure/vectors.clj              |   23 +-
 171 files changed, 27090 insertions(+), 1648 deletions(-)

diff --git a/build.xml b/build.xml
index 031c2e6..97ab199 100644
--- a/build.xml
+++ b/build.xml
@@ -17,6 +17,7 @@
   <property name="build" location="${target}/classes"/>
   <property name="test-classes" location="${target}/test-classes"/>
   <property name="dist" location="dist"/>
+  <property file="maven-classpath.properties"/>
 
   <!-- Get the version string out of the POM -->
   <xmlproperty file="pom.xml" prefix="pom"/>
@@ -37,23 +38,25 @@
           description="Compile Java sources.">
     <javac srcdir="${jsrc}" destdir="${build}" includeJavaRuntime="yes"
            includeAntRuntime="false"
-           debug="true" source="1.5" target="1.5"/>
+           debug="true" source="1.6" target="1.6"/>
   </target>
 
   <target name="compile-clojure"
           description="Compile Clojure sources.">
     <java classname="clojure.lang.Compile"
-          classpath="${build}:${cljsrc}"
+          classpath="${maven.compile.classpath}:${build}:${cljsrc}"
           failonerror="true"
           fork="true">
       <sysproperty key="clojure.compile.path" value="${build}"/>
          <!--<sysproperty key="clojure.compiler.elide-meta" value="[:doc :file :line :added]"/>-->
          <!--<sysproperty key="clojure.compiler.disable-locals-clearing" value="true"/>-->
       <!-- <sysproperty key="clojure.compile.warn-on-reflection" value="true"/> -->
+      <sysproperty key="java.awt.headless" value="true"/>
       <arg value="clojure.core"/>
       <arg value="clojure.core.protocols"/>
       <arg value="clojure.main"/>
       <arg value="clojure.set"/>
+      <arg value="clojure.edn"/>
       <arg value="clojure.xml"/>
       <arg value="clojure.zip"/>
       <arg value="clojure.inspector"/>
@@ -81,7 +84,7 @@
           unless="maven.test.skip">
     <mkdir dir="${test-classes}"/>
     <javac srcdir="${jtestsrc}" destdir="${test-classes}" includeJavaRuntime="yes"
-           debug="true" source="1.5" target="1.5" includeantruntime="no"/>
+           debug="true" source="1.6" target="1.6" includeantruntime="no"/>
     <java classname="clojure.lang.Compile"
           classpath="${test-classes}:${test}:${build}:${cljsrc}"
           failonerror="true"
@@ -91,6 +94,7 @@
         <!--<sysproperty key="clojure.compiler.disable-locals-clearing" value="true"/>-->
       <arg value="clojure.test-clojure.protocols.examples"/>
       <arg value="clojure.test-clojure.genclass.examples"/>
+      <arg value="clojure.test-clojure.annotations"/>
     </java>
   </target>
 
@@ -100,6 +104,7 @@
           unless="maven.test.skip">
     <java classname="clojure.main" failonerror="true" fork="true">
       <classpath>
+        <pathelement path="${maven.test.classpath}"/>
         <path location="${test-classes}"/>
         <path location="${test}"/>
         <path location="${build}"/>
@@ -126,7 +131,26 @@
     </jar>
     <copy file="${clojure_jar}" tofile="${clojure_noversion_jar}"/>
   </target>
-  
+
+  <target name="javadoc"
+	  description="Creates javadoc for Clojure API.">
+    <copy file="src/jvm/clojure/lang/IFn.java" tofile="target/tmpjd/IFn.java"/>
+    <copy file="src/jvm/clojure/lang/package.html" tofile="target/tmpjd/package.html"/>
+    <replaceregexp file="target/tmpjd/IFn.java" match="(static public interface .*})" replace="" byline="true"/>
+    <javadoc destdir="target/javadoc"
+	    nodeprecatedlist="true" nohelp="true" nonavbar="true" notree="true"
+	    link="http://docs.oracle.com/javase/7/docs/api/"
+	    windowtitle="Clojure API">
+      <classpath>
+        <path location="${build}"/>
+      </classpath>
+      <fileset dir="${basedir}">
+	<include name="src/jvm/clojure/java/api/Clojure.java"/>
+	<include name="target/tmpjd/IFn.java"/>
+      </fileset>
+    </javadoc>
+  </target>
+
   <target name="all" depends="build,test,jar"/>
 
   <target name="clean"
diff --git a/changes.md b/changes.md
index bafe22b..376116d 100644
--- a/changes.md
+++ b/changes.md
@@ -1,5 +1,822 @@
 <!-- -*- mode: markdown ; mode: visual-line ; coding: utf-8 -*- -->
 
+# Changes to Clojure in Version 1.6
+
+## CONTENTS
+
+## 1 Compatibility and Dependencies
+
+## 1.1 JDK Version Update
+
+Clojure now builds with Java SE 1.6 and emits bytecode requiring Java
+SE 1.6 instead of Java SE 1.5. [CLJ-1268]
+
+## 1.2 ASM Library Update
+
+The embedded version of the ASM bytecode library has been upgraded to
+ASM 4.1. [CLJ-713]
+
+## 1.3 Promoted "Alpha" Features
+
+The following features are no longer marked Alpha in Clojure:
+
+* Watches - add-watch, remove-watch
+* Transients - transient, persistent!, conj!, assoc!, dissoc!, pop!, disj!
+* Exception data - ex-info, ex-data
+* Promises - promise, deliver
+* Records - defrecord
+* Types - deftype
+* Pretty-print tables - print-table
+
+## 2 New and Improved Features
+
+### 2.1 Java API
+
+The clojure.java.api package provides a minimal interface to bootstrap
+Clojure access from other JVM languages. It does this by providing:
+1. The ability to use Clojure's namespaces to locate an arbitrary var,
+   returning the var's clojure.lang.IFn interface.
+2. A convenience method read for reading data using Clojure's edn
+   reader.
+
+IFns provide complete access to Clojure's APIs. You can also access
+any other library written in Clojure, after adding either its source
+or compiled form to the classpath.
+
+The public Java API for Clojure consists of the following classes and interfaces:
+
+* clojure.java.api.Clojure
+* clojure.lang.IFn
+
+All other Java classes should be treated as implementation details,
+and applications should avoid relying on them.
+
+To look up and call a Clojure function:
+
+    IFn plus = Clojure.var("clojure.core", "+");
+    plus.invoke(1, 2);
+
+Functions in clojure.core are automatically loaded. Other namespaces
+can be loaded via require:
+
+    IFn require = Clojure.var("clojure.core", "require");
+    require.invoke(Clojure.read("clojure.set"));
+   
+IFns can be passed to higher order functions, e.g. the example below
+passes plus to read:
+
+    IFn map = Clojure.var("clojure.core", "map");
+    IFn inc = Clojure.var("clojure.core", "inc");
+    map.invoke(inc, Clojure.read("[1 2 3]"));
+
+Most IFns in Clojure refer to functions. A few, however, refer to
+non-function data values. To access these, use deref instead of fn:
+
+    IFn printLength = Clojure.var("clojure.core", "*print-length*");
+    Clojure.var("clojure.core", "deref").invoke(printLength);
+
+### 2.2 Map destructuring extended to support namespaced keys
+
+* [CLJ-1318](http://dev.clojure.org/jira/browse/CLJ-1318)
+
+In the past, map destructuring with :keys and :syms would not work
+with maps containing namespaced keys or symbols. The :keys and :syms
+forms have been updated to allow them to match namespaced keys and
+bind to a local variable based on the name.
+
+Examples:
+
+    (let [m {:x/a 1, :y/b 2}
+          {:keys [x/a y/b]} m]
+      (+ a b))
+
+    (let [m {'x/a 1, 'y/b 2}
+          {:syms [x/a y/b]} m]
+      (+ a b))
+
+Additionally, the :keys form can now take keywords instead of symbols.
+This provides support specifically for auto-resolved keywords:
+
+    (let [m {:x/a 1, :y/b 2}
+          {:keys [:x/a :y/b]} m]
+      (+ a b))
+
+    (let [m {::x 1}
+          {:keys [::x]} m]
+      x)
+
+### 2.3 New "some" operations
+
+Many conditional functions rely on logical truth (where "falsey"
+values are nil or false). Sometimes it is useful to have functions
+that rely on "not nilness" instead. These functions have been added to
+support these cases [CLJ-1343]:
+
+* some? - same as (not (nil? x))
+* if-some - like if-let, but checks (some? test) instead of test
+* when-some - like when-let, but checks (some? test) instead of test
+
+### 2.4 Hashing
+
+Clojure 1.6 provides new hashing algorithms for primitives and
+collections, accessible via IHashEq/hasheq (in Java) or the
+clojure.core/hash function (in Clojure). In general, these changes
+should be transparent to users, except hash codes used inside hashed
+collections like maps and sets will have better properties.
+
+Hash codes returned by the Java .hashCode() method are unchanged and
+continue to match Java behavior or conform to the Java specification
+as appropriate.
+
+Any collections implementing IHashEq or wishing to interoperate with
+Clojure collections should conform to the hashing algorithms specified
+in http://clojure.org/data_structures#hash and use the new function
+`mix-collection-hash` for the final mixing operation. Alternatively,
+you may call the helper functions `hash-ordered-coll` and
+`hash-unordered-coll`.
+
+Any details of the current hashing algorithm not specified on that
+page should be considered subject to future change.
+
+Related tickets for dev and regressions:
+
+* [CLJ-1328](http://dev.clojure.org/jira/browse/CLJ-1328)
+  Make several Clojure tests independent of ordering
+* [CLJ-1331](http://dev.clojure.org/jira/browse/CLJ-1331)
+  Update primitive vectors to use Murmur3 hash
+* [CLJ-1335](http://dev.clojure.org/jira/browse/CLJ-1335)
+  Update hash for empty PersistentList and LazySeq
+* [CLJ-1336](http://dev.clojure.org/jira/browse/CLJ-1336)
+  Make hashing mixing functions available in Clojure
+* [CLJ-1338](http://dev.clojure.org/jira/browse/CLJ-1338)
+  Make Murmur3 class public
+* [CLJ-1344](http://dev.clojure.org/jira/browse/CLJ-1344)
+  Update mapHasheq to call Murmur3 algorithm
+* [CLJ-1348](http://dev.clojure.org/jira/browse/CLJ-1348)
+  Add hash-ordered-coll and hash-unordered-coll
+* [CLJ-1355](http://dev.clojure.org/jira/browse/CLJ-1355)
+  Restore cached hashCode for Symbol and (uncached) hashCode for Keyword
+* [CLJ-1365](http://dev.clojure.org/jira/browse/CLJ-1365)
+  Add type hints for new collection hash functions
+
+### 2.5 bitops
+
+* [CLJ-827](http://dev.clojure.org/jira/browse/CLJ-827) - unsigned-bit-shift-right
+
+A new unsigned-bit-shift-right (Java's >>>) has been added to the core
+library. The shift distance is truncated to the least 6 bits (per the
+Java specification for long >>>).
+
+Examples:
+  (unsigned-bit-shift-right 2r100 1) ;; 2r010
+  (unsigned-bit-shift-right 2r100 2) ;; 2r001
+  (unsigned-bit-shift-right 2r100 3) ;; 2r000
+
+### 2.6 clojure.test
+
+* [CLJ-866](http://dev.clojure.org/jira/browse/CLJ-866) - test-vars
+* [CLJ-1352](http://dev.clojure.org/jira/browse/CLJ-1352) - fix
+  regression in CLJ-866
+
+Added a new clojure.test/test-vars function that takes a list of vars, groups them by namespace, and
+runs them *with their fixtures*.
+
+## 3 Enhancements
+
+### 3.1 Printing
+
+* [CLJ-908](http://dev.clojure.org/jira/browse/CLJ-908)
+  Print metadata for functions when *print-meta* is true and remove errant space at beginning.
+* [CLJ-937](http://dev.clojure.org/jira/browse/CLJ-937)
+  pprint cl-format now supports E, F, and G formats for ratios.
+
+### 3.2 Error messages
+
+* [CLJ-1248](http://dev.clojure.org/jira/browse/CLJ-1248)
+  Include type information in reflection warning messages
+* [CLJ-1099](http://dev.clojure.org/jira/browse/CLJ-1099)
+  If non-seq passed where seq is needed, error message now is an
+  ExceptionInfo with the instance value, retrievable via ex-data.
+* [CLJ-1083](http://dev.clojure.org/jira/browse/CLJ-1083)
+  Fix error message reporting for "munged" function names (like a->b).
+* [CLJ-1056](http://dev.clojure.org/jira/browse/CLJ-1056)
+  Handle more cases and improve error message for errors in defprotocol definitions.
+* [CLJ-1102](http://dev.clojure.org/jira/browse/CLJ-1102)
+  Better handling of exceptions with empty stack traces.
+* [CLJ-939](http://dev.clojure.org/jira/browse/CLJ-939)
+  Exceptions thrown in the top level ns form are reported without file or line number.
+
+### 3.3 Documentation strings
+
+* [CLJ-1164](http://dev.clojure.org/jira/browse/CLJ-1164)
+  Fix typos in clojure.instant/validated and other internal instant functions.
+* [CLJ-1143](http://dev.clojure.org/jira/browse/CLJ-1143)
+  Correct doc string for ns macro.
+* [CLJ-196](http://dev.clojure.org/jira/browse/CLJ-196)
+  Clarify value of *file* is undefined in the REPL.
+* [CLJ-1228](http://dev.clojure.org/jira/browse/CLJ-1228)
+  Fix a number of spelling errors in namespace and doc strings.
+* [CLJ-835](http://dev.clojure.org/jira/browse/CLJ-835)
+  Update defmulti doc to clarify expectations for hierarchy argument.
+* [CLJ-1304](http://dev.clojure.org/jira/browse/CLJ-1304)
+  Fix minor typos in documentation and comments
+* [CLJ-1302](http://dev.clojure.org/jira/browse/CLJ-1302)
+  Mention that keys and vals order are consistent with seq order
+
+### 3.4 Performance
+
+* [CLJ-858](http://dev.clojure.org/jira/browse/CLJ-858)
+  Improve speed of STM by removing System.currentTimeMillis.
+* [CLJ-669](http://dev.clojure.org/jira/browse/CLJ-669)
+  clojure.java.io/do-copy: use java.nio for Files
+* [commit](https://github.com/clojure/clojure/commit/0b73494c3c855e54b1da591eeb687f24f608f346)
+  Reduce overhead of protocol callsites by removing unneeded generated
+  cache fields.
+
+### 3.5 Other enhancements
+
+* [CLJ-908](http://dev.clojure.org/jira/browse/CLJ-908)
+  Make *default-data-reader-fn* set!-able in REPL, similar to *data-readers*.
+* [CLJ-783](http://dev.clojure.org/jira/browse/CLJ-783)
+  Make clojure.inspector/inspect-tree work on sets.
+* [CLJ-896](http://dev.clojure.org/jira/browse/CLJ-896)
+  Make browse-url aware of xdg-open.
+* [CLJ-1160](http://dev.clojure.org/jira/browse/CLJ-1160)
+  Fix clojure.core.reducers/mapcat does not stop on reduced? values.
+* [CLJ-1121](http://dev.clojure.org/jira/browse/CLJ-1121)
+  -> and ->> have been rewritten to work with a broader set of macros.
+* [CLJ-1105](http://dev.clojure.org/jira/browse/CLJ-1105)
+  clojure.walk now supports records.
+* [CLJ-949](http://dev.clojure.org/jira/browse/CLJ-949)
+  Removed all unnecessary cases of sneakyThrow.
+* [CLJ-1238](http://dev.clojure.org/jira/browse/CLJ-1238)
+  Allow EdnReader to read foo// (matches LispReader behavior).
+* [CLJ-1264](http://dev.clojure.org/jira/browse/CLJ-1264)
+  Remove uses of _ as a var in the Java code (causes warning in Java 8).
+* [CLJ-394](http://dev.clojure.org/jira/browse/CLJ-394)
+  Add record? predicate.
+* [CLJ-1200](http://dev.clojure.org/jira/browse/CLJ-1200)
+  ArraySeq dead code cleanup, ArraySeq_short support added.
+* [CLJ-1331](http://dev.clojure.org/jira/browse/CLJ-1331)
+  Primitive vectors should implement hasheq and use new hash algorithm
+* [CLJ-1354](http://dev.clojure.org/jira/browse/CLJ-1354)
+  Make APersistentVector.SubVector public so other collections can access
+* [CLJ-1353](http://dev.clojure.org/jira/browse/CLJ-1353)
+  Make awt run headless during the build process
+
+## 4 Bug Fixes
+
+* [CLJ-1018](http://dev.clojure.org/jira/browse/CLJ-1018)
+  Make range consistently return infinite sequence of start with a step of 0.
+* [CLJ-863](http://dev.clojure.org/jira/browse/CLJ-863)
+  Make interleave return () on 0 args and identity on 1 args.
+* [CLJ-1072](http://dev.clojure.org/jira/browse/CLJ-1072)
+  Update internal usages of the old metadata reader syntax to new syntax.
+* [CLJ-1193](http://dev.clojure.org/jira/browse/CLJ-1193)
+  Make bigint and biginteger functions work on double values outside long range.
+* [CLJ-1154](http://dev.clojure.org/jira/browse/CLJ-1154)
+  Make Compile.java flush but not close stdout so errors can be reported.
+* [CLJ-1161](http://dev.clojure.org/jira/browse/CLJ-1161)
+  Remove bad version.properties from sources jar.
+* [CLJ-1175](http://dev.clojure.org/jira/browse/CLJ-1175)
+  Fix invalid behavior of Delay/deref if an exception is thrown - exception will
+  now be rethrown on subsequent calls and not enter a corrupted state.
+* [CLJ-1171](http://dev.clojure.org/jira/browse/CLJ-1171)
+  Fix several issues with instance? to make it consistent when used with apply.
+* [CLJ-1202](http://dev.clojure.org/jira/browse/CLJ-1202)
+  Protocol fns with dashes may get incorrectly compiled into field accesses.
+* [CLJ-850](http://dev.clojure.org/jira/browse/CLJ-850)
+  Add check to emit invokePrim with return type of double or long if type-hinted.
+* [CLJ-1177](http://dev.clojure.org/jira/browse/CLJ-1177)
+  clojure.java.io URL to File coercion corrupts path containing UTF-8 characters.
+* [CLJ-1234](http://dev.clojure.org/jira/browse/CLJ-1234)
+  Accept whitespace in Record and Type reader forms (similar to data literals).
+* [CLJ-1233](http://dev.clojure.org/jira/browse/CLJ-1233)
+  Allow ** as a valid symbol name without triggering dynamic warnings.
+* [CLJ-1246](http://dev.clojure.org/jira/browse/CLJ-1246)
+  Add support to clojure.reflect for classes with annotations.
+  * [CLJ-1184](http://dev.clojure.org/jira/browse/CLJ-1184)
+  Evaling #{do ...} or [do ...] is treated as do special form.
+* [CLJ-1090](http://dev.clojure.org/jira/browse/CLJ-1090)
+  Indirect function calls through Var instances fail to clear locals.
+* [CLJ-1076](http://dev.clojure.org/jira/browse/CLJ-1076)
+  pprint tests fail on Windows, expecting \n.
+* [CLJ-766](http://dev.clojure.org/jira/browse/CLJ-766)
+  Make into-array work consistently with short-array and byte-array on
+  bigger types.
+* [CLJ-1285](http://dev.clojure.org/jira/browse/CLJ-1285)
+  Data structure invariants are violated after persistent operations when
+  collision node created by transients.
+* [CLJ-1222](http://dev.clojure.org/jira/browse/CLJ-1222)
+  Multiplication overflow issues around Long/MIN_VALUE
+* [CLJ-1118](http://dev.clojure.org/jira/browse/CLJ-1118)
+  Inconsistent numeric comparison semantics between BigDecimals and other numerics
+* [CLJ-1125](http://dev.clojure.org/jira/browse/CLJ-1125)
+  Clojure can leak memory in a servlet container when using dynamic
+  bindings or STM transactions.
+* [CLJ-1082](http://dev.clojure.org/jira/browse/CLJ-1082)
+  Subvecs of primitve vectors cannot be reduced
+* [CLJ-1301](http://dev.clojure.org/jira/browse/CLJ-1301)
+  Case expressions use a mixture of hashCode and hasheq, potentially
+  leading to missed case matches when these differ.
+* [CLJ-983](http://dev.clojure.org/jira/browse/CLJ-983)
+  proxy-super does not restore original binding if call throws exception
+* [CLJ-1176](http://dev.clojure.org/jira/browse/CLJ-1176)
+  clojure.repl/source errors when *read-eval* bound to :unknown
+* [CLJ-935](http://dev.clojure.org/jira/browse/CLJ-935)
+  clojure.string/trim uses different definition of whitespace than
+  triml and trimr
+* [CLJ-1058](http://dev.clojure.org/jira/browse/CLJ-1058)
+  StackOverflowError on exception in reducef for PersistentHashMap
+  fold
+* [CLJ-1328](http://dev.clojure.org/jira/browse/CLJ-1328)
+  Fix some tests in the Clojure test suite to make their names unique
+  and independent of hashing order
+* [CLJ-1339](http://dev.clojure.org/jira/browse/CLJ-1339)
+  Empty primitive vectors throw NPE on .equals with non-vector
+  sequential types
+* [CLJ-1363](http://dev.clojure.org/jira/browse/CLJ-1363)
+  Field access via .- in reflective case does not work
+* [CLJ-944](http://dev.clojure.org/jira/browse/CLJ-944)
+  Compiler gives constant collections types which mismatch their
+  runtime values
+* [CLJ-1387](http://dev.clojure.org/jira/browse/CLJ-1387)
+  reduce-kv on large hash maps ignores reduced result
+
+# Changes to Clojure in Version 1.5.1
+
+* fix for leak caused by ddc65a96fdb1163b
+
+# Changes to Clojure in Version 1.5
+
+## CONTENTS
+
+<pre>
+ 1 Deprecated and Removed Features
+    1.1 Clojure 1.5 reducers library requires Java 6 or later
+ 2 New and Improved Features
+    2.1 Reducers
+    2.2 Reader Literals improved
+    2.3 clojure.core/set-agent-send-executor!, set-agent-send-off-executor!, and send-via
+    2.4 New threading macros
+    2.5 Column metadata captured by reader
+    2.6 gen-class improvements
+    2.7 Support added for marker protocols
+    2.8 clojure.pprint/print-table output compatible with Emacs Org mode
+    2.9 clojure.string/replace and replace-first handle special characters more predictably
+    2.10 Set and map constructor functions allow duplicates
+    2.11 More functions preserve metadata
+    2.12 New edn reader, improvements to *read-eval*
+ 3 Performance Enhancements
+ 4 Improved error messages
+ 5 Improved documentation strings
+ 6 Bug Fixes
+ 7 Binary Compatibility Notes
+</pre>
+
+## 1 Deprecated and Removed Features
+
+### 1.1 Clojure 1.5 reducers library requires Java 6 or later
+
+The new reducers library (see below) requires Java 6 plus a ForkJoin
+library, or Java 7 or later.  Clojure 1.5 can still be compiled and
+run with Java 5.  The only limitations with Java 5 are that the new
+reducers library will not work, and building Clojure requires skipping
+the test suite (e.g. by using the command "ant jar").
+
+
+## 2 New and Improved Features
+
+### 2.1 Reducers
+
+Reducers provide a set of high performance functions for working with collections. The actual fold/reduce algorithms are specified via the collection being reduced. This allows each collection to define the most efficient way to reduce its contents.
+
+The implementation details of reducers are available at the  [Clojure blog](http://clojure.com/blog/2012/05/08/reducers-a-library-and-model-for-collection-processing.html) and therefore won't be repeated in these change notes. However, as a summary:
+
+* There is a new namespace: clojure.core.reducers
+* It contains new versions of map, filter etc based upon transforming reducing functions - reducers
+* It contains a new function, fold, which is a parallel reduce+combine
+fold uses fork/join when working with (the existing!) Clojure vectors and maps
+* Your new parallel code has exactly the same shape as your existing seq-based code
+* The reducers are composable
+* Reducer implementations are primarily functional - no iterators
+* The model uses regular data structures, not 'parallel collections' or other OO malarkey
+* It's fast, and can become faster still
+* This is work-in-progress
+
+Examples:
+
+	user=> (require '[clojure.core.reducers :as r])
+	user=> (reduce + (r/filter even? (r/map inc [1 1 1 2])))
+	;=> 6
+
+
+	;;red is a reducer awaiting a collection
+	user=> (def red (comp (r/filter even?) (r/map inc)))
+	user=> (reduce + (red [1 1 1 2]))
+	;=> 6
+
+	user=> (into #{} (r/filter even? (r/map inc [1 1 1 2])))
+	;=> #{2}
+
+### 2.2 Reader Literals improved
+
+* [CLJ-1034](http://dev.clojure.org/jira/browse/CLJ-1034)
+  "Conflicting data-reader mapping" should no longer be thrown where there really isn't a conflict. Until this patch, having data_readers.clj on the classpath twice would cause the above exception.
+
+* [CLJ-927](http://dev.clojure.org/jira/browse/CLJ-927)
+  Added `*default-data-reader-fn*` to clojure.core. When no data reader is found for a tag and `*default-data-reader-fn*`is non-nil, it will be called with two arguments, the tag and the value.  If `*default-data-reader-fn*` is nil (the default), an exception will be thrown for the unknown tag.
+
+### 2.3 clojure.core/set-agent-send-executor!, set-agent-send-off-executor!, and send-via
+
+Added two new functions:
+
+* clojure.core/set-agent-send-executor!
+
+  Allows the user to set the `java.util.concurrent.Executor` used when calling `clojure.core/send`. Defaults to a fixed thread pool of size: (numCores + 2)
+
+* clojure.core/set-agent-send-off-executor!
+
+ 	Allows the user to set the `java.util.concurrent.Executor` used when calling `clojure.core/send-off`. Defaults to a cached thread pool.
+
+* clojure.core/send-via
+
+	Like `send`, and `send-off`, except the first argument to this function is an executor to use when sending.
+
+
+
+
+### 2.4 New threading macros
+
+* clojure.core/cond-> [expr & clauses]
+
+	Takes an expression and a set of test/form pairs. Threads the expression (via ->) through each form for which the corresponding test expression (not threaded) is true.
+
+Example:
+
+	user=> (cond-> 1
+				   true inc
+	               false (* 42)
+	               (= 2 2) (* 3))
+	6
+
+* clojure.core/cond->> [expr & clauses]
+
+	Takes an expression and a set of test/form pairs. Threads expr (via ->>)
+  through each form for which the corresponding test expression (not threaded) is true.
+
+Example:
+
+	user=> (def d [0 1 2 3])
+	#'user/d
+	user=> (cond->> d
+				    true (map inc)
+					(seq? d) (map dec)
+					(= (count d) 4) (reduce +)) ;; no threading in the test expr
+					                            ;; so d must be passed in explicitly
+	10
+
+
+* clojure.core/as-> [expr name & forms]
+
+Binds name to expr, evaluates the first form in the lexical context of that binding, then binds name to that result, repeating for each successive form
+
+Note: this form does not actually perform any threading. Instead it allows the user to assign a name and lexical context to a value created by a parent threading form.
+
+Example:
+
+	user=> (-> 84
+	    	   (/ 4)
+	    	   (as-> twenty-one          ;; uses the value from ->
+	           		  (* 2 twenty-one)))  ;; no threading here
+	42
+
+* clojure.core/some-> [expr & forms]
+
+
+When expr is not nil, threads it into the first form (via ->),
+ and when that result is not nil, through the next etc.
+
+Example:
+
+	user=> (defn die [x] (assert false))
+	#'user/die
+	user=> (-> 1 inc range next next next die)
+	AssertionError Assert failed: false  user/die (NO_SOURCE_FILE:65)
+	user=> (some-> 1 inc range next next next die)
+	nil
+
+
+
+* clojure.core/some->> [expr & forms]
+
+  When expr is not nil, threads it into the first form (via ->>),
+  and when that result is not nil, through the next etc.
+
+  Same as some-> except the value is threaded as the last argument in each form.
+
+### 2.5 Column metadata captured by reader
+
+* [CLJ-960](http://dev.clojure.org/jira/browse/CLJ-960)
+  Data read by the clojure reader is now tagged with :column in addition to :line.
+
+
+### 2.6 gen-class improvements
+
+* [CLJ-745](http://dev.clojure.org/jira/browse/CLJ-745)
+  It is now possible to expose protected final methods via `:exposes-methods` in `gen-class`. This allows Clojure classes created via gen-class to access protected methods of its parent class.
+
+Example:
+
+	(gen-class :name clojure.test_clojure.genclass.examples.ProtectedFinalTester
+    	       :extends java.lang.ClassLoader
+        	   :main false
+           	   :prefix "pf-"
+           	   :exposes-methods {findSystemClass superFindSystemClass})
+
+* [CLJ-948](http://dev.clojure.org/jira/browse/CLJ-948)
+  It is now possible to annotate constructors via `gen-class`.
+
+Example:
+
+	(gen-class :name foo.Bar
+    	       :extends clojure.lang.Box
+        	   :constructors {^{Deprecated true} [Object] [Object]}
+           	   :init init
+           	   :prefix "foo")
+
+### 2.7 Support added for marker protocols
+
+* [CLJ-966](http://dev.clojure.org/jira/browse/CLJ-966)
+  `defprotocol` no longer requires that at least one method be given in the definition of the protocol. This allows for marker protocols, whose sole reason of existence is to allow `satisfies?` to be true for a given type.
+
+
+Example:
+
+	user=> (defprotocol P (hi [_]))
+    P
+    user=> (defprotocol M) ; marker protocol
+    M
+    user=> (deftype T [a] M P (hi [_] "hi there"))
+    user.T
+    user=> (satisfies? P (T. 1))
+    true
+    user=> (satisfies? M (T. 1))
+    true
+    user=> (hi (T. 1))
+    "hi there"
+    user=> (defprotocol M2 "marker for 2") ; marker protocol again
+    M2
+    user=> (extend-type T M2)
+    nil
+    user=> (satisfies? M2 (T. 1))
+    true
+
+
+### 2.8 clojure.pprint/print-table output compatible with Emacs Org mode
+
+For the convenience of those that use Emacs Org mode,
+`clojure.pprint/print-table` now prints tables in the form used by
+that mode.  Emacs Org mode has features to make it easy to edit such
+tables, and even to do spreadsheet-like calculations on their
+contents.  See the [Org mode documentation on
+tables](http://orgmode.org/manual/Tables.html) for details.
+
+    user=> (clojure.pprint/print-table [:name :initial-impression]
+               [{:name "Rich" :initial-impression "rock star"}
+                {:name "Andy" :initial-impression "engineer"}])
+    | :name | :initial-impression |
+    |-------+---------------------|
+    |  Rich |           rock star |
+    |  Andy |            engineer |
+
+
+### 2.9 clojure.string/replace and replace-first handle special characters more predictably
+
+`clojure.string/replace` and `clojure.string/replace-first` are now
+consistent in the way that they handle the replacement strings: all
+characters in the replacement strings are treated literally, including
+backslash and dollar sign characters.
+
+    user=> (require '[clojure.string :as s])
+
+    user=> (s/replace-first "munge.this" "." "$")
+    ;=> "munge$this"
+
+    user=> (s/replace "/my/home/dir" #"/" (fn [s] "\\"))
+    ;=> "\\my\\home\\dir"
+
+There is one exception, which is described in the doc strings.  If you
+call these functions with a regex to search for and a string as the
+replacement, then dollar sign and backslash characters in the
+replacement string are treated specially.  Occurrences of `$1` in the
+replacement string are replaced with the string that matched the first
+parenthesized subexpression of the regex, occurrences of `$2` are
+replaced with the match of the second parenthesized subexpression,
+etc.
+
+    user=> (s/replace "x12, b4" #"([a-z]+)([0-9]+)" "$1 <- $2")
+    ;=> "x <- 12, b <- 4"
+
+Individual occurrences of `$` or `\` in the replacement string that
+you wish to be treated literally can be escaped by prefixing them with
+a `\`.  If you wish your replacement string to be treated literally
+and its contents are unknown to you at compile time (or you don't wish
+to tarnish your constant string with lots of backslashes), you can use
+the new function `clojure.string/re-quote-replacement` to do the
+necessary escaping of special characters for you.
+
+    user=> (s/replace "x12, b4" #"([a-z]+)([0-9]+)"
+                         (s/re-quote-replacement "$1 <- $2"))
+    ;=> "$1 <- $2, $1 <- $2"
+
+
+### 2.10 Set and map constructor functions allow duplicates
+
+All of the functions that construct sets such as `set` and
+`sorted-set` allow duplicate elements to appear in their arguments,
+and they are documented to treat this case as if by repeated uses of
+`conj`.
+
+Similarly, all map constructor functions such as `hash-map`,
+`array-map`, and `sorted-map` allow duplicate keys, and are documented
+to treat this case as if by repeated uses of `assoc`.
+
+As before, literal sets, e.g. `#{1 2 3}`, do not allow duplicate
+elements, and while elements can be expressions evaluated at run time
+such as `#{(inc x) (dec y)}`, this leads to a check for duplicates at
+run time whenever the set needs to be constructed, throwing an
+exception if any duplicates are found.
+
+Similarly, literal maps do not allow duplicate keys.  New to Clojure
+1.5 is a performance optimization: if all keys are compile time
+constants but one or more values are expressions requiring evaluation
+at run time, duplicate keys are checked for once at compile time only,
+not each time a map is constructed at run time.
+
+* [CLJ-1065](http://dev.clojure.org/jira/browse/CLJ-1065)
+  Allow duplicate set elements and map keys for all set and map constructors
+
+
+### 2.11 More functions preserve metadata
+
+Most functions that take a collection and return a "modified" version
+of that collection preserve the metadata that was on the input
+collection, e.g. `conj`, `assoc`, `dissoc`, etc.  One notable
+exception was `into`, which would return a collection with metadata
+`nil` for several common types of input collections.
+
+Now the functions `into`, `select-keys`, `clojure.set/project`, and
+`clojure.set/rename` return collections with the same metadata as
+their input collections.
+
+### 2.12 New edn reader, improvements to `*read-eval*`
+
+The new `clojure.edn` namespace reads edn (http://edn-format.org) data,
+and should be used for reading data from untrusted sources.
+
+Clojure's core read* functions can evaluate code, and should not be
+used to read data from untrusted sources. As of 1.5, `*read-eval*`
+supports a documented set of thread-local bindings, see the doc string
+for details.
+
+`*read-eval*`'s default can be set to false by setting a system property:
+
+    -Dclojure.read.eval=false
+
+## 3 Performance and Memory Enhancements
+
+* [CLJ-988](http://dev.clojure.org/jira/browse/CLJ-988)
+  Multimethod tables are now protected by a read/write lock instead of a synchronized method. This should result in a performance boost for multithreaded code using multimethods.
+* [CLJ-1061](http://dev.clojure.org/jira/browse/CLJ-1061)
+  `when-first` now evaluates its expression only once.
+* [CLJ-1084](http://dev.clojure.org/jira/browse/CLJ-1084)
+  `PersistentVector$ChunkedSeq` now implements `Counted` interface, to avoid some cases where vector elements were being counted by iterating over their elements.
+* [CLJ-867](http://dev.clojure.org/jira/browse/CLJ-867)
+  Records with same fields and field values, but different types, now usually hash to different values.
+* [CLJ-1000](http://dev.clojure.org/jira/browse/CLJ-1000)
+  Cache hasheq() for seqs, sets, vectors, maps and queues
+* (no ticket) array-map perf tweaks
+* [CLJ-1111](http://dev.clojure.org/jira/browse/CLJ-1111)
+  Allows loop to evaluate to primitive values
+* (no ticket) Move loop locals into same clearing context as loop body
+
+
+## 4 Improved error messages
+
+* [CLJ-103](http://dev.clojure.org/jira/browse/CLJ-103)
+  Improved if-let error message when form has a improperly defined body.
+* [CLJ-897](http://dev.clojure.org/jira/browse/CLJ-897)
+  Don't use destructuring in defrecord/deftype arglists to get a slightly better error message when forgetting to specify the fields vector
+* [CLJ-788](http://dev.clojure.org/jira/browse/CLJ-788)
+  Add source and line members and getters to CompilerException
+* [CLJ-157](http://dev.clojure.org/jira/browse/CLJ-157)
+  Better error messages for syntax errors w/ defn and fn
+* [CLJ-940](http://dev.clojure.org/jira/browse/CLJ-940)
+  Passing a non-sequence to refer :only results in uninformative exception
+* [CLJ-1052](http://dev.clojure.org/jira/browse/CLJ-1052)
+  `assoc` now throws an exception if the last key argument is missing a value.
+
+
+## 5 Improved documentation strings
+
+* [CLJ-893](http://dev.clojure.org/jira/browse/CLJ-893)
+  Document that vec will alias Java arrays
+* [CLJ-892](http://dev.clojure.org/jira/browse/CLJ-892)
+  Clarify doc strings of sort and sort-by: they will modify Java array arguments
+* [CLJ-1019](http://dev.clojure.org/jira/browse/CLJ-1019)
+  ns-resolve doc has a typo
+* [CLJ-1038](http://dev.clojure.org/jira/browse/CLJ-1038)
+  Docstring for deliver doesn't match behavior
+* [CLJ-1055](http://dev.clojure.org/jira/browse/CLJ-1055)
+  "be come" should be "become"
+* [CLJ-917](http://dev.clojure.org/jira/browse/CLJ-917)
+  clojure.core/definterface is not included in the API docs
+* (no ticket) clojure.core/read, read-string, and *read-eval* all have more extensive documentation.
+
+
+## 6 Bug Fixes
+
+* [CLJ-962](http://dev.clojure.org/jira/browse/CLJ-962)
+  Vectors returned by subvec allow access at negative indices
+* [CLJ-952](http://dev.clojure.org/jira/browse/CLJ-952)
+  bigdec does not properly convert a clojure.lang.BigInt
+* [CLJ-975](http://dev.clojure.org/jira/browse/CLJ-975)
+  inconsistent destructuring behaviour when using nested maps
+* [CLJ-954](http://dev.clojure.org/jira/browse/CLJ-954)
+  TAP support in clojure.test.tap Needs Updating
+* [CLJ-881](http://dev.clojure.org/jira/browse/CLJ-881)
+  exception when cl-format is given some ~f directive/value combinations
+* [CLJ-763](http://dev.clojure.org/jira/browse/CLJ-763)
+  Do not check for duplicates in destructuring map creation
+* [CLJ-667](http://dev.clojure.org/jira/browse/CLJ-667)
+  Allow loops fully nested in catch/finally
+* [CLJ-768](http://dev.clojure.org/jira/browse/CLJ-768)
+  cl-format bug in ~f formatting
+* [CLJ-844](http://dev.clojure.org/jira/browse/CLJ-844)
+  NPE calling keyword on map from bean
+* [CLJ-934](http://dev.clojure.org/jira/browse/CLJ-934)
+  disj! Throws exception when attempting to remove multiple items in one call
+* [CLJ-943](http://dev.clojure.org/jira/browse/CLJ-943)
+  When load-lib fails, a namespace is still created
+* [CLJ-981](http://dev.clojure.org/jira/browse/CLJ-981)
+  clojure.set/rename-keys deletes keys when there's a collision
+* [CLJ-961](http://dev.clojure.org/jira/browse/CLJ-961)
+  with-redefs loses a Var's root binding if the Var is thread-bound
+* [CLJ-1032](http://dev.clojure.org/jira/browse/CLJ-1032)
+  seque leaks threads from the send-off pool
+* [CLJ-1041](http://dev.clojure.org/jira/browse/CLJ-1041)
+  reduce-kv on sorted maps should stop on seeing a Reduced value
+* [CLJ-1011](http://dev.clojure.org/jira/browse/CLJ-1011)
+  clojure.data/diff should cope with null and false values in maps
+* [CLJ-977](http://dev.clojure.org/jira/browse/CLJ-977)
+  (int \a) returns a value, (long \a) throws an exception
+* [CLJ-964](http://dev.clojure.org/jira/browse/CLJ-964)
+  test-clojure/rt.clj has undeclared dependency on clojure.set
+* [CLJ-923](http://dev.clojure.org/jira/browse/CLJ-923)
+  Reading ratios prefixed by + is not working
+* [CLJ-1012](http://dev.clojure.org/jira/browse/CLJ-1012)
+  partial function should also accept 1 arg (just f)
+* [CLJ-932](http://dev.clojure.org/jira/browse/CLJ-932)
+  contains? Should throw exception on non-keyed collections
+* [CLJ-730](http://dev.clojure.org/jira/browse/CLJ-730) Create test suite for functional fns (e.g. juxt, comp, partial, etc.)
+* [CLJ-757](http://dev.clojure.org/jira/browse/CLJ-757)
+  Empty transient maps/sets return wrong value for .contains
+* [CLJ-828](http://dev.clojure.org/jira/browse/CLJ-828)
+  clojure.core/bases returns a cons when passed a class and a Java array when passed an interface
+* [CLJ-1062](http://dev.clojure.org/jira/browse/CLJ-1062)
+  CLJ-940 breaks compilation of namespaces that don't have any public functions
+* [CLJ-1070](http://dev.clojure.org/jira/browse/CLJ-1070)
+  PersistentQueue's hash function does not match its equality
+* [CLJ-987](http://dev.clojure.org/jira/browse/CLJ-987)
+  pprint doesn't flush the underlying stream
+* [CLJ-963](http://dev.clojure.org/jira/browse/CLJ-963)
+  Support pretty printing namespace declarations under code-dispatch
+* [CLJ-902](http://dev.clojure.org/jira/browse/CLJ-902)
+  doc macro broken for namespaces
+* [CLJ-909](http://dev.clojure.org/jira/browse/CLJ-909) Make LineNumberingPushbackReader's buffer size configurable
+* [CLJ-910](http://dev.clojure.org/jira/browse/CLJ-910) Allow for type-hinting the method receiver in memfn
+* [CLJ-1048](http://dev.clojure.org/jira/browse/CLJ-1048) add test.generative to Clojure's tests
+* [CLJ-1071](http://dev.clojure.org/jira/browse/CLJ-1071) ExceptionInfo does no abstraction
+* [CLJ-1085](http://dev.clojure.org/jira/browse/CLJ-1085) clojure.main/repl unconditionally refers REPL utilities into `*ns*`
+* (no ticket) Rich Hickey fix: syntax-quote was walking records, returning maps
+* [CLJ-1116](http://dev.clojure.org/jira/browse/CLJ-1116) More REPL-friendly 'ns macro
+* (no ticket) Rich Hickey fix: deref any j.u.c.Future
+* [CLJ-1092](http://dev.clojure.org/jira/browse/CLJ-1092) New function re-quote-replacement has incorrect :added metadata
+* [CLJ-1098](http://dev.clojure.org/jira/browse/CLJ-1098) Implement IKVReduce and CollFold for nil
+* (no ticket) Rich Hickey fix: impose once semantics on fabricated closures for e.g. loops
+* [CLJ-1140](http://dev.clojure.org/jira/browse/CLJ-1140) Restore {:as x} destructuring for empty lists
+* [CLJ-1150](http://dev.clojure.org/jira/browse/CLJ-1150) Make some PersistentVector's and APersistentVector.SubVector's internals public
+* (no ticket) Rich Hickey fix: use non-loading classForName
+* [CLJ-1106](http://dev.clojure.org/jira/browse/CLJ-1106) Fixing set equality
+
+## 7 Binary Compatibility Notes
+
+* `public static inner class LispReader.ReaderException(int line, Throwable cause)`
+  Constructor changed to `ReaderException(int line, int column, Throwable cause)`
+* `public Object clojure.lang.Agent.dispatch(IFn fn, ISeq args, boolean solo)`
+  Replaced with `dispatch(IFn fn, ISeq args, Executor exec)`
+
 # Changes to Clojure in Version 1.4
 
 ## CONTENTS
@@ -41,7 +858,7 @@ For example, in Clojure 1.3, one can declare a record with a field starting with
 
     (defrecord Bar [-a]) ;=> user.Bar
     (.-a (Bar. 10)) ;=> 10
-    
+
 In 1.4, the above code results in `IllegalArgumentException No matching field found: a for class user.Bar`
 
 However, the field may still be accessed as a keyword:
@@ -75,26 +892,26 @@ by invoking the Var `#'my.project.foo/bar` on the vector `[1 2 3]`. The
 data reader function is invoked on the form AFTER it has been read
 as a normal Clojure data structure by the reader.
 
-Reader tags without namespace qualifiers are reserved for Clojure. Default 
-reader tags are defined in `clojure.core/default-data-readers` but may be 
+Reader tags without namespace qualifiers are reserved for Clojure. Default
+reader tags are defined in `clojure.core/default-data-readers` but may be
 overridden in `data_readers.clj` or by rebinding `*data-readers*`.
 
 #### 2.1.1 Instant Literals
 
-Clojure supports literals for instants in the form 
+Clojure supports literals for instants in the form
 `#inst "yyyy-mm-ddThh:mm:ss.fff+hh:mm"`. These literals are parsed as `java.util.Date`s
 by default. They can be parsed as `java.util.Calendar`s or `java.util.Timestamp`s
 by binding `*data-readers*` to use `clojure.instant/read-instant-calendar` or
 `clojure.instant/read-instant-timestamp`.
 
     (def instant "#inst \"@2010-11-12T13:14:15.666\"")
-    
+
     ; Instants are read as java.util.Date by default
     (= java.util.Date (class (read-string instant)))
     ;=> true
-    
+
     ; Instants can be read as java.util.Calendar or java.util.Timestamp
-    
+
     (binding [*data-readers* {'inst read-instant-calendar}]
       (= java.util.Calendar (class (read-string instant))))
     ;=> true
@@ -110,10 +927,10 @@ literals are parsed as `java.util.UUID`s.
 
 ### 2.2 clojure.core/mapv
 
-`mapv` takes a function `f` and one or more collections and returns a 
-vector consisting of the result of applying `f` to the set of first items of 
-each collection, followed by applying `f` to the set of second items in each 
-collection, until any one of the collections is exhausted. Any remaining 
+`mapv` takes a function `f` and one or more collections and returns a
+vector consisting of the result of applying `f` to the set of first items of
+each collection, followed by applying `f` to the set of second items in each
+collection, until any one of the collections is exhausted. Any remaining
 items in other collections are ignored. `f` should accept a number of arguments
 equal to the number of collections.
 
@@ -152,11 +969,11 @@ if the exception is an instance of `ExceptionInfo`.
 ### 2.5 clojure.core/reduce-kv
 
 `reduce-kv` reduces an associative collection. It takes a function `f`,
-an initial value `init` and an associative collection `coll`. `f` should 
-be a function of 3 arguments. Returns the result of applying `f` to `init`, 
-the first key and the first value in `coll`, then applying `f` to that result 
+an initial value `init` and an associative collection `coll`. `f` should
+be a function of 3 arguments. Returns the result of applying `f` to `init`,
+the first key and the first value in `coll`, then applying `f` to that result
 and the 2nd key and value, etc. If `coll` contains no entries, returns `init`
-and f is not called. Note that `reduce-kv` is supported on vectors, 
+and f is not called. Note that `reduce-kv` is supported on vectors,
 where the keys will be the ordinals.
 
     (reduce-kv str "Hello " {:w \o :r \l :d \!})
@@ -187,7 +1004,7 @@ write:
 
     (defrecord Foo [x]) ;=> user.Foo
     (.-x (Foo. 10)) ;=> 10
-    
+
 This addition makes it easier to write code that will run as expected
 in both Clojure and ClojureScript.
 
@@ -229,7 +1046,7 @@ to the Clojure compiler.
 
 Supported options:
 
-* `:elide-meta`: Have certain metadata elided during compilation. This 
+* `:elide-meta`: Have certain metadata elided during compilation. This
 should be set to a collection of keywords.
 * `:disable-locals-clearing`: Set to true to disable clearing. Useful for
 using a debugger.
@@ -259,7 +1076,7 @@ running Java 7.
 
 ### 2.18 loadLibrary Loads Library Using System ClassLoader
 
-A static method, `loadLibrary`, was added to `clojure.lang.RT` to load a 
+A static method, `loadLibrary`, was added to `clojure.lang.RT` to load a
 library using the system ClassLoader instead of Clojure's class loader.
 
 ### 2.19 Java int is Boxed As java.lang.Integer
@@ -307,4 +1124,472 @@ for more information.
   Syntactically broken clojure.test/are tests succeed
 * [CLJ-933](http://dev.clojure.org/jira/browse/CLJ-933)
   Compiler warning on clojure.test-clojure.require-scratch
-  
+
+# Changes to Clojure in Version 1.3
+
+## CONTENTS
+<pre>
+ 1 Deprecated and Removed Features
+    1.1 Earmuffed Vars are No Longer Automatically Considered Dynamic
+    1.2 ISeq No Longer Inherits from Sequential
+    1.3 Removed Bit Operation Support for Boxed Numbers
+    1.4 Ancillary Namespaces No Longer Auto-Load on Startup
+    1.5 Replicate Deprecated
+ 2 New/Improved Features
+    2.1 Enhanced Primitive Support
+    2.2 defrecord and deftype Improvements
+    2.3 Better Exception Reporting
+    2.4 clojure.reflect/reflect
+    2.5 clojure.data/diff
+    2.6 clojure.core/every-pred and clojure.core/some-fn Combinators
+    2.7 clojure.core/realized?
+    2.8 clojure.core/with-redefs-fn & with-redefs
+    2.9 clojure.core/find-keyword
+    2.10 clojure.repl/pst
+    2.11 clojure.pprint/print-table
+    2.12 pprint respects *print-length*
+    2.13 compilation and deployment via Maven
+    2.14 internal keyword map uses weak refs
+    2.15 ^:const defs
+    2.16 Message Bearing Assert
+    2.17 Error Checking for defmulti Options
+    2.18 Removed Checked Exceptions
+    2.19 vector-of Takes Multiple Arguments
+    2.20 deref with timeout
+    2.21 Walk Support for sorted-by Collections
+    2.22 string.join Enhanced to Work with Sets
+    2.23 clojure.test-helper
+    2.24 Newline outputs platform-specific newline sequence
+    2.25 init-proxy and update-proxy return proxy
+    2.26 doc & find-doc moved to REPL
+    2.27 clojure.java.shell/sh accepts as input anything that clojure.java.io/copy does
+    2.28 InterruptedHandler Promoted to clojure.repl
+    2.29 Add support for running -main namespaces from clojure.main
+    2.30 Set thread names on agent thread pools
+    2.31 Add docstring support to def
+    2.32 Comp function returns identity when called with zero arity
+    2.33 Type hints can be applied to arg vectors
+    2.34 Binding Conveyance
+ 3 Performance Enhancements
+ 4 Bug Fixes
+ 5 Modular Contrib
+</pre>
+
+## 1 Deprecated and Removed Features
+
+### 1.1 Earmuffed Vars Are No Longer Automatically Considered Dynamic.
+
+    (def *fred*)
+    => Warning: *fred* not declared dynamic and thus is not dynamically rebindable, but its name suggests otherwise. Please either indicate ^:dynamic ** or change the name.
+
+### 1.2 ISeq No Longer Inherits From Sequential
+
+This allows ISeq implementers to be in the map or set equality partition.
+
+### 1.3 Removed Bit Operation Support for Boxed Numbers
+
+Bit Operations map directly to primitive operations
+
+### 1.4 Ancillary Namespaces No Longer Auto-Load on Startup
+
+The following namespaces are no longer loaded on startup: clojure.set, clojure.xml, clojure.zip
+
+### 1.5 Replicate Deprecated
+
+Use repeat instead.
+
+## 2 New/Improved Features
+
+### 2.1 Enhanced Primitive Support
+
+Full details here:
+
+ - [Enhanced Primitive Support][EPS]
+ - [Documentation for 1.3 Numerics][NUM]
+
+[EPS]: http://dev.clojure.org/display/doc/Enhanced+Primitive+Support
+[NUM]: http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics
+
+### 2.2 defrecord and deftype Improvements
+
+Details here: [Defrecord Improvements](http://dev.clojure.org/display/design/defrecord+improvements)
+
+### 2.3 Better Exception Reporting
+
+Details here: [Error Handling](http://dev.clojure.org/display/design/Error+Handling)
+
+Additionally:
+
+Better error messages:
+
+ * When calling macros with arity
+ * For Invalid Map Literals
+ * For alias function if using unknown namespace
+ * In the REPL
+ * Add "starting at <line>" to EOF while reading exceptions
+ * Better compilation error reporting
+
+### 2.4 clojure.reflect/reflect
+
+Full details here: [Reflection API](http://dev.clojure.org/display/design/Reflection+API)
+
+### 2.5 clojure.data/diff
+
+Recursively compares a and b, returning a tuple of [things-only-in-a things-only-in-b things-in-both]
+
+    (diff {:a 1 :b 2} {:a 1 :b 22 :c 3})
+    => ({:b 2} {:c 3, :b 22} {:a 1})
+
+### 2.6 clojure.core/every-pred and clojure.core/some-fn Combinators
+
+every-pred takes a set of predicates and returns a function f that returns true if all of its composing predicates return a logical true value against all of its arguments, else it returns false.
+
+    ((every-pred even?) 2 4 6)
+    => true
+
+    ((every-pred even?) 2 4 5)
+    =>false
+
+some-fn takes a set of predicates and returns a function f that returns the first logical true value  returned by one of its composing predicates against any of its arguments, else it returns logical false.
+
+    ((some-fn even?) 2 4 5)
+    => true
+    ((some-fn odd?) 2 4 6)
+    => false
+
+### 2.7 clojure.core/realized?
+
+Returns true if a value has been produced for a promise, delay, future or lazy sequence.
+
+    (let [x (range 5)]
+      (println (realized? x))
+      (first x)
+      (println (realized? x)))
+    => false
+    => true
+
+### 2.8 clojure.core/with-redefs-fn & clojure.core/with-redefs
+
+with-redefs-fn temporarily redefines Vars during a call to func. with-redefs temporarily redefines Vars while executing the body.
+
+    (with-redefs [nil? :temp] (println nil?))
+    => :temp
+
+### 2.9 clojure.core/find-keyword
+
+Returns a Keyword with the given namespace and name if one already exists.
+
+    (find-keyword "def")
+    => :def
+    (find-keyword "fred")
+    => nil
+
+### 2.10 clojure.repl/pst
+
+Prints a stack trace of the exception
+
+
+    (pst (IllegalArgumentException.))
+
+    IllegalArgumentException
+        user/eval27 (NO_SOURCE_FILE:18)
+        clojure.lang.Compiler.eval (Compiler.java:6355)
+        clojure.lang.Compiler.eval (Compiler.java:6322)
+        clojure.core/eval (core.clj:2699)
+        clojure.main/repl/read-eval-print--5906 (main.clj:244)
+        clojure.main/repl/fn--5911 (main.clj:265)
+        clojure.main/repl (main.clj:265)
+        clojure.main/repl-opt (main.clj:331)
+        clojure.main/main (main.clj:427)
+        clojure.lang.Var.invoke (Var.java:397)
+        clojure.lang.Var.applyTo (Var.java:518)
+        clojure.main.main (main.java:37)
+
+### 2.11 clojure.pprint/print-table
+
+Prints a collection of maps in a textual table.
+
+    (print-table [:fred :barney]
+                 [{:fred "ethel"}
+                  {:fred "wilma" :barney "betty"}])
+
+    ===============
+    :fred | :barney
+    ===============
+    ethel |
+    wilma | betty
+    ===============
+
+### 2.12 pprint respects \*print-length\*
+
+Assigning \*print-length\* now affects output of pprint
+
+### 2.13 compilation and deployment via Maven
+
+See the following pages for more information:
+
+ - [Maven Settings and Repositories][MSR]
+ - [Why Maven?][WM]
+ - [Common Contrib Build][CCB]
+ - [How to Make Releases][HMR]
+
+ [MSR]: http://dev.clojure.org/display/doc/Maven+Settings+and+Repositories
+ [WM]: http://dev.clojure.org/pages/viewpage.action?pageId=950842
+ [CCB]: http://dev.clojure.org/display/design/Common+Contrib+Build
+ [HMR]:http://dev.clojure.org/display/design/How+to+Make+Releases
+
+### 2.14 internal keyword map uses weak refs
+
+### 2.15 ^:const defs
+
+^:const lets you name primitive values with speedier reference.
+
+    (def constants
+     {:pi 3.14
+      :e 2.71})
+
+    (def ^:const pi (:pi constants))
+    (def ^:const e (:e constants))
+
+The overhead of looking up :e and :pi in the map happens at compile time, as (:pi constants) and (:e constants) are evaluated when their parent def forms are evaluated.
+
+### 2.16 Message Bearing Assert
+
+Assert can take a second argument which will be printed when the assert fails
+
+    (assert (= 1 2) "1 is not equal to 2")
+    => AssertionError Assert failed: 1 is not equal to 2
+
+### 2.17 Error Checking for defmulti Options
+
+defmulti will check to verify that its options are valid. For example, the following code will throw an exception:
+
+    (defmulti fred :ethel :lucy :ricky)
+    => IllegalArgumentException
+
+### 2.18 Removed Checked Exceptions
+
+Clojure does not throw checked exceptions
+
+### 2.19 vector-of Takes Multiple Args
+
+vector-of takes multiple args used to populate the array
+
+    (vector-of :int 1 2 3)
+    => [1 2 3]
+
+### 2.20 deref with timeout
+
+deref now takes a timeout option - when given with a blocking reference, will return the timeout-val if the timeout (in milliseconds) is reached before value is available.
+
+    (deref (promise) 10 :ethel)
+    => :ethel
+
+### 2.21 Walk Support for sorted-by Collections
+
+Walk modified to work on sorted-by collections
+
+    let [x (sorted-set-by > 1 2 3)] (walk inc reverse x))
+    => (2 3 4)
+
+### 2.22 string.join Enhanced to Work with Sets
+
+Just like join works on other collections
+
+    (join " and " #{:fred :ethel :lucy})
+    => ":lucy and :fred and :ethel"
+
+### 2.23 clojure.test-helper
+
+All test helpers moved into clojure.test-helper
+
+### 2.24 Newline outputs platform-specific newline sequence
+
+Newline sequence is output as \r\n on Windows now.
+
+### 2.25 init-proxy and update-proxy return proxy
+
+Now you can chain calls on the proxy
+
+### 2.26 doc & find-doc moved to REPL
+
+Adds special form docs to the REPL
+
+### 2.27 clojure.java.shell/sh accepts as input anything that clojure.java.io/copy does
+
+This adds InputStream, Reader, File, byte[] to the list of inputs for clojure.java.shell/sh
+
+### 2.28 Interrupt Handler Promoted to clojure.repl
+
+Promoting this library eliminates the need for a dependency on old contrib.
+
+### 2.29 Add support for running -main namespaces from clojure.main
+
+This patch allows clojure.main to accept an argument pointing to a namespace to look for a -main function in. This allows users to write -main functions that will work the same whether the code is AOT-compiled for use in an executable jar or just run from source.
+
+### 2.30 Set thread names on agent thread pools
+
+It's a best practice to name the threads in an executor thread pool with a custom ThreadFactory so that the purpose of these threads is clear in thread dumps and other runtime operational tools.
+
+Patch causes thread names like:
+
+    clojure-agent-send-pool-%d     (should be fixed # of threads)
+    clojure-agent-send-off-pool-%d (will be added and removed over time)
+
+### 2.31 Add docstring support to def
+
+A def can now have a docstring between name and value.
+
+    (def foo "a foo" :foo)
+
+### 2.32 Comp function returns identity when called with zero arity
+
+    (= (comp) identity)
+    => true
+
+### 2.33 Type hints can be applied to arg vectors
+
+You can hint different arities separately:
+
+    (defn hinted
+      (^String [])
+      (^Integer [a])
+      (^java.util.List [a & args]))
+
+This is preferred over hinting the function name. Hinting the function name is still allowed for backward compatibility, but will likely be deprecated in a future release.
+
+### 2.34 Binding Conveyance
+
+Clojure APIs that pass work off to other threads (e.g. send, send-off, pmap, future) now convey the dynamic bindings of the calling thread:
+
+    (def ^:dynamic *num* 1)
+    (binding [*num* 2] (future (println *num*)))
+    ;; prints "2", not "1"
+
+## 3 Performance Enhancements
+
+  * Code path for using vars is now much faster for the common case
+  * Improved startup time
+  * Fix performance on some numeric overloads
+    See [CLJ-380](http://dev.clojure.org/jira/browse/CLJ-5) for more information
+  * Promises are lock free
+  * Functions only get metadata support code when metadata explicitly supplied
+  * definterface/gen-interface accepts array type hints
+  * inline nil?
+  * inline bit-functions & math ops
+  * inline n-ary min & max
+  * PersistentQueue count is now O(1)
+  * Intrinsics: unchecked math operators now emit bytecodes directly where possible
+
+## 4 Bug Fixes
+
+[Complete list of Tickets for 1.3 Release][ISSUES].
+
+[ISSUES]: http://dev.clojure.org/jira/secure/IssueNavigator.jspa?mode=hide&requestId=10052
+
+ * [CLJ-8](http://dev.clojure.org/jira/browse/CLJ-8)
+   detect and report cyclic load dependencies
+    * Patch restore detection of cyclic load dependencies
+
+ * [CLJ-31](http://dev.clojure.org/jira/browse/CLJ-31)
+   compiler now correctly rejects attempts to recur across try
+    (fn [x] (try (recur 1)))
+    => CompilerException
+
+ * [CLJ-286](http://dev.clojure.org/jira/browse/CLJ-286)
+   \*out\* being used as java.io.PrintWriter
+    * Patch fixes using Writer instead of PrintWriter
+    * fix clojure.main to not assume that *err* is a PrintWriter
+
+ * [CLJ-292](http://dev.clojure.org/jira/browse/CLJ-292)
+   LazySeq.sval() nests RuntimeExceptions
+    * Patch causes only the original RuntimeException to be thrown
+
+ * [CLJ-390](http://dev.clojure.org/jira/browse/CLJ-390)
+   sends from agent error-handlers should be allowed
+    * Patch allows agent error-handler to send successfully
+
+ * [CLJ-426](http://dev.clojure.org/jira/browse/CLJ-426)
+   case should handle hash collision
+    * There were situations where a hash collision would occur with case and an exception would be thrown. See [discussion](https://groups.google.com/d/topic/clojure/m4ZDWKSfmfo/discussion) for more details
+
+ * [CLJ-430](http://dev.clojure.org/jira/browse/CLJ-430)
+   clojure.java.io URL Coercion throws java.lang.ClassCastException
+    * Patch correct exception to be thrown
+
+ * [CLJ-432](http://dev.clojure.org/jira/browse/CLJ-432)
+   deftype does not work if containing ns contains dashes
+    * Patch munges namespaces with dashes properly
+
+ * [CLJ-433](http://dev.clojure.org/jira/browse/CLJ-433)
+   munge should not munge $ (which isJavaIdentifierPart), should munge ' (which is not)
+
+ * [CLJ-435](http://dev.clojure.org/jira/browse/CLJ-435)
+   stackoverflow exception in printing meta with :type
+    * Patch fixes exception being thrown on certain type metadata
+      (with-meta {:value 2} {:type Object})
+      => No message. [Thrown class java.lang.StackOverflowError]
+
+ * [CLJ-437](http://dev.clojure.org/jira/browse/CLJ-437)
+   Bugs in clojure.set/subset? and superset? for sets with false/nil elements
+    * Patch fixes failing on subset? and superset? for sets with false/nil elements
+
+ * [CLJ-439](http://dev.clojure.org/jira/browse/CLJ-439)
+   Automatic type translation from Integer to Long
+    * Patch fixes increase coercion from Integer to Long
+
+ * [CLJ-444](http://dev.clojure.org/jira/browse/CLJ-444)
+   Infinite recursion in Keyword.intern leads to stack overflow
+    * No more infinite recursion with patch
+
+ * [CLJ-673](http://dev.clojure.org/jira/browse/CLJ-673)
+   use system class loader when base loader is null
+    * facilitates placing Clojure on bootclasspath
+
+ * [CLJ-678](http://dev.clojure.org/jira/browse/CLJ-678)
+   into-array should work with all primitive types
+
+ * [CLJ-680](http://dev.clojure.org/jira/browse/CLJ-680)
+   printing promises should not block
+    * Patch allows printing of promises without blocking
+
+ * [CLJ-682](http://dev.clojure.org/jira/browse/CLJ-682)
+   cl-format: ~w throws an exception when not wrapped in a pretty-writer
+    * Patch fixes the following bug in cl-format with ~w:
+
+ * [CLJ-693](http://dev.clojure.org/jira/browse/CLJ-693)
+   VerifyError with symbol metadata, macros, and defrecord
+
+ * [CLJ-702](http://dev.clojure.org/jira/browse/CLJ-702)
+   case gives NPE when used with nil
+    * Patch allows nil to be used with case
+
+ * [CLJ-734](http://dev.clojure.org/jira/browse/CLJ-734)
+   starting scope of let bindings seems incorrect from jdi perspective
+    * Patch fixes local variables table to have the correct code index for let bindings.
+
+ * [CLJ-739](http://dev.clojure.org/jira/browse/CLJ-739)
+   version.properties file is not closed
+    * Patch properly closes version.properties file
+
+ * [CLJ-751](http://dev.clojure.org/jira/browse/CLJ-751)
+   cl-format: ~( throws an exception with an empty string
+    * Patch fixes the following bug in cl-format when format is nil
+    (cl-format nil "~:(~a~)" "")
+    => NullPointerException
+
+ * [CLJ-780](http://dev.clojure.org/jira/browse/CLJ-780)
+   race condition in reference cache on Java 5
+    * Map.Entry instances can have null values prior to Java 6. This patch provides a workaround.
+
+ * floats were being boxed as Doubles, now they are boxed as Floats
+
+ * several "holding onto head" fixes
+    * Stop top-level defs from hanging onto the head of an expression that uses a lazy seq
+    * Stop multimethods from holding onto heads of their arguments
+
+## 5 Modular Contrib
+
+In 1.3, the monolithic clojure-contrib.jar has been replaced by a modular system of contrib libraries, so that production systems can include only the code they actually need. This also allows individual contribs to have their own release cycles. Many contribs have moved forward by several point versions already. Documentation for updating applications to use the new contrib libraries is at http://dev.clojure.org/display/design/Where+Did+Clojure.Contrib+Go
+
+Important Note: Many of the new modular contribs are compatible with both 1.2 and 1.3. This offers an incremental migration path: First, upgrade your contrib libraries while holding Clojure at 1.2, Then, in a separate step, upgrade to Clojure 1.3.
diff --git a/clojure.iml b/clojure.iml
index 2661388..c90b030 100644
--- a/clojure.iml
+++ b/clojure.iml
@@ -11,12 +11,18 @@
     <content url="file://$MODULE_DIR$">
       <sourceFolder url="file://$MODULE_DIR$/src/resources" isTestSource="false" />
       <sourceFolder url="file://$MODULE_DIR$/src/clj" isTestSource="false" />
-      <sourceFolder url="file://$MODULE_DIR$/test/java" isTestSource="true" />
       <sourceFolder url="file://$MODULE_DIR$/src/jvm" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/test/java" isTestSource="true" />
       <excludeFolder url="file://$MODULE_DIR$/target" />
+      <excludeFolder url="file://$MODULE_DIR$/test-classes" />
     </content>
     <orderEntry type="inheritedJdk" />
     <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" scope="PROVIDED" name="Maven: org.codehaus.jsr166-mirror:jsr166y:1.7.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.clojure:test.generative:0.4.0" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.clojure:tools.namespace:0.1.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.clojure:java.classpath:0.1.1" level="project" />
+    <orderEntry type="library" scope="TEST" name="Maven: org.clojure:data.generators:0.1.2" level="project" />
   </component>
 </module>
 
diff --git a/doc/clojure/pprint/CommonLispFormat.markdown b/doc/clojure/pprint/CommonLispFormat.markdown
index 4bb38ac..c6d7eb4 100644
--- a/doc/clojure/pprint/CommonLispFormat.markdown
+++ b/doc/clojure/pprint/CommonLispFormat.markdown
@@ -11,7 +11,7 @@ This implementation for clojure has the following goals:
  * Support the full feature set of the Common Lisp format function (including the X3J13 extensions) with the only exception being concepts that make no sense or are differently interpreted in Clojure.
  * Make porting code from Common Lisp easier.
  * Provide a more native feeling solution for Clojure programmers than the Java format method and its relatives.
- * Be fast. This includes the ability to precompile formats that are going to be used reptitively.
+ * Be fast. This includes the ability to precompile formats that are going to be used repetitively.
  * Include useful error handling and comprehensive documentation.
 
 ## Why would I use cl-format?
@@ -180,7 +180,7 @@ There are some more examples in the pretty print examples gallery at
 http://github.com/tomfaulhaber/pprint-examples:
 
  * hexdump - a program that uses cl-format to create a standard formatted hexdump of the requested stream.
- * multiply - a function to show a formatted multipication table in a very "first-order" way.
+ * multiply - a function to show a formatted multiplication table in a very "first-order" way.
  * props - the show-props example shown above.
  * show_doc - some utilities for showing documentation from various name spaces.
 
diff --git a/doc/clojure/pprint/PrettyPrinting.markdown b/doc/clojure/pprint/PrettyPrinting.markdown
index db7b26b..60a15a1 100644
--- a/doc/clojure/pprint/PrettyPrinting.markdown
+++ b/doc/clojure/pprint/PrettyPrinting.markdown
@@ -93,7 +93,7 @@ The pretty printer comes with two pre-defined dispatch tables to cover
 the most common situations:
 
 `*`simple-dispatch`*` - supports basic representation of data in various
-Clojure structures: seqs, maps, vectors, etc. in a fairly statndard
+Clojure structures: seqs, maps, vectors, etc. in a fairly standard
 way. When structures need to be broken across lines, following lines
 are indented to line up with the first element. `*`simple-dispatch`*` is
 the default and is good for showing the output of most operations.
@@ -124,7 +124,7 @@ An example formatted with code dispatch:
     user=> 
 
 There are three ways to set the current dispatch: set it to a specific
-table permanantly with set-pprint-dispatch, bind it with
+table permanently with set-pprint-dispatch, bind it with
 with-pprint-dispatch (as shown in the example above), or use the
 :dispatch keyword argument to write.
 
@@ -174,7 +174,7 @@ For example:
 
 As with the regular Clojure print function, this variable controls the 
 number of items that are printed at each layer of structure. When a
-layer has too many items, elipses (...) are displayed. 
+layer has too many items, ellipses (...) are displayed.
 
 For example:
 
diff --git a/pom.xml b/pom.xml
index 0339a8b..78f0ac9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
   <artifactId>clojure</artifactId>
   <name>clojure</name>
   <packaging>jar</packaging>
-  <version>1.4.0</version>
+  <version>1.6.0</version>
 
   <url>http://clojure.org/</url>
   <description>Clojure core environment and runtime library.</description>
@@ -29,7 +29,7 @@
   <parent>
     <groupId>org.sonatype.oss</groupId>
     <artifactId>oss-parent</artifactId>
-    <version>5</version>
+    <version>7</version>
   </parent>
 
   <scm>
@@ -38,6 +38,27 @@
     <url>git at github.com:clojure/clojure.git</url>
   </scm>
 
+  <dependencies>
+    <dependency>
+      <groupId>org.codehaus.jsr166-mirror</groupId>
+      <artifactId>jsr166y</artifactId>
+      <version>1.7.0</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.clojure</groupId>
+      <artifactId>test.generative</artifactId>
+      <version>0.4.0</version>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.clojure</groupId>
+          <artifactId>clojure</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+
   <build>
     <resources>
       <resource>
@@ -55,8 +76,8 @@
 	<artifactId>maven-compiler-plugin</artifactId>
 	<version>2.3.2</version>
 	<configuration>
-	  <source>1.5</source>
-	  <target>1.5</target>
+	  <source>1.6</source>
+	  <target>1.6</target>
 	  <encoding>${project.build.sourceEncoding}</encoding>
 	</configuration>
       </plugin>
@@ -72,6 +93,7 @@
 	    </goals>
 	    <configuration>
 	      <target>
+                <property name="maven.compile.classpath" refid="maven.compile.classpath" />
 		<ant target="compile-clojure" />
 	      </target>
 	    </configuration>
@@ -84,6 +106,7 @@
 	    </goals>
 	    <configuration>
 	      <target>
+                <property name="maven.test.classpath" refid="maven.test.classpath" />
 		<ant target="test" />
 	      </target>
 	    </configuration>
@@ -153,6 +176,11 @@
 	    <goals>
 	      <goal>jar</goal>
 	    </goals>
+            <configuration>
+              <excludes>
+                <exclude>clojure/version.properties</exclude>
+              </excludes>
+            </configuration>
 	  </execution>
 	</executions>
       </plugin>
@@ -208,5 +236,43 @@
 	</plugins>
       </build>
     </profile>
+    <profile>
+      <id>sonatype-oss-release</id>
+      <!-- This profile is enabled automatically by the Sonatype
+           oss-parent POM when invoking the Maven Release Plugin -->
+      <build>
+        <plugins>
+          <plugin>
+            <groupId>org.apache.maven.plugins</groupId>
+            <artifactId>maven-deploy-plugin</artifactId>
+            <version>2.7</version>
+            <configuration>
+              <skip>true</skip>
+            </configuration>
+          </plugin>
+          <plugin>
+            <groupId>org.sonatype.plugins</groupId>
+            <artifactId>nexus-staging-maven-plugin</artifactId>
+            <version>1.4.4</version>
+            <executions>
+              <execution>
+                <id>default-deploy</id>
+                <phase>deploy</phase>
+                <!-- By default, this is the phase deploy goal will bind to -->
+                <goals>
+                  <goal>deploy</goal>
+                </goals>
+              </execution>
+            </executions>
+            <configuration>
+              <!-- The Base URL of Nexus instance where we want to stage -->
+              <nexusUrl>https://oss.sonatype.org/</nexusUrl>
+              <!-- The server "id" element from settings to use authentication from -->
+              <serverId>sonatype-nexus-staging</serverId>
+            </configuration>
+          </plugin>
+        </plugins>
+      </build>
+    </profile>
   </profiles>
 </project>
diff --git a/readme.txt b/readme.txt
index 41c8c2a..4871d0f 100644
--- a/readme.txt
+++ b/readme.txt
@@ -13,8 +13,10 @@ Getting Started: http://dev.clojure.org/display/doc/Getting+Started
 
 To run:  java -cp clojure-${VERSION}.jar clojure.main
 
-To build locally with Ant:  ant
+To build locally with Ant:  
 
+   One-time setup:    ./antsetup.sh
+   To build:          ant
 
 Maven 2 build instructions:
 
@@ -62,3 +64,188 @@ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 THE POSSIBILITY OF SUCH DAMAGE.
+
+
+
+
+-------------------------------------------------------------------------
+This program uses the Guava Murmur3 hash implementation which is distributed
+under the Apache License:
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj
index 336be78..2716817 100644
--- a/src/clj/clojure/core.clj
+++ b/src/clj/clojure/core.clj
@@ -188,7 +188,10 @@
    ([map key val & kvs]
     (let [ret (assoc map key val)]
       (if kvs
-        (recur ret (first kvs) (second kvs) (nnext kvs))
+        (if (next kvs)
+          (recur ret (first kvs) (second kvs) (nnext kvs))
+          (throw (IllegalArgumentException.
+                  "assoc expects even number of arguments after map/vector, found odd number")))
         ret)))))
 
 ;;;;;;;;;;;;;;;;; metadata ;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -270,6 +273,10 @@
                 [name doc-string? attr-map? ([params*] prepost-map? body)+ attr-map?])
    :added "1.0"}
  defn (fn defn [&form &env name & fdecl]
+        ;; Note: Cannot delegate this check to def because of the call to (with-meta name ..)
+        (if (instance? clojure.lang.Symbol name)
+          nil
+          (throw (IllegalArgumentException. "First argument to defn must be a symbol")))
         (let [m (if (string? (first fdecl))
                   {:doc (first fdecl)}
                   {})
@@ -338,7 +345,8 @@
      (. clojure.lang.LazilyPersistentVector (create (cons a (cons b (cons c (cons d args))))))))
 
 (defn vec
-  "Creates a new vector containing the contents of coll."
+  "Creates a new vector containing the contents of coll. Java arrays
+  will be aliased and should not be modified."
   {:added "1.0"
    :static true}
   ([coll]
@@ -348,24 +356,27 @@
 
 (defn hash-map
   "keyval => key val
-  Returns a new hash map with supplied mappings."
+  Returns a new hash map with supplied mappings.  If any keys are
+  equal, they are handled as if by repeated uses of assoc."
   {:added "1.0"
    :static true}
   ([] {})
   ([& keyvals]
-   (. clojure.lang.PersistentHashMap (createWithCheck keyvals))))
+   (. clojure.lang.PersistentHashMap (create keyvals))))
 
 (defn hash-set
-  "Returns a new hash set with supplied keys."
+  "Returns a new hash set with supplied keys.  Any equal keys are
+  handled as if by repeated uses of conj."
   {:added "1.0"
    :static true}
   ([] #{})
   ([& keys]
-   (clojure.lang.PersistentHashSet/createWithCheck keys)))
+   (clojure.lang.PersistentHashSet/create keys)))
 
 (defn sorted-map
   "keyval => key val
-  Returns a new sorted map with supplied mappings."
+  Returns a new sorted map with supplied mappings.  If any keys are
+  equal, they are handled as if by repeated uses of assoc."
   {:added "1.0"
    :static true}
   ([& keyvals]
@@ -373,21 +384,26 @@
 
 (defn sorted-map-by
   "keyval => key val
-  Returns a new sorted map with supplied mappings, using the supplied comparator."
+  Returns a new sorted map with supplied mappings, using the supplied
+  comparator.  If any keys are equal, they are handled as if by
+  repeated uses of assoc."
   {:added "1.0"
    :static true}
   ([comparator & keyvals]
    (clojure.lang.PersistentTreeMap/create comparator keyvals)))
 
 (defn sorted-set
-  "Returns a new sorted set with supplied keys."
+  "Returns a new sorted set with supplied keys.  Any equal keys are
+  handled as if by repeated uses of conj."
   {:added "1.0"
    :static true}
   ([& keys]
    (clojure.lang.PersistentTreeSet/create keys)))
 
 (defn sorted-set-by
-  "Returns a new sorted set with supplied keys, using the supplied comparator."
+  "Returns a new sorted set with supplied keys, using the supplied
+  comparator.  Any equal keys are handled as if by repeated uses of
+  conj."
   {:added "1.1"
    :static true} 
   ([comparator & keys]
@@ -485,6 +501,13 @@
    :static true}
   [x] (if x false true))
 
+(defn some?
+  "Returns true if x is not nil, false otherwise."
+  {:tag Boolean
+   :added "1.6"
+   :static true}
+  [x] (not (nil? x)))
+
 (defn str
   "With no args, returns the empty string. With one arg x, returns
   x.toString().  (str nil) returns the empty string. With more than
@@ -1301,6 +1324,12 @@
    :added "1.0"}
   [x n] (. clojure.lang.Numbers shiftRight x n))
 
+(defn unsigned-bit-shift-right
+  "Bitwise shift right, without sign-extension."
+  {:inline (fn [x n] `(. clojure.lang.Numbers (unsignedShiftRight ~x ~n)))
+   :added "1.6"}
+  [x n] (. clojure.lang.Numbers unsignedShiftRight x n))
+
 (defn integer?
   "Returns true if n is an integer"
   {:added "1.0"
@@ -1444,16 +1473,16 @@
              (conj ret entry)
              ret)
            (next keys)))
-        ret)))
+        (with-meta ret (meta map)))))
 
 (defn keys
-  "Returns a sequence of the map's keys."
+  "Returns a sequence of the map's keys, in the same order as (seq map)."
   {:added "1.0"
    :static true}
   [map] (. clojure.lang.RT (keys map)))
 
 (defn vals
-  "Returns a sequence of the map's values."
+  "Returns a sequence of the map's values, in the same order as (seq map)."
   {:added "1.0"
    :static true}
   [map] (. clojure.lang.RT (vals map)))
@@ -1532,22 +1561,31 @@
   list already. If there are more forms, inserts the first form as the
   second item in second form, etc."
   {:added "1.0"}
-  ([x] x)
-  ([x form] (if (seq? form)
-              (with-meta `(~(first form) ~x ~@(next form)) (meta form))
-              (list form x)))
-  ([x form & more] `(-> (-> ~x ~form) ~@more)))
+  [x & forms]
+  (loop [x x, forms forms]
+    (if forms
+      (let [form (first forms)
+            threaded (if (seq? form)
+                       (with-meta `(~(first form) ~x ~@(next form)) (meta form))
+                       (list form x))]
+        (recur threaded (next forms)))
+      x)))
 
 (defmacro ->>
   "Threads the expr through the forms. Inserts x as the
   last item in the first form, making a list of it if it is not a
   list already. If there are more forms, inserts the first form as the
   last item in second form, etc."
-  {:added "1.1"} 
-  ([x form] (if (seq? form)
+  {:added "1.1"}
+  [x & forms]
+  (loop [x x, forms forms]
+    (if forms
+      (let [form (first forms)
+            threaded (if (seq? form)
               (with-meta `(~(first form) ~@(next form)  ~x) (meta form))
-              (list form x)))
-  ([x form & more] `(->> (->> ~x ~form) ~@more)))
+              (list form x))]
+        (recur threaded (next forms)))
+      x)))
 
 (def map)
 
@@ -1570,9 +1608,24 @@
   The docstring and attribute-map are optional.
 
   Options are key-value pairs and may be one of:
-    :default    the default dispatch value, defaults to :default
-    :hierarchy  the isa? hierarchy to use for dispatching
-                defaults to the global hierarchy"
+
+  :default
+
+  The default dispatch value, defaults to :default
+
+  :hierarchy
+
+  The value used for hierarchical dispatch (e.g. ::square is-a ::shape)
+
+  Hierarchies are type-like relationships that do not depend upon type
+  inheritance. By default Clojure's multimethods dispatch off of a
+  global hierarchy map.  However, a hierarchy relationship can be
+  created with the derive function used to augment the root ancestor
+  created with make-hierarchy.
+
+  Multimethods expect the value of the hierarchy option to be supplied as
+  a reference type e.g. a var (i.e. via the Var-quote dispatch macro #'
+  or the var special form)."
   {:arglists '([name docstring? attr-map? dispatch-fn & options])
    :added "1.0"}
   [mm-name & options]
@@ -1675,7 +1728,8 @@
    `(if-let ~bindings ~then nil))
   ([bindings then else & oldform]
    (assert-args
-     (and (vector? bindings) (nil? oldform)) "a vector for its binding"
+     (vector? bindings) "a vector for its binding"
+     (nil? oldform) "1 or 2 forms after binding vector"
      (= 2 (count bindings)) "exactly 2 forms in binding vector")
    (let [form (bindings 0) tst (bindings 1)]
      `(let [temp# ~tst]
@@ -1699,6 +1753,43 @@
          (let [~form temp#]
            ~@body)))))
 
+(defmacro if-some
+  "bindings => binding-form test
+
+   If test is not nil, evaluates then with binding-form bound to the
+   value of test, if not, yields else"
+  {:added "1.6"}
+  ([bindings then]
+   `(if-some ~bindings ~then nil))
+  ([bindings then else & oldform]
+   (assert-args
+     (vector? bindings) "a vector for its binding"
+     (nil? oldform) "1 or 2 forms after binding vector"
+     (= 2 (count bindings)) "exactly 2 forms in binding vector")
+   (let [form (bindings 0) tst (bindings 1)]
+     `(let [temp# ~tst]
+        (if (nil? temp#)
+          ~else
+          (let [~form temp#]
+            ~then))))))
+
+(defmacro when-some
+  "bindings => binding-form test
+
+   When test is not nil, evaluates body with binding-form bound to the
+   value of test"
+  {:added "1.6"}
+  [bindings & body]
+  (assert-args
+     (vector? bindings) "a vector for its binding"
+     (= 2 (count bindings)) "exactly 2 forms in binding vector")
+   (let [form (bindings 0) tst (bindings 1)]
+    `(let [temp# ~tst]
+       (if (nil? temp#)
+         nil
+         (let [~form temp#]
+           ~@body)))))
+
 (defn push-thread-bindings
   "WARNING: This is a low-level function. Prefer high-level macros like
   binding where ever possible.
@@ -1852,7 +1943,7 @@
 
   :error-mode mode-keyword
 
-  If metadata-map is supplied, it will be come the metadata on the
+  If metadata-map is supplied, it will become the metadata on the
   agent. validate-fn must be nil or a side-effect-free fn of one
   argument, which will be passed the intended new state on any state
   change. If the new state is unacceptable, the validate-fn should
@@ -1875,6 +1966,28 @@
                             (if (:error-handler opts) :continue :fail)))
        a)))
 
+(defn set-agent-send-executor!
+  "Sets the ExecutorService to be used by send"
+  {:added "1.5"}
+  [executor]
+  (set! clojure.lang.Agent/pooledExecutor executor))
+
+(defn set-agent-send-off-executor!
+  "Sets the ExecutorService to be used by send-off"
+  {:added "1.5"}
+  [executor]
+  (set! clojure.lang.Agent/soloExecutor executor))
+
+(defn send-via
+  "Dispatch an action to an agent. Returns the agent immediately.
+  Subsequently, in a thread supplied by executor, the state of the agent
+  will be set to the value of:
+
+  (apply action-fn state-of-agent args)"
+  {:added "1.5"}
+  [executor ^clojure.lang.Agent a f & args]
+  (.dispatch a (binding [*agent* a] (binding-conveyor-fn f)) args executor))
+
 (defn send
   "Dispatch an action to an agent. Returns the agent immediately.
   Subsequently, in a thread from a thread pool, the state of the agent
@@ -1884,7 +1997,7 @@
   {:added "1.0"
    :static true}
   [^clojure.lang.Agent a f & args]
-  (.dispatch a (binding [*agent* a] (binding-conveyor-fn f)) args false))
+  (apply send-via clojure.lang.Agent/pooledExecutor a f args))
 
 (defn send-off
   "Dispatch a potentially blocking action to an agent. Returns the
@@ -1895,7 +2008,7 @@
   {:added "1.0"
    :static true}
   [^clojure.lang.Agent a f & args]
-  (.dispatch a (binding [*agent* a] (binding-conveyor-fn f)) args true))
+  (apply send-via clojure.lang.Agent/soloExecutor a f args))
 
 (defn release-pending-sends
   "Normally, actions sent directly or indirectly during another action
@@ -1909,8 +2022,7 @@
   [] (clojure.lang.Agent/releasePendingSends))
 
 (defn add-watch
-  "Alpha - subject to change.
-  Adds a watch function to an agent/atom/var/ref reference. The watch
+  "Adds a watch function to an agent/atom/var/ref reference. The watch
   fn must be a fn of 4 args: a key, the reference, its old-state, its
   new-state. Whenever the reference's state might have been changed,
   any registered watches will have their functions called. The watch fn
@@ -1928,8 +2040,7 @@
   [^clojure.lang.IRef reference key fn] (.addWatch reference key fn))
 
 (defn remove-watch
-  "Alpha - subject to change.
-  Removes a watch (set by add-watch) from a reference"
+  "Removes a watch (set by add-watch) from a reference"
   {:added "1.0"
    :static true}
   [^clojure.lang.IRef reference key]
@@ -2039,7 +2150,7 @@
   :min-history (default 0)
   :max-history (default 10)
 
-  If metadata-map is supplied, it will be come the metadata on the
+  If metadata-map is supplied, it will become the metadata on the
   ref. validate-fn must be nil or a side-effect-free fn of one
   argument, which will be passed the intended new state on any state
   change. If the new state is unacceptable, the validate-fn should
@@ -2064,6 +2175,14 @@
       (.setMinHistory r (:min-history opts)))
     r)))
 
+(defn ^:private deref-future
+  ([^java.util.concurrent.Future fut]
+     (.get fut))
+  ([^java.util.concurrent.Future fut timeout-ms timeout-val]
+     (try (.get fut timeout-ms java.util.concurrent.TimeUnit/MILLISECONDS)
+          (catch java.util.concurrent.TimeoutException e
+            timeout-val))))
+     
 (defn deref
   "Also reader macro: @ref/@agent/@var/@atom/@delay/@future/@promise. Within a transaction,
   returns the in-transaction-value of ref, else returns the
@@ -2077,8 +2196,13 @@
   value is available. See also - realized?."
   {:added "1.0"
    :static true}
-  ([^clojure.lang.IDeref ref] (.deref ref))
-  ([^clojure.lang.IBlockingDeref ref timeout-ms timeout-val] (.deref ref timeout-ms timeout-val)))
+  ([ref] (if (instance? clojure.lang.IDeref ref)
+           (.deref ^clojure.lang.IDeref ref)
+           (deref-future ref)))
+  ([ref timeout-ms timeout-val]
+     (if (instance? clojure.lang.IBlockingDeref ref)
+       (.deref ^clojure.lang.IBlockingDeref ref timeout-ms timeout-val)
+       (deref-future ref timeout-ms timeout-val))))
 
 (defn atom
   "Creates and returns an Atom with an initial value of x and zero or
@@ -2088,7 +2212,7 @@
 
   :validator validate-fn
 
-  If metadata-map is supplied, it will be come the metadata on the
+  If metadata-map is supplied, it will become the metadata on the
   atom. validate-fn must be nil or a side-effect-free fn of one
   argument, which will be passed the intended new state on any state
   change. If the new state is unacceptable, the validate-fn should
@@ -2339,6 +2463,7 @@
   called, the returned function calls f with args + additional args."
   {:added "1.0"
    :static true}
+  ([f] f)
   ([f arg1]
    (fn [& args] (apply f arg1 args)))
   ([f arg1 arg2]
@@ -2597,10 +2722,11 @@
    :static true}
   [f x] (cons x (lazy-seq (iterate f (f x)))))
 
-(defn range 
+(defn range
   "Returns a lazy seq of nums from start (inclusive) to end
-  (exclusive), by step, where start defaults to 0, step to 1, and end
-  to infinity."
+  (exclusive), by step, where start defaults to 0, step to 1, and end to
+  infinity. When step is equal to 0, returns an infinite sequence of
+  start. When start is equal to end, returns empty list."
   {:added "1.0"
    :static true}
   ([] (range 0 Double/POSITIVE_INFINITY 1))
@@ -2609,7 +2735,9 @@
   ([start end step]
    (lazy-seq
     (let [b (chunk-buffer 32)
-          comp (if (pos? step) < >)]
+          comp (cond (or (zero? step) (= start end)) not=
+                     (pos? step) <
+                     (neg? step) >)]
       (loop [i start]
         (if (and (< (count b) 32)
                  (comp i end))
@@ -2688,8 +2816,9 @@
 
 (defn sort
   "Returns a sorted sequence of the items in coll. If no comparator is
-  supplied, uses compare. comparator must
-  implement java.util.Comparator."
+  supplied, uses compare.  comparator must implement
+  java.util.Comparator.  If coll is a Java array, it will be modified.
+  To avoid this, sort a copy of the array."
   {:added "1.0"
    :static true}
   ([coll]
@@ -2704,8 +2833,9 @@
 (defn sort-by
   "Returns a sorted sequence of the items in coll, where the sort
   order is determined by comparing (keyfn item).  If no comparator is
-  supplied, uses compare. comparator must
-  implement java.util.Comparator."
+  supplied, uses compare.  comparator must implement
+  java.util.Comparator.  If coll is a Java array, it will be modified.
+  To avoid this, sort a copy of the array."
   {:added "1.0"
    :static true}
   ([keyfn coll]
@@ -2923,16 +3053,14 @@
 
 ;;;;;;;;;;;;;;;;;;;;; editable collections ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (defn transient 
-  "Alpha - subject to change.
-  Returns a new, transient version of the collection, in constant time."
+  "Returns a new, transient version of the collection, in constant time."
   {:added "1.1"
    :static true}
   [^clojure.lang.IEditableCollection coll] 
   (.asTransient coll))
 
 (defn persistent! 
-  "Alpha - subject to change.
-  Returns a new, persistent version of the transient collection, in
+  "Returns a new, persistent version of the transient collection, in
   constant time. The transient collection cannot be used after this
   call, any such use will throw an exception."
   {:added "1.1"
@@ -2941,8 +3069,7 @@
   (.persistent coll))
 
 (defn conj!
-  "Alpha - subject to change.
-  Adds x to the transient collection, and return coll. The 'addition'
+  "Adds x to the transient collection, and return coll. The 'addition'
   may happen at different 'places' depending on the concrete type."
   {:added "1.1"
    :static true}
@@ -2950,8 +3077,7 @@
   (.conj coll x))
 
 (defn assoc!
-  "Alpha - subject to change.
-  When applied to a transient map, adds mapping of key(s) to
+  "When applied to a transient map, adds mapping of key(s) to
   val(s). When applied to a transient vector, sets the val at index.
   Note - index must be <= (count vector). Returns coll."
   {:added "1.1"
@@ -2964,8 +3090,7 @@
        ret))))
 
 (defn dissoc!
-  "Alpha - subject to change.
-  Returns a transient map that doesn't contain a mapping for key(s)."
+  "Returns a transient map that doesn't contain a mapping for key(s)."
   {:added "1.1"
    :static true}
   ([^clojure.lang.ITransientMap map key] (.without map key))
@@ -2976,8 +3101,7 @@
        ret))))
 
 (defn pop!
-  "Alpha - subject to change.
-  Removes the last item from a transient vector. If
+  "Removes the last item from a transient vector. If
   the collection is empty, throws an exception. Returns coll"
   {:added "1.1"
    :static true}
@@ -2985,16 +3109,15 @@
   (.pop coll)) 
 
 (defn disj!
-  "Alpha - subject to change.
-  disj[oin]. Returns a transient set of the same (hashed/sorted) type, that
+  "disj[oin]. Returns a transient set of the same (hashed/sorted) type, that
   does not contain key(s)."
   {:added "1.1"
    :static true}
   ([set] set)
   ([^clojure.lang.ITransientSet set key]
    (. set (disjoin key)))
-  ([set key & ks]
-   (let [ret (disj set key)]
+  ([^clojure.lang.ITransientSet set key & ks]
+   (let [ret (. set (disjoin key))]
      (if ks
        (recur ret (first ks) (next ks))
        ret))))
@@ -3220,6 +3343,7 @@
        (instance? clojure.lang.BigInt x) x
        (instance? BigInteger x) (clojure.lang.BigInt/fromBigInteger x)
        (decimal? x) (bigint (.toBigInteger ^BigDecimal x))
+       (float? x)  (bigint (. BigDecimal valueOf (double x)))
        (ratio? x) (bigint (.bigIntegerValue ^clojure.lang.Ratio x))
        (number? x) (clojure.lang.BigInt/valueOf (long x))
        :else (bigint (BigInteger. x))))
@@ -3233,6 +3357,7 @@
        (instance? BigInteger x) x
        (instance? clojure.lang.BigInt x) (.toBigInteger ^clojure.lang.BigInt x)
        (decimal? x) (.toBigInteger ^BigDecimal x)
+       (float? x) (.toBigInteger (. BigDecimal valueOf (double x)))
        (ratio? x) (.bigIntegerValue ^clojure.lang.Ratio x)
        (number? x) (BigInteger/valueOf (long x))
        :else (BigInteger. x)))
@@ -3245,7 +3370,8 @@
   [x] (cond
        (decimal? x) x
        (float? x) (. BigDecimal valueOf (double x))
-       (ratio? x) (/ (BigDecimal. (.numerator x)) (.denominator x))
+       (ratio? x) (/ (BigDecimal. (.numerator ^clojure.lang.Ratio x)) (.denominator ^clojure.lang.Ratio x))
+       (instance? clojure.lang.BigInt x) (.toBigDecimal ^clojure.lang.BigInt x)
        (instance? BigInteger x) (BigDecimal. ^BigInteger x)
        (number? x) (BigDecimal/valueOf (long x))
        :else (BigDecimal. x)))
@@ -3333,7 +3459,12 @@
 (defn read
   "Reads the next object from stream, which must be an instance of
   java.io.PushbackReader or some derivee.  stream defaults to the
-  current value of *in* ."
+  current value of *in*.
+
+  Note that read can execute code (controlled by *read-eval*),
+  and as such should be used only with trusted sources.
+
+  For data structure interop use clojure.edn/read"
   {:added "1.0"
    :static true}
   ([]
@@ -3355,7 +3486,12 @@
     (.readLine ^java.io.BufferedReader *in*)))
 
 (defn read-string
-  "Reads one object from the string s"
+  "Reads one object from the string s.
+
+  Note that read-string can execute code (controlled by *read-eval*),
+  and as such should be used only with trusted sources.
+
+  For data structure interop use clojure.edn/read-string"
   {:added "1.0"
    :static true}
   [s] (clojure.lang.RT/readString s))
@@ -3415,11 +3551,14 @@
   "Expands into code that creates a fn that expects to be passed an
   object and any args and calls the named instance method on the
   object passing the args. Use when you want to treat a Java method as
-  a first-class fn."
+  a first-class fn. name may be type-hinted with the method receiver's
+  type in order to avoid reflective calls."
   {:added "1.0"}
   [name & args]
-  `(fn [target# ~@args]
-     (. target# (~name ~@args))))
+  (let [t (with-meta (gensym "target")
+            (meta name))]
+    `(fn [~t ~@args]
+       (. ~t (~name ~@args)))))
 
 (defmacro time
   "Evaluates expr and prints the time it took.  Returns the value of
@@ -3768,6 +3907,8 @@
           to-do (if (= :all (:refer fs))
                   (keys nspublics)
                   (or (:refer fs) (:only fs) (keys nspublics)))]
+      (when (and to-do (not (instance? clojure.lang.Sequential to-do)))
+        (throw (new Exception ":only/:refer value must be a sequential collection of symbols")))
       (doseq [sym to-do]
         (when-not (exclude sym)
           (let [v (nspublics sym)]
@@ -3825,6 +3966,8 @@
   "Returns a lazy seq of the first item in each coll, then the second etc."
   {:added "1.0"
    :static true}
+  ([] ())
+  ([c1] (lazy-seq c1))
   ([c1 c2]
      (lazy-seq
       (let [s1 (seq c1) s2 (seq c2)]
@@ -3871,7 +4014,7 @@
 
 (defn ns-resolve
   "Returns the var or Class to which a symbol will be resolved in the
-  namespace (unless found in the environement), else nil.  Note that
+  namespace (unless found in the environment), else nil.  Note that
   if the symbol is fully qualified, the var/Class to which it resolves
   need not be present in the namespace."
   {:added "1.0"
@@ -3890,11 +4033,13 @@
   ([env sym] (ns-resolve *ns* env sym)))
 
 (defn array-map
-  "Constructs an array-map."
+  "Constructs an array-map. If any keys are equal, they are handled as
+  if by repeated uses of assoc."
   {:added "1.0"
    :static true}
   ([] (. clojure.lang.PersistentArrayMap EMPTY))
-  ([& keyvals] (clojure.lang.PersistentArrayMap/createWithCheck (to-array keyvals))))
+  ([& keyvals]
+     (clojure.lang.PersistentArrayMap/createAsIfByAssoc (to-array keyvals))))
 
 ;redefine let and loop  with destructuring
 (defn destructure [bindings]
@@ -3924,17 +4069,23 @@
                              ret))))
                      pmap
                      (fn [bvec b v]
-                       (let [gmap (or (:as b) (gensym "map__"))
+                       (let [gmap (gensym "map__")
+                             gmapseq (with-meta gmap {:tag 'clojure.lang.ISeq})
                              defaults (:or b)]
                          (loop [ret (-> bvec (conj gmap) (conj v)
-                                        (conj gmap) (conj `(if (seq? ~gmap) (apply hash-map ~gmap) ~gmap)))
+                                        (conj gmap) (conj `(if (seq? ~gmap) (clojure.lang.PersistentHashMap/create (seq ~gmapseq)) ~gmap))
+                                        ((fn [ret]
+                                           (if (:as b)
+                                             (conj ret (:as b) gmap)
+                                             ret))))
                                 bes (reduce1
                                      (fn [bes entry]
                                        (reduce1 #(assoc %1 %2 ((val entry) %2))
                                                (dissoc bes (key entry))
                                                ((key entry) bes)))
                                      (dissoc b :as :or)
-                                     {:keys #(keyword (str %)), :strs str, :syms #(list `quote %)})]
+                                     {:keys #(if (keyword? %) % (keyword (str %))),
+                                      :strs str, :syms #(list `quote %)})]
                            (if (seq bes)
                              (let [bb (key (first bes))
                                    bk (val (first bes))
@@ -3945,14 +4096,17 @@
                                       (next bes)))
                              ret))))]
                  (cond
-                  (symbol? b) (-> bvec (conj b) (conj v))
+                  (symbol? b) (-> bvec (conj (if (namespace b) (symbol (name b)) b)) (conj v))
+                  (keyword? b) (-> bvec (conj (symbol (name b))) (conj v))
                   (vector? b) (pvec bvec b v)
                   (map? b) (pmap bvec b v)
                   :else (throw (new Exception (str "Unsupported binding form: " b))))))
         process-entry (fn [bvec b] (pb bvec (first b) (second b)))]
     (if (every? symbol? (map first bents))
       bindings
-      (reduce1 process-entry [] bents))))
+      (if-let [kwbs (seq (filter #(keyword? (first %)) bents))]
+        (throw (new Exception (str "Unsupported binding key: " (ffirst kwbs))))
+        (reduce1 process-entry [] bents)))))
 
 (defmacro let
   "binding => binding-form init-expr
@@ -3998,9 +4152,31 @@
   [& sigs]
     (let [name (if (symbol? (first sigs)) (first sigs) nil)
           sigs (if name (next sigs) sigs)
-          sigs (if (vector? (first sigs)) (list sigs) sigs)
+          sigs (if (vector? (first sigs)) 
+                 (list sigs) 
+                 (if (seq? (first sigs))
+                   sigs
+                   ;; Assume single arity syntax
+                   (throw (IllegalArgumentException. 
+                            (if (seq sigs)
+                              (str "Parameter declaration " 
+                                   (first sigs)
+                                   " should be a vector")
+                              (str "Parameter declaration missing"))))))
           psig (fn* [sig]
+                 ;; Ensure correct type before destructuring sig
+                 (when (not (seq? sig))
+                   (throw (IllegalArgumentException.
+                            (str "Invalid signature " sig
+                                 " should be a list"))))
                  (let [[params & body] sig
+                       _ (when (not (vector? params))
+                           (throw (IllegalArgumentException. 
+                                    (if (seq? (first sigs))
+                                      (str "Parameter declaration " params
+                                           " should be a vector")
+                                      (str "Invalid signature " sig
+                                           " should be a list")))))
                        conds (when (and (next body) (map? (first body))) 
                                            (first body))
                        body (if conds (next body) body)
@@ -4054,16 +4230,16 @@
 (defmacro when-first
   "bindings => x xs
 
-  Same as (when (seq xs) (let [x (first xs)] body))"
+  Roughly the same as (when (seq xs) (let [x (first xs)] body)) but xs is evaluated only once"
   {:added "1.0"}
   [bindings & body]
   (assert-args
      (vector? bindings) "a vector for its binding"
      (= 2 (count bindings)) "exactly 2 forms in binding vector")
   (let [[x xs] bindings]
-    `(when (seq ~xs)
-       (let [~x (first ~xs)]
-         ~@body))))
+    `(when-let [xs# (seq ~xs)]
+       (let [~x (first xs#)]
+           ~@body))))
 
 (defmacro lazy-cat
   "Expands to code which yields a lazy sequence of the concatenation
@@ -4223,10 +4399,9 @@
     (with-out-str
      (apply println xs)))
 
-(import clojure.lang.ExceptionInfo)
+(import clojure.lang.ExceptionInfo clojure.lang.IExceptionInfo)
 (defn ex-info
-  "Alpha - subject to change.
-   Create an instance of ExceptionInfo, a RuntimeException subclass
+  "Create an instance of ExceptionInfo, a RuntimeException subclass
    that carries a map of additional data."
   {:added "1.4"}
   ([msg map]
@@ -4235,13 +4410,12 @@
      (ExceptionInfo. msg map cause)))
 
 (defn ex-data
-  "Alpha - subject to change.
-   Returns exception data (a map) if ex is an ExceptionInfo.
+  "Returns exception data (a map) if ex is an IExceptionInfo.
    Otherwise returns nil."
   {:added "1.4"}
   [ex]
-  (when (instance? ExceptionInfo ex)
-    (.getData ^ExceptionInfo ex)))
+  (when (instance? IExceptionInfo ex)
+    (.getData ^IExceptionInfo ex)))
 
 (defmacro assert
   "Evaluates expr and throws an exception if it does not evaluate to
@@ -4564,6 +4738,38 @@
    :static true}
   [x] (. clojure.lang.Util (hasheq x)))
 
+
+(defn mix-collection-hash
+  "Mix final collection hash for ordered or unordered collections.
+   hash-basis is the combined collection hash, count is the number
+   of elements included in the basis. Note this is the hash code
+   consistent with =, different from .hashCode.
+   See http://clojure.org/data_structures#hash for full algorithms."
+  {:added "1.6"
+   :static true}
+  ^long
+  [^long hash-basis ^long count] (clojure.lang.Murmur3/mixCollHash hash-basis count))
+
+(defn hash-ordered-coll
+  "Returns the hash code, consistent with =, for an external ordered
+   collection implementing Iterable.
+   See http://clojure.org/data_structures#hash for full algorithms."
+  {:added "1.6"
+   :static true}
+  ^long
+  [coll] (clojure.lang.Murmur3/hashOrdered coll))
+
+(defn hash-unordered-coll
+  "Returns the hash code, consistent with =, for an external unordered
+   collection implementing Iterable. For maps, the iterator should
+   return map entries whose hash is computed as
+     (hash-ordered-coll [k v]).
+   See http://clojure.org/data_structures#hash for full algorithms."
+  {:added "1.6"
+   :static true}
+  ^long
+  [coll] (clojure.lang.Murmur3/hashUnordered coll))
+
 (defn interpose
   "Returns a lazy seq of the elements of coll separated by sep"
   {:added "1.0"
@@ -4746,18 +4952,25 @@
                              n-or-q
                              (LinkedBlockingQueue. (int n-or-q)))
          NIL (Object.) ;nil sentinel since LBQ doesn't support nils
-         agt (agent (seq s))
+         agt (agent (lazy-seq s)) ; never start with nil; that signifies we've already put eos
+         log-error (fn [q e]
+                     (if (.offer q q)
+                       (throw e)
+                       e))
          fill (fn [s]
-                (try
-                  (loop [[x & xs :as s] s]
-                    (if s
-                      (if (.offer q (if (nil? x) NIL x))
-                        (recur xs)
-                        s)
-                      (.put q q))) ; q itself is eos sentinel
-                  (catch Exception e
-                    (.put q q)
-                    (throw e))))
+                (when s
+                  (if (instance? Exception s) ; we failed to .offer an error earlier
+                    (log-error q s)
+                    (try
+                      (loop [[x & xs :as s] (seq s)]
+                        (if s
+                          (if (.offer q (if (nil? x) NIL x))
+                            (recur xs)
+                            s)
+                          (when-not (.offer q q) ; q itself is eos sentinel
+                            ()))) ; empty seq, not nil, so we know to put eos next time
+                      (catch Exception e
+                        (log-error q e))))))
          drain (fn drain []
                  (lazy-seq
                   (let [x (.take q)]
@@ -4875,10 +5088,9 @@
    :static true}
   [^Class c]
   (when c
-    (let [i (.getInterfaces c)
+    (let [i (seq (.getInterfaces c))
           s (.getSuperclass c)]
-      (not-empty
-       (if s (cons s i) i)))))
+      (if s (cons s i) i))))
 
 (defn supers
   "Returns the immediate and indirect superclasses and interfaces of c, if any"
@@ -5101,7 +5313,7 @@
   supported. The :gen-class directive is ignored when not
   compiling. If :gen-class is not supplied, when compiled only an
   nsname__init.class will be generated. If :refer-clojure is not used, a
-  default (refer 'clojure) is used.  Use of ns is preferred to
+  default (refer 'clojure.core) is used.  Use of ns is preferred to
   individual calls to in-ns/require/use/import:
 
   (ns foo.bar
@@ -5140,7 +5352,10 @@
         ~@(when gen-class-call (list gen-class-call))
         ~@(when (and (not= name 'clojure.core) (not-any? #(= :refer-clojure (first %)) references))
             `((clojure.core/refer '~'clojure.core)))
-        ~@(map process-reference references)))))
+        ~@(map process-reference references))
+        (if (.equals '~name 'clojure.core) 
+          nil
+          (do (dosync (commute @#'*loaded-libs* conj '~name)) nil)))))
 
 (defmacro refer-clojure
   "Same as (refer 'clojure.core <filters>)"
@@ -5175,7 +5390,7 @@
   *loading-verbosely* false)
 
 (defn- throw-if
-  "Throws an exception with a message if pred is true"
+  "Throws a CompilerException with a message if pred is true"
   [pred fmt & args]
   (when pred
     (let [^String message (apply format fmt args)
@@ -5184,7 +5399,11 @@
           boring? #(not= (.getMethodName ^StackTraceElement %) "doInvoke")
           trace (into-array (drop 2 (drop-while boring? raw-trace)))]
       (.setStackTrace exception trace)
-      (throw exception))))
+      (throw (clojure.lang.Compiler$CompilerException.
+              *file*
+              (.deref clojure.lang.Compiler/LINE)
+              (.deref clojure.lang.Compiler/COLUMN)
+              exception)))))
 
 (defn- libspec?
   "Returns true if x is a libspec"
@@ -5248,7 +5467,8 @@
   "Loads a lib with options"
   [prefix lib & options]
   (throw-if (and prefix (pos? (.indexOf (name lib) (int \.))))
-            "lib names inside prefix lists must not contain periods")
+            "Found lib name '%s' containing period with prefix '%s'.  lib names inside prefix lists must not contain periods"
+            (name lib) prefix)
   (let [lib (if prefix (symbol (str prefix \. lib)) lib)
         opts (apply hash-map options)
         {:keys [as reload reload-all require use verbose]} opts
@@ -5258,10 +5478,16 @@
                    (or reload (not require) (not loaded))
                    load-one)
         need-ns (or as use)
-        filter-opts (select-keys opts '(:exclude :only :rename :refer))]
+        filter-opts (select-keys opts '(:exclude :only :rename :refer))
+        undefined-on-entry (not (find-ns lib))]
     (binding [*loading-verbosely* (or *loading-verbosely* verbose)]
       (if load
-        (load lib need-ns require)
+        (try
+          (load lib need-ns require)
+          (catch Exception e
+            (when undefined-on-entry
+              (remove-ns lib))
+            (throw e)))
         (throw-if (and need-ns (not (find-ns lib)))
                   "namespace '%s' not found" lib))
       (when (and need-ns *loading-verbosely*)
@@ -5312,7 +5538,7 @@
     (let [pending (map #(if (= % path) (str "[ " % " ]") %)
                        (cons path *pending-paths*))
           chain (apply str (interpose "->" pending))]
-      (throw (Exception. (str "Cyclic load dependency: " chain))))))
+      (throw-if true "Cyclic load dependency: %s" chain))))
 
 ;; Public
 
@@ -5675,7 +5901,7 @@
 (add-doc-and-meta *file*
   "The path of the file being evaluated, as a String.
 
-  Evaluates to nil when there is no file, eg. in the REPL."
+  When there is no file, e.g. in the REPL, the value is not defined."
   {:added "1.0"})
 
 (add-doc-and-meta *command-line-args*
@@ -5766,11 +5992,29 @@
   {:added "1.0"})
 
 (add-doc-and-meta *read-eval*
-  "When set to logical false, the EvalReader (#=(...)) is disabled in the 
-  read/load in the thread-local binding.
-  Example: (binding [*read-eval* false] (read-string \"#=(eval (def x 3))\"))
-
-  Defaults to true"
+ "Defaults to true (or value specified by system property, see below)
+  ***This setting implies that the full power of the reader is in play,
+  including syntax that can cause code to execute. It should never be
+  used with untrusted sources. See also: clojure.edn/read.***
+
+  When set to logical false in the thread-local binding,
+  the eval reader (#=) and record/type literal syntax are disabled in read/load.
+  Example (will fail): (binding [*read-eval* false] (read-string \"#=(* 2 21)\"))
+
+  The default binding can be controlled by the system property
+  'clojure.read.eval' System properties can be set on the command line
+  like this:
+
+  java -Dclojure.read.eval=false ...
+
+  The system property can also be set to 'unknown' via
+  -Dclojure.read.eval=unknown, in which case the default binding
+  is :unknown and all reads will fail in contexts where *read-eval*
+  has not been explicitly bound to either true or false. This setting
+  can be a useful diagnostic tool to ensure that all of your reads
+  occur in considered contexts. You can also accomplish this in a
+  particular scope by binding *read-eval* to :unknown
+  "
   {:added "1.0"})
 
 (defn future?
@@ -5892,7 +6136,7 @@
   (let [buckets (loop [m {} ks tests vs thens]
                   (if (and ks vs)
                     (recur
-                      (update-in m [(hash (first ks))] (fnil conj []) [(first ks) (first vs)])
+                      (update-in m [(clojure.lang.Util/hash (first ks))] (fnil conj []) [(first ks) (first vs)])
                       (next ks) (next vs))
                     m))
         assoc-multi (fn [m h bucket]
@@ -5919,17 +6163,18 @@
   post-switch equivalence checking must not be done (occurs with hash
   collisions)."
   [expr-sym default tests thens]
-  (let [hashes (into1 #{} (map hash tests))]
+  (let [hashcode #(clojure.lang.Util/hash %)
+        hashes (into1 #{} (map hashcode tests))]
     (if (== (count tests) (count hashes))
       (if (fits-table? hashes)
         ; compact case ints, no shift-mask
-        [0 0 (case-map hash identity tests thens) :compact]
+        [0 0 (case-map hashcode identity tests thens) :compact]
         (let [[shift mask] (or (maybe-min-hash hashes) [0 0])]
           (if (zero? mask)
             ; sparse case ints, no shift-mask
-            [0 0 (case-map hash identity tests thens) :sparse]
+            [0 0 (case-map hashcode identity tests thens) :sparse]
             ; compact case ints, with shift-mask
-            [shift mask (case-map #(shift-mask shift mask (hash %)) identity tests thens) :compact])))
+            [shift mask (case-map #(shift-mask shift mask (hashcode %)) identity tests thens) :compact])))
       ; resolve hash collisions and try again
       (let [[tests thens skip-check] (merge-hash-collisions expr-sym default tests thens)
             [shift mask case-map switch-type] (prep-hashes expr-sym default tests thens)
@@ -6001,6 +6246,21 @@
           (let [[shift mask imap switch-type skip-check] (prep-hashes ge default tests thens)]
             `(let [~ge ~e] (case* ~ge ~shift ~mask ~default ~imap ~switch-type :hash-identity ~skip-check))))))))
 
+
+;; redefine reduce with internal-reduce
+(defn reduced
+  "Wraps x in a way such that a reduce will terminate with the value x"
+  {:added "1.5"}
+  [x]
+  (clojure.lang.Reduced. x))
+
+(defn reduced?
+  "Returns true if x is the result of a call to reduced"
+  {:inline (fn [x] `(clojure.lang.RT/isReduced ~x ))
+   :inline-arities #{1}
+   :added "1.5"}
+  ([x] (clojure.lang.RT/isReduced x)))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; helper files ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 (alter-meta! (find-ns 'clojure.core) assoc :doc "Fundamental library of the Clojure language")
 (load "core_proxy")
@@ -6012,7 +6272,6 @@
 (load "instant")
 (load "uuid")
 
-;; redefine reduce with internal-reduce
 (defn reduce
   "f should be a function of 2 arguments. If val is not supplied,
   returns the result of applying f to the first 2 items in coll, then
@@ -6030,6 +6289,11 @@
      (clojure.core.protocols/coll-reduce coll f val)))
 
 (extend-protocol clojure.core.protocols/IKVReduce
+ nil
+ (kv-reduce
+  [_ f init]
+  init)
+
  ;;slow path default
  clojure.lang.IPersistentMap
  (kv-reduce 
@@ -6074,7 +6338,7 @@
    :static true}
   [to from]
   (if (instance? clojure.lang.IEditableCollection to)
-    (persistent! (reduce conj! (transient to) from))
+    (with-meta (persistent! (reduce conj! (transient to) from)) (meta to))
     (reduce conj to from)))
 
 (defn mapv
@@ -6123,7 +6387,7 @@
   ([f & opts]
      (let [opts (normalize-slurp-opts opts)
            sb (StringBuilder.)]
-       (with-open [#^java.io.Reader r (apply jio/reader f opts)]
+       (with-open [^java.io.Reader r (apply jio/reader f opts)]
          (loop [c (.read r)]
            (if (neg? c)
              (str sb)
@@ -6136,7 +6400,7 @@
   closes f. Options passed to clojure.java.io/writer."
   {:added "1.2"}
   [f content & options]
-  (with-open [#^java.io.Writer w (apply jio/writer f options)]
+  (with-open [^java.io.Writer w (apply jio/writer f options)]
     (.write w (str content))))
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; futures (needs proxy);;;;;;;;;;;;;;;;;;
@@ -6153,13 +6417,11 @@
         fut (.submit clojure.lang.Agent/soloExecutor ^Callable f)]
     (reify 
      clojure.lang.IDeref 
-     (deref [_] (.get fut))
+     (deref [_] (deref-future fut))
      clojure.lang.IBlockingDeref
      (deref
       [_ timeout-ms timeout-val]
-      (try (.get fut timeout-ms java.util.concurrent.TimeUnit/MILLISECONDS)
-           (catch java.util.concurrent.TimeoutException e
-             timeout-val)))
+      (deref-future fut timeout-ms timeout-val))
      clojure.lang.IPending
      (isRealized [_] (.isDone fut))
      java.util.concurrent.Future
@@ -6276,8 +6538,7 @@
          "-SNAPSHOT")))
 
 (defn promise
-  "Alpha - subject to change.
-  Returns a promise object that can be read with deref/@, and set,
+  "Returns a promise object that can be read with deref/@, and set,
   once only, with deliver. Calls to deref/@ prior to delivery will
   block, unless the variant of deref with timeout is used. All
   subsequent derefs will return the same delivered value without
@@ -6308,9 +6569,8 @@
         this)))))
 
 (defn deliver
-  "Alpha - subject to change.
-  Delivers the supplied value to the promise, releasing any pending
-  derefs. A subsequent call to deliver on a promise will throw an exception."
+  "Delivers the supplied value to the promise, releasing any pending
+  derefs. A subsequent call to deliver on a promise will have no effect."
   {:added "1.1"
    :static true}
   [promise val] (promise val))
@@ -6563,8 +6823,24 @@
 (defn- ^{:dynamic true} assert-valid-fdecl
   "A good fdecl looks like (([a] ...) ([a b] ...)) near the end of defn."
   [fdecl]
-  (if-let [bad-args (seq (remove #(vector? %) (map first fdecl)))]
-    (throw (IllegalArgumentException. (str "Parameter declaration " (first bad-args) " should be a vector")))))
+  (when (empty? fdecl) (throw (IllegalArgumentException.
+                                "Parameter declaration missing")))
+  (let [argdecls (map 
+                   #(if (seq? %)
+                      (first %)
+                      (throw (IllegalArgumentException. 
+                        (if (seq? (first fdecl))
+                          (str "Invalid signature "
+                               %
+                               " should be a list")
+                          (str "Parameter declaration "
+                               %
+                               " should be a vector")))))
+                   fdecl)
+        bad-args (seq (remove #(vector? %) argdecls))]
+    (when bad-args
+      (throw (IllegalArgumentException. (str "Parameter declaration " (first bad-args) 
+                                             " should be a vector"))))))
 
 (defn with-redefs-fn
   "Temporarily redefines Vars during a call to func.  Each val of
@@ -6579,7 +6855,7 @@
                     (doseq [[a-var a-val] m]
                       (.bindRoot ^clojure.lang.Var a-var a-val)))
         old-vals (zipmap (keys binding-map)
-                         (map deref (keys binding-map)))]
+                         (map #(.getRawRoot ^clojure.lang.Var %) (keys binding-map)))]
     (try
       (root-bind binding-map)
       (func)
@@ -6606,6 +6882,66 @@
   {:added "1.3"}
   [^clojure.lang.IPending x] (.isRealized x))
 
+(defmacro cond->
+  "Takes an expression and a set of test/form pairs. Threads expr (via ->)
+  through each form for which the corresponding test
+  expression is true. Note that, unlike cond branching, cond-> threading does
+  not short circuit after the first true test expression."
+  {:added "1.5"}
+  [expr & clauses]
+  (assert (even? (count clauses)))
+  (let [g (gensym)
+        pstep (fn [[test step]] `(if ~test (-> ~g ~step) ~g))]
+    `(let [~g ~expr
+           ~@(interleave (repeat g) (map pstep (partition 2 clauses)))]
+       ~g)))
+
+(defmacro cond->>
+  "Takes an expression and a set of test/form pairs. Threads expr (via ->>)
+  through each form for which the corresponding test expression
+  is true.  Note that, unlike cond branching, cond->> threading does not short circuit
+  after the first true test expression."
+  {:added "1.5"}
+  [expr & clauses]
+  (assert (even? (count clauses)))
+  (let [g (gensym)
+        pstep (fn [[test step]] `(if ~test (->> ~g ~step) ~g))]
+    `(let [~g ~expr
+           ~@(interleave (repeat g) (map pstep (partition 2 clauses)))]
+       ~g)))
+
+(defmacro as->
+  "Binds name to expr, evaluates the first form in the lexical context
+  of that binding, then binds name to that result, repeating for each
+  successive form, returning the result of the last form."
+  {:added "1.5"}
+  [expr name & forms]
+  `(let [~name ~expr
+         ~@(interleave (repeat name) forms)]
+     ~name))
+
+(defmacro some->
+  "When expr is not nil, threads it into the first form (via ->),
+  and when that result is not nil, through the next etc"
+  {:added "1.5"}
+  [expr & forms]
+  (let [g (gensym)
+        pstep (fn [step] `(if (nil? ~g) nil (-> ~g ~step)))]
+    `(let [~g ~expr
+           ~@(interleave (repeat g) (map pstep forms))]
+       ~g)))
+
+(defmacro some->>
+  "When expr is not nil, threads it into the first form (via ->>),
+  and when that result is not nil, through the next etc"
+  {:added "1.5"}
+  [expr & forms]
+  (let [g (gensym)
+        pstep (fn [step] `(if (nil? ~g) nil (->> ~g ~step)))]
+    `(let [~g ~expr
+           ~@(interleave (repeat g) (map pstep forms))]
+       ~g)))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; data readers ;;;;;;;;;;;;;;;;;;
 
 (def ^{:added "1.4"} default-data-readers
@@ -6643,6 +6979,13 @@
   data_readers.clj or by rebinding this Var."
   {})
 
+(def ^{:added "1.5" :dynamic true} *default-data-reader-fn* 
+  "When no data reader is found for a tag and *default-data-reader-fn*
+  is non-nil, it will be called with two arguments,
+  the tag and the value.  If *default-data-reader-fn* is nil (the
+  default), an exception will be thrown for the unknown tag."
+  nil)
+
 (defn- data-reader-urls []
   (enumeration-seq
    (.. Thread currentThread getContextClassLoader
@@ -6667,12 +7010,14 @@
              (throw (ex-info (str "Invalid form in data-reader file")
                              {:url url
                               :form k})))
-           (when (contains? mappings k)
-             (throw (ex-info "Conflicting data-reader mapping"
-                             {:url url
-                              :conflict k
-                              :mappings m})))
-           (assoc m k (data-reader-var v)))
+           (let [v-var (data-reader-var v)]
+             (when (and (contains? mappings k)
+                        (not= (mappings k) v-var))
+               (throw (ex-info "Conflicting data-reader mapping"
+                               {:url url
+                                :conflict k
+                                :mappings m})))
+             (assoc m k v-var)))
          mappings
          new-mappings)))))
 
diff --git a/src/clj/clojure/core/protocols.clj b/src/clj/clojure/core/protocols.clj
index 199ce64..4f5ffa1 100644
--- a/src/clj/clojure/core/protocols.clj
+++ b/src/clj/clojure/core/protocols.clj
@@ -66,14 +66,20 @@
         (if (.hasNext iter)
           (loop [ret (.next iter)]
             (if (.hasNext iter)
-              (recur (f ret (.next iter)))
+              (let [ret (f ret (.next iter))]
+                (if (reduced? ret)
+                  @ret
+                  (recur ret)))
               ret))
           (f))))
    ([coll f val]
       (let [iter (.iterator coll)]
         (loop [ret val]
           (if (.hasNext iter)
-            (recur (f ret (.next iter)))
+            (let [ret (f ret (.next iter))]
+                (if (reduced? ret)
+                  @ret
+                  (recur ret)))
             ret)))))
   )
 
@@ -89,9 +95,12 @@
    [s f val]
    (if-let [s (seq s)]
      (if (chunked-seq? s)
-       (recur (chunk-next s)
-              f
-              (.reduce (chunk-first s) f val))
+       (let [ret (.reduce (chunk-first s) f val)]
+         (if (reduced? ret)
+           @ret
+           (recur (chunk-next s)
+                  f
+                  ret)))
        (internal-reduce s f val))
      val))
  
@@ -102,7 +111,10 @@
      (loop [i (.i str-seq)
             val val]
        (if (< i (.length s))
-         (recur (inc i) (f val (.charAt s i)))
+         (let [ret (f val (.charAt s i))]
+                (if (reduced? ret)
+                  @ret
+                  (recur (inc i) ret)))
          val))))
   
   clojure.lang.ArraySeq
@@ -112,7 +124,10 @@
          (loop [i (.index a-seq)
                 val val]
            (if (< i (alength arr))
-             (recur (inc i) (f val (aget arr i)))
+             (let [ret (f val (aget arr i))]
+                (if (reduced? ret)
+                  @ret
+                  (recur (inc i) ret)))
              val))))
   
   java.lang.Object
@@ -125,7 +140,10 @@
      (if-let [s (seq s)]
        ;; roll over to faster implementation if underlying seq changes type
        (if (identical? (class s) cls)
-         (recur cls (next s) f (f val (first s)))
+         (let [ret (f val (first s))]
+                (if (reduced? ret)
+                  @ret
+                  (recur cls (next s) f ret)))
          (internal-reduce s f val))
        val))))
 
@@ -136,7 +154,10 @@
          (loop [i (.index a-seq)
                 val val]
            (if (< i (alength arr))
-             (recur (inc i) (f val (aget arr i)))
+             (let [ret (f val (aget arr i))]
+                (if (reduced? ret)
+                  @ret
+                  (recur (inc i) ret)))
              val)))))
 
 (defn- emit-array-impls*
diff --git a/src/clj/clojure/core/reducers.clj b/src/clj/clojure/core/reducers.clj
new file mode 100644
index 0000000..f7e46b3
--- /dev/null
+++ b/src/clj/clojure/core/reducers.clj
@@ -0,0 +1,367 @@
+;   Copyright (c) Rich Hickey. All rights reserved.
+;   The use and distribution terms for this software are covered by the
+;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;   which can be found in the file epl-v10.html at the root of this distribution.
+;   By using this software in any fashion, you are agreeing to be bound by
+;   the terms of this license.
+;   You must not remove this notice, or any other, from this software.
+
+(ns ^{:doc
+      "A library for reduction and parallel folding. Alpha and subject
+      to change.  Note that fold and its derivatives require Java 7+ or
+      Java 6 + jsr166y.jar for fork/join support. See Clojure's pom.xml for the
+      dependency info."
+      :author "Rich Hickey"}
+  clojure.core.reducers
+  (:refer-clojure :exclude [reduce map mapcat filter remove take take-while drop flatten])
+  (:require [clojure.walk :as walk]))
+
+(alias 'core 'clojure.core)
+(set! *warn-on-reflection* true)
+
+;;;;;;;;;;;;;; some fj stuff ;;;;;;;;;;
+
+(defmacro ^:private compile-if
+  "Evaluate `exp` and if it returns logical true and doesn't error, expand to
+  `then`.  Else expand to `else`.
+
+  (compile-if (Class/forName \"java.util.concurrent.ForkJoinTask\")
+    (do-cool-stuff-with-fork-join)
+    (fall-back-to-executor-services))"
+  [exp then else]
+  (if (try (eval exp)
+           (catch Throwable _ false))
+    `(do ~then)
+    `(do ~else)))
+
+(compile-if
+ (Class/forName "java.util.concurrent.ForkJoinTask")
+ ;; We're running a JDK 7+
+ (do
+   (def pool (delay (java.util.concurrent.ForkJoinPool.)))
+
+   (defn fjtask [^Callable f]
+     (java.util.concurrent.ForkJoinTask/adapt f))
+
+   (defn- fjinvoke [f]
+     (if (java.util.concurrent.ForkJoinTask/inForkJoinPool)
+       (f)
+       (.invoke ^java.util.concurrent.ForkJoinPool @pool ^java.util.concurrent.ForkJoinTask (fjtask f))))
+
+   (defn- fjfork [task] (.fork ^java.util.concurrent.ForkJoinTask task))
+
+   (defn- fjjoin [task] (.join ^java.util.concurrent.ForkJoinTask task)))
+ ;; We're running a JDK <7
+ (do
+   (def pool (delay (jsr166y.ForkJoinPool.)))
+
+   (defn fjtask [^Callable f]
+     (jsr166y.ForkJoinTask/adapt f))
+
+   (defn- fjinvoke [f]
+     (if (jsr166y.ForkJoinTask/inForkJoinPool)
+       (f)
+       (.invoke ^jsr166y.ForkJoinPool @pool ^jsr166y.ForkJoinTask (fjtask f))))
+
+   (defn- fjfork [task] (.fork ^jsr166y.ForkJoinTask task))
+
+   (defn- fjjoin [task] (.join ^jsr166y.ForkJoinTask task))))
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn reduce
+  "Like core/reduce except:
+     When init is not provided, (f) is used.
+     Maps are reduced with reduce-kv"
+  ([f coll] (reduce f (f) coll))
+  ([f init coll]
+     (if (instance? java.util.Map coll)
+       (clojure.core.protocols/kv-reduce coll f init)
+       (clojure.core.protocols/coll-reduce coll f init))))
+
+(defprotocol CollFold
+  (coll-fold [coll n combinef reducef]))
+
+(defn fold
+  "Reduces a collection using a (potentially parallel) reduce-combine
+  strategy. The collection is partitioned into groups of approximately
+  n (default 512), each of which is reduced with reducef (with a seed
+  value obtained by calling (combinef) with no arguments). The results
+  of these reductions are then reduced with combinef (default
+  reducef). combinef must be associative, and, when called with no
+  arguments, (combinef) must produce its identity element. These
+  operations may be performed in parallel, but the results will
+  preserve order."
+  {:added "1.5"}
+  ([reducef coll] (fold reducef reducef coll))
+  ([combinef reducef coll] (fold 512 combinef reducef coll))
+  ([n combinef reducef coll]
+     (coll-fold coll n combinef reducef)))
+
+(defn reducer
+  "Given a reducible collection, and a transformation function xf,
+  returns a reducible collection, where any supplied reducing
+  fn will be transformed by xf. xf is a function of reducing fn to
+  reducing fn."
+  {:added "1.5"}
+  ([coll xf]
+     (reify
+      clojure.core.protocols/CollReduce
+      (coll-reduce [this f1]
+                   (clojure.core.protocols/coll-reduce this f1 (f1)))
+      (coll-reduce [_ f1 init]
+                   (clojure.core.protocols/coll-reduce coll (xf f1) init)))))
+
+(defn folder
+  "Given a foldable collection, and a transformation function xf,
+  returns a foldable collection, where any supplied reducing
+  fn will be transformed by xf. xf is a function of reducing fn to
+  reducing fn."
+  {:added "1.5"}
+  ([coll xf]
+     (reify
+      clojure.core.protocols/CollReduce
+      (coll-reduce [_ f1]
+                   (clojure.core.protocols/coll-reduce coll (xf f1) (f1)))
+      (coll-reduce [_ f1 init]
+                   (clojure.core.protocols/coll-reduce coll (xf f1) init))
+
+      CollFold
+      (coll-fold [_ n combinef reducef]
+                 (coll-fold coll n combinef (xf reducef))))))
+
+(defn- do-curried
+  [name doc meta args body]
+  (let [cargs (vec (butlast args))]
+    `(defn ~name ~doc ~meta
+       (~cargs (fn [x#] (~name ~@cargs x#)))
+       (~args ~@body))))
+
+(defmacro ^:private defcurried
+  "Builds another arity of the fn that returns a fn awaiting the last
+  param"
+  [name doc meta args & body]
+  (do-curried name doc meta args body))
+
+(defn- do-rfn [f1 k fkv]
+  `(fn
+     ([] (~f1))
+     ~(clojure.walk/postwalk
+       #(if (sequential? %)
+          ((if (vector? %) vec identity)
+           (core/remove #{k} %))
+          %)
+       fkv)
+     ~fkv))
+
+(defmacro ^:private rfn
+  "Builds 3-arity reducing fn given names of wrapped fn and key, and k/v impl."
+  [[f1 k] fkv]
+  (do-rfn f1 k fkv))
+
+(defcurried map
+  "Applies f to every value in the reduction of coll. Foldable."
+  {:added "1.5"}
+  [f coll]
+  (folder coll
+   (fn [f1]
+     (rfn [f1 k]
+          ([ret k v]
+             (f1 ret (f k v)))))))
+
+(defcurried mapcat
+  "Applies f to every value in the reduction of coll, concatenating the result
+  colls of (f val). Foldable."
+  {:added "1.5"}
+  [f coll]
+  (folder coll
+   (fn [f1]
+     (let [f1 (fn
+                ([ret v]
+                  (let [x (f1 ret v)] (if (reduced? x) (reduced x) x)))
+                ([ret k v]
+                  (let [x (f1 ret k v)] (if (reduced? x) (reduced x) x))))]
+       (rfn [f1 k]
+            ([ret k v]
+               (reduce f1 ret (f k v))))))))
+
+(defcurried filter
+  "Retains values in the reduction of coll for which (pred val)
+  returns logical true. Foldable."
+  {:added "1.5"}
+  [pred coll]
+  (folder coll
+   (fn [f1]
+     (rfn [f1 k]
+          ([ret k v]
+             (if (pred k v)
+               (f1 ret k v)
+               ret))))))
+
+(defcurried remove
+  "Removes values in the reduction of coll for which (pred val)
+  returns logical true. Foldable."
+  {:added "1.5"}
+  [pred coll]
+  (filter (complement pred) coll))
+
+(defcurried flatten
+  "Takes any nested combination of sequential things (lists, vectors,
+  etc.) and returns their contents as a single, flat foldable
+  collection."
+  {:added "1.5"}
+  [coll]
+  (folder coll
+   (fn [f1]
+     (fn
+       ([] (f1))
+       ([ret v]
+          (if (sequential? v)
+            (clojure.core.protocols/coll-reduce (flatten v) f1 ret)
+            (f1 ret v)))))))
+
+(defcurried take-while
+  "Ends the reduction of coll when (pred val) returns logical false."
+  {:added "1.5"}
+  [pred coll]
+  (reducer coll
+   (fn [f1]
+     (rfn [f1 k]
+          ([ret k v]
+             (if (pred k v)
+               (f1 ret k v)
+               (reduced ret)))))))
+
+(defcurried take
+  "Ends the reduction of coll after consuming n values."
+  {:added "1.5"}
+  [n coll]
+  (reducer coll
+   (fn [f1]
+     (let [cnt (atom n)]
+       (rfn [f1 k]
+         ([ret k v]
+            (swap! cnt dec)
+            (if (neg? @cnt)
+              (reduced ret)
+              (f1 ret k v))))))))
+
+(defcurried drop
+  "Elides the first n values from the reduction of coll."
+  {:added "1.5"}
+  [n coll]
+  (reducer coll
+   (fn [f1]
+     (let [cnt (atom n)]
+       (rfn [f1 k]
+         ([ret k v]
+            (swap! cnt dec)
+            (if (neg? @cnt)
+              (f1 ret k v)
+              ret)))))))
+
+;;do not construct this directly, use cat
+(deftype Cat [cnt left right]
+  clojure.lang.Counted
+  (count [_] cnt)
+
+  clojure.lang.Seqable
+  (seq [_] (concat (seq left) (seq right)))
+
+  clojure.core.protocols/CollReduce
+  (coll-reduce [this f1] (clojure.core.protocols/coll-reduce this f1 (f1)))
+  (coll-reduce
+   [_  f1 init]
+   (clojure.core.protocols/coll-reduce
+    right f1
+    (clojure.core.protocols/coll-reduce left f1 init)))
+
+  CollFold
+  (coll-fold
+   [_ n combinef reducef]
+   (fjinvoke
+    (fn []
+      (let [rt (fjfork (fjtask #(coll-fold right n combinef reducef)))]
+        (combinef
+         (coll-fold left n combinef reducef)
+         (fjjoin rt)))))))
+
+(defn cat
+  "A high-performance combining fn that yields the catenation of the
+  reduced values. The result is reducible, foldable, seqable and
+  counted, providing the identity collections are reducible, seqable
+  and counted. The single argument version will build a combining fn
+  with the supplied identity constructor. Tests for identity
+  with (zero? (count x)). See also foldcat."
+  {:added "1.5"}
+  ([] (java.util.ArrayList.))
+  ([ctor]
+     (fn
+       ([] (ctor))
+       ([left right] (cat left right))))
+  ([left right]
+     (cond
+      (zero? (count left)) right
+      (zero? (count right)) left
+      :else
+      (Cat. (+ (count left) (count right)) left right))))
+
+(defn append!
+  ".adds x to acc and returns acc"
+  {:added "1.5"}
+  [^java.util.Collection acc x]
+  (doto acc (.add x)))
+
+(defn foldcat
+  "Equivalent to (fold cat append! coll)"
+  {:added "1.5"}
+  [coll]
+  (fold cat append! coll))
+
+(defn monoid
+  "Builds a combining fn out of the supplied operator and identity
+  constructor. op must be associative and ctor called with no args
+  must return an identity value for it."
+  {:added "1.5"}
+  [op ctor]
+  (fn m
+    ([] (ctor))
+    ([a b] (op a b))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; fold impls ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+(defn- foldvec
+  [v n combinef reducef]
+  (cond
+   (empty? v) (combinef)
+   (<= (count v) n) (reduce reducef (combinef) v)
+   :else
+   (let [split (quot (count v) 2)
+         v1 (subvec v 0 split)
+         v2 (subvec v split (count v))
+         fc (fn [child] #(foldvec child n combinef reducef))]
+     (fjinvoke
+      #(let [f1 (fc v1)
+             t2 (fjtask (fc v2))]
+         (fjfork t2)
+         (combinef (f1) (fjjoin t2)))))))
+
+(extend-protocol CollFold
+ nil
+ (coll-fold
+  [coll n combinef reducef]
+  (combinef))
+
+ Object
+ (coll-fold
+  [coll n combinef reducef]
+  ;;can't fold, single reduce
+  (reduce reducef (combinef) coll))
+
+ clojure.lang.IPersistentVector
+ (coll-fold
+  [v n combinef reducef]
+  (foldvec v n combinef reducef))
+
+ clojure.lang.PersistentHashMap
+ (coll-fold
+  [m n combinef reducef]
+  (.fold m n combinef reducef fjinvoke fjtask fjfork fjjoin)))
diff --git a/src/clj/clojure/core_deftype.clj b/src/clj/clojure/core_deftype.clj
index cca4501..c560447 100644
--- a/src/clj/clojure/core_deftype.clj
+++ b/src/clj/clojure/core_deftype.clj
@@ -17,7 +17,15 @@
   (.replace (str ns) \- \_))
 
 ;for now, built on gen-interface
-(defmacro definterface 
+(defmacro definterface
+  "Creates a new Java interface with the given name and method sigs.
+  The method return types and parameter types may be specified with type hints,
+  defaulting to Object if omitted.
+
+  (definterface MyInterface
+    (^int method1 [x])
+    (^Bar method2 [^Baz b ^Quux q]))"
+  {:added "1.2"} ;; Present since 1.2, but made public in 1.5.
   [name & sigs]
   (let [tag (fn [x] (or (:tag (meta x)) Object))
         psig (fn [[name [& args]]]
@@ -148,7 +156,8 @@
         hinted-fields fields
         fields (vec (map #(with-meta % nil) fields))
         base-fields fields
-        fields (conj fields '__meta '__extmap)]
+        fields (conj fields '__meta '__extmap)
+        type-hash (hash classname)]
     (when (some #{:volatile-mutable :unsynchronized-mutable} (mapcat (comp keys meta) hinted-fields))
       (throw (IllegalArgumentException. ":volatile-mutable or :unsynchronized-mutable not supported for record fields")))
     (let [gs (gensym)]
@@ -157,8 +166,9 @@
         [(conj i 'clojure.lang.IRecord)
          m])
       (eqhash [[i m]] 
-        [i
-         (conj m 
+        [(conj i 'clojure.lang.IHashEq)
+         (conj m
+               `(hasheq [this#] (bit-xor ~type-hash (clojure.lang.APersistentMap/mapHasheq this#)))
                `(hashCode [this#] (clojure.lang.APersistentMap/mapHash this#))
                `(equals [this# ~gs] (clojure.lang.APersistentMap/mapEquals this# ~gs)))])
       (iobj [[i m]] 
@@ -266,14 +276,14 @@
 (defn- validate-fields
   ""
   [fields]
+  (when-not (vector? fields)
+    (throw (AssertionError. "No fields vector given.")))
   (let [specials #{'__meta '__extmap}]
     (when (some specials fields)
       (throw (AssertionError. (str "The names in " specials " cannot be used as field names for types or records."))))))
 
 (defmacro defrecord
-  "Alpha - subject to change
-  
-  (defrecord name [fields*]  options* specs*)
+  "(defrecord name [fields*]  options* specs*)
   
   Currently there are no options.
 
@@ -293,7 +303,7 @@
   are optional. The only methods that can be supplied are those
   declared in the protocols/interfaces.  Note that method bodies are
   not closures, the local environment includes only the named fields,
-  and those fields can be accessed directy.
+  and those fields can be accessed directly.
 
   Method definitions take the form:
 
@@ -337,9 +347,10 @@
   Given (defrecord TypeName ...), two factory functions will be
   defined: ->TypeName, taking positional parameters for the fields,
   and map->TypeName, taking a map of keywords to field values."
-  {:added "1.2"}
+  {:added "1.2"
+   :arglists '([name [& fields] & opts+specs])}
 
-  [name [& fields] & opts+specs]
+  [name fields & opts+specs]
   (validate-fields fields)
   (let [gname name
         [interfaces methods opts] (parse-opts+specs opts+specs)
@@ -358,7 +369,14 @@
          ([m#] (~(symbol (str classname "/create")) m#)))
        ~classname)))
 
-(defn- emit-deftype* 
+(defn record?
+  "Returns true if x is a record"
+  {:added "1.6"
+   :static true}
+  [x]
+  (instance? clojure.lang.IRecord x))
+
+(defn- emit-deftype*
   "Do not use this directly - use deftype"
   [tagname name fields interfaces methods]
   (let [classname (with-meta (symbol (str (namespace-munge *ns*) "." name)) (meta name))
@@ -368,9 +386,7 @@
        ~@methods)))
 
 (defmacro deftype
-  "Alpha - subject to change
-  
-  (deftype name [fields*]  options* specs*)
+  "(deftype name [fields*]  options* specs*)
   
   Currently there are no options.
 
@@ -430,9 +446,10 @@
 
   Given (deftype TypeName ...), a factory function called ->TypeName
   will be defined, taking positional parameters for the fields"
-  {:added "1.2"}
+  {:added "1.2"
+   :arglists '([name [& fields] & opts+specs])}
 
-  [name [& fields] & opts+specs]
+  [name fields & opts+specs]
   (validate-fields fields)
   (let [gname name
         [interfaces methods opts] (parse-opts+specs opts+specs)
@@ -540,7 +557,7 @@
                     (let [gargs (map #(gensym (str "gf__" % "__")) args)
                           target (first gargs)]
                       `([~@gargs]
-                          (. ~(with-meta target {:tag on-interface})  ~(or on-method method) ~@(rest gargs)))))
+                          (. ~(with-meta target {:tag on-interface}) (~(or on-method method) ~@(rest gargs))))))
                   arglists))
              ^clojure.lang.AFunction f#
              (fn ~gthis
@@ -582,22 +599,25 @@
             string? (recur (assoc opts :doc (first sigs)) (next sigs))
             keyword? (recur (assoc opts (first sigs) (second sigs)) (nnext sigs))
             [opts sigs]))
-        sigs (reduce1 (fn [m s]
-                       (let [name-meta (meta (first s))
-                             mname (with-meta (first s) nil)
-                             [arglists doc]
-                               (loop [as [] rs (rest s)]
-                                 (if (vector? (first rs))
-                                   (recur (conj as (first rs)) (next rs))
-                                   [(seq as) (first rs)]))]
-                         (when (some #{0} (map count arglists))
-                           (throw (IllegalArgumentException. (str "Protocol fn: " mname " must take at least one arg"))))
-                         (assoc m (keyword mname)
-                                (merge name-meta
-                                       {:name (vary-meta mname assoc :doc doc :arglists arglists)
-                                        :arglists arglists
-                                        :doc doc}))))
-                     {} sigs)
+        sigs (when sigs
+               (reduce1 (fn [m s]
+                          (let [name-meta (meta (first s))
+                                mname (with-meta (first s) nil)
+                                [arglists doc]
+                                (loop [as [] rs (rest s)]
+                                  (if (vector? (first rs))
+                                    (recur (conj as (first rs)) (next rs))
+                                    [(seq as) (first rs)]))]
+                            (when (some #{0} (map count arglists))
+                              (throw (IllegalArgumentException. (str "Definition of function " mname " in protocol " name " must take at least one arg."))))
+                            (when (m (keyword mname))
+                              (throw (IllegalArgumentException. (str "Function " mname " in protocol " name " was redefined. Specify all arities in single definition."))))
+                            (assoc m (keyword mname)
+                                   (merge name-meta
+                                          {:name (vary-meta mname assoc :doc doc :arglists arglists)
+                                           :arglists arglists
+                                           :doc doc}))))
+                        {} sigs))
         meths (mapcat (fn [sig]
                         (let [m (munge (:name sig))]
                           (map #(vector m (vec (repeat (dec (count %))'Object)) 'Object) 
@@ -607,7 +627,8 @@
      (defonce ~name {})
      (gen-interface :name ~iname :methods ~meths)
      (alter-meta! (var ~name) assoc :doc ~(:doc opts))
-     (#'assert-same-protocol (var ~name) '~(map :name (vals sigs)))
+     ~(when sigs
+        `(#'assert-same-protocol (var ~name) '~(map :name (vals sigs))))
      (alter-var-root (var ~name) merge 
                      (assoc ~opts 
                        :sigs '~sigs 
diff --git a/src/clj/clojure/core_print.clj b/src/clj/clojure/core_print.clj
index 641c707..5235e2a 100644
--- a/src/clj/clojure/core_print.clj
+++ b/src/clj/clojure/core_print.clj
@@ -87,13 +87,20 @@
   (print-args o w)
   (.write w ")"))
 
-(defmethod print-method Object [o, ^Writer w]
+(defn- print-object [o, ^Writer w]
+  (when (instance? clojure.lang.IMeta o)
+    (print-meta o w))
   (.write w "#<")
-  (.write w (.getSimpleName (class o)))
-  (.write w " ")
+  (let [name (.getSimpleName (class o))]
+    (when (seq name) ;; anonymous classes have a simple name of ""
+      (.write w name)
+      (.write w " ")))
   (.write w (str o))
   (.write w ">"))
 
+(defmethod print-method Object [o, ^Writer w]
+  (print-object o w))
+
 (defmethod print-method clojure.lang.Keyword [o, ^Writer w]
   (.write w (str o)))
 
@@ -214,6 +221,40 @@
   (print-map m print-dup w)
   (.write w ")"))
 
+;; java.util
+(prefer-method print-method clojure.lang.IPersistentCollection java.util.Collection)
+(prefer-method print-method clojure.lang.IPersistentCollection java.util.RandomAccess)
+(prefer-method print-method java.util.RandomAccess java.util.List)
+(prefer-method print-method clojure.lang.IPersistentCollection java.util.Map)
+
+(defmethod print-method java.util.List [c, ^Writer w]
+  (if *print-readably*
+    (do
+      (print-meta c w)
+      (print-sequential "(" pr-on " " ")" c w))
+    (print-object c w)))
+
+(defmethod print-method java.util.RandomAccess [v, ^Writer w]
+  (if *print-readably*
+    (do
+      (print-meta v w)
+      (print-sequential "[" pr-on " " "]" v w))
+    (print-object v w)))
+
+(defmethod print-method java.util.Map [m, ^Writer w]
+  (if *print-readably*
+    (do
+      (print-meta m w)
+      (print-map m pr-on w))
+    (print-object m w)))
+
+(defmethod print-method java.util.Set [s, ^Writer w]
+  (if *print-readably*
+    (do
+      (print-meta s w)
+      (print-sequential "#{" pr-on " " "}" (seq s) w))
+    (print-object s w)))
+
 ;; Records
 
 (defmethod print-method clojure.lang.IRecord [r, ^Writer w]
@@ -241,7 +282,7 @@
   (print-meta s w)
   (print-sequential "#{" pr-on " " "}" (seq s) w))
 
-(def ^{:tag String 
+(def ^{:tag String
        :doc "Returns name string for char or nil if none"
        :added "1.0"} 
  char-name-string
@@ -343,7 +384,8 @@
                                      (agent-error o))
                               " FAILED"
                               ""))
-                    pr-on, "", ">", (list (if (and (instance? clojure.lang.IPending o) (not (.isRealized o)))
+                    pr-on, "", ">", (list (if (and (instance? clojure.lang.IPending o)
+                                                   (not (.isRealized ^clojure.lang.IPending o)))
                                             :pending
                                             @o)), w))
 
diff --git a/src/clj/clojure/core_proxy.clj b/src/clj/clojure/core_proxy.clj
index 830f5ce..b3f3bb8 100644
--- a/src/clj/clojure/core_proxy.clj
+++ b/src/clj/clojure/core_proxy.clj
@@ -358,9 +358,9 @@
 (defn proxy-call-with-super [call this meth]
  (let [m (proxy-mappings this)]
     (update-proxy this (assoc m meth nil))
-    (let [ret (call)]
-      (update-proxy this m)
-      ret)))
+    (try
+      (call)
+      (finally (update-proxy this m)))))
 
 (defmacro proxy-super 
   "Use to call a superclass method in the body of a proxy method. 
@@ -394,7 +394,7 @@
            []
       (containsKey [k] (contains? pmap k))
       (entryAt [k] (when (contains? pmap k) (new clojure.lang.MapEntry k (v k))))
-      (valAt ([k] (v k))
+      (valAt ([k] (when (contains? pmap k) (v k)))
 	     ([k default] (if (contains? pmap k) (v k) default)))
       (cons [m] (conj (snapshot) m))
       (count [] (count pmap))
diff --git a/src/clj/clojure/data.clj b/src/clj/clojure/data.clj
index 6e8dbcf..345b234 100644
--- a/src/clj/clojure/data.clj
+++ b/src/clj/clojure/data.clj
@@ -30,6 +30,22 @@
      (vec (repeat (apply max (keys m))  nil))
      m)))
 
+(defn- diff-associative-key
+  "Diff associative things a and b, comparing only the key k."
+  [a b k]
+  (let [va (get a k)
+        vb (get b k)
+        [a* b* ab] (diff va vb)
+        in-a (contains? a k)
+        in-b (contains? b k)
+        same (and in-a in-b
+                  (or (not (nil? ab))
+                      (and (nil? va) (nil? vb))))]
+    [(when (and in-a (or (not (nil? a*)) (not same))) {k a*})
+     (when (and in-b (or (not (nil? b*)) (not same))) {k b*})
+     (when same {k ab})
+     ]))
+
 (defn- diff-associative
   "Diff associative things a and b, comparing only keys in ks."
   [a b ks]
@@ -38,7 +54,7 @@
      (doall (map merge diff1 diff2)))
    [nil nil nil]
    (map
-    (fn [k] (map #(when % {k %}) (diff (get a k) (get b k))))
+    (partial diff-associative-key a b)
     ks)))
 
 (defn- diff-sequential
diff --git a/src/clj/clojure/edn.clj b/src/clj/clojure/edn.clj
new file mode 100644
index 0000000..6d1634a
--- /dev/null
+++ b/src/clj/clojure/edn.clj
@@ -0,0 +1,46 @@
+;   Copyright (c) Rich Hickey. All rights reserved.
+;   The use and distribution terms for this software are covered by the
+;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;   which can be found in the file epl-v10.html at the root of this distribution.
+;   By using this software in any fashion, you are agreeing to be bound by
+;   the terms of this license.
+;   You must not remove this notice, or any other, from this software.
+
+(ns ^{:doc "edn reading."
+      :author "Rich Hickey"}
+  clojure.edn
+  (:refer-clojure :exclude [read read-string]))
+
+(defn read
+  "Reads the next object from stream, which must be an instance of
+  java.io.PushbackReader or some derivee.  stream defaults to the
+  current value of *in*.
+
+  Reads data in the edn format (subset of Clojure data):
+  http://edn-format.org
+
+  opts is a map that can include the following keys:
+  :eof - value to return on end-of-file. When not supplied, eof throws an exception.
+  :readers  - a map of tag symbols to data-reader functions to be considered before default-data-readers.
+              When not supplied, only the default-data-readers will be used.
+  :default - A function of two args, that will, if present and no reader is found for a tag,
+             be called with the tag and the value."
+  
+  {:added "1.5"}
+  ([]
+   (read *in*))
+  ([stream]
+   (read {} stream))
+  ([opts stream]
+     (clojure.lang.EdnReader/read stream opts)))
+
+(defn read-string
+  "Reads one object from the string s. Returns nil when s is nil or empty.
+
+  Reads data in the edn format (subset of Clojure data):
+  http://edn-format.org
+
+  opts is a map as per clojure.edn/read"
+  {:added "1.5"}
+  ([s] (read-string {:eof nil} s))
+  ([opts s] (when s (clojure.lang.EdnReader/readString s opts))))
\ No newline at end of file
diff --git a/src/clj/clojure/genclass.clj b/src/clj/clojure/genclass.clj
index 2b1237e..c61d554 100644
--- a/src/clj/clojure/genclass.clj
+++ b/src/clj/clojure/genclass.clj
@@ -16,7 +16,7 @@
 ;(defn method-sig [^java.lang.reflect.Method meth]
 ;  [(. meth (getName)) (seq (. meth (getParameterTypes)))])
 
-(defn- non-private-methods [^Class c]
+(defn- filter-methods [^Class c invalid-method?]
   (loop [mm {}
          considered #{}
          c c]
@@ -32,17 +32,30 @@
                       mods (. meth (getModifiers))
                       mk (method-sig meth)]
                   (if (or (considered mk)
-                          (not (or (Modifier/isPublic mods) (Modifier/isProtected mods)))
-                          ;(. Modifier (isPrivate mods))
-                          (. Modifier (isStatic mods))
-                          (. Modifier (isFinal mods))
-                          (= "finalize" (.getName meth)))
+                          (invalid-method? meth))
                     (recur mm (conj considered mk) (next meths))
                     (recur (assoc mm mk meth) (conj considered mk) (next meths))))
                 [mm considered]))]
         (recur mm considered (. c (getSuperclass))))
       mm)))
 
+(defn- non-private-methods [^Class c]
+  (let [not-overridable? (fn [^java.lang.reflect.Method meth]
+                           (let [mods (. meth (getModifiers))]
+                             (or (not (or (Modifier/isPublic mods) (Modifier/isProtected mods)))
+                                 (. Modifier (isStatic mods))
+                                 (. Modifier (isFinal mods))
+                                 (= "finalize" (.getName meth)))))]
+    (filter-methods c not-overridable?)))
+
+(defn- protected-final-methods [^Class c]
+  (let [not-exposable? (fn [^java.lang.reflect.Method meth]
+                         (let [mods (. meth (getModifiers))]
+                           (not (and (Modifier/isProtected mods)
+                                     (Modifier/isFinal mods)
+                                     (not (Modifier/isStatic mods))))))]
+    (filter-methods c not-exposable?)))
+
 (defn- ctor-sigs [^Class super]
   (for [^Constructor ctor (. super (getDeclaredConstructors))
         :when (not (. Modifier (isPrivate (. ctor (getModifiers)))))]
@@ -288,13 +301,15 @@
     
                                         ;ctors
     (doseq [[pclasses super-pclasses] ctor-sig-map]
-      (let [pclasses (map the-class pclasses)
+      (let [constructor-annotations (meta pclasses)
+            pclasses (map the-class pclasses)
             super-pclasses (map the-class super-pclasses)
             ptypes (to-types pclasses)
             super-ptypes (to-types super-pclasses)
             m (new Method "<init>" (. Type VOID_TYPE) ptypes)
             super-m (new Method "<init>" (. Type VOID_TYPE) super-ptypes)
             gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)
+            _ (add-annotations gen constructor-annotations)
             no-init-label (. gen newLabel)
             end-label (. gen newLabel)
             no-post-init-label (. gen newLabel)
@@ -415,7 +430,8 @@
        (doseq [[local-mname ^java.lang.reflect.Method m] (reduce1 (fn [ms [[name _ _] m]]
                               (if (contains? exposes-methods (symbol name))
                                 (conj ms [((symbol name) exposes-methods) m])
-                                ms)) [] (seq mm))]
+                                ms)) [] (concat (seq mm)
+                                                (seq (protected-final-methods super))))]
          (let [ptypes (to-types (.getParameterTypes m))
                rtype (totype (.getReturnType m))
                exposer-m (new Method (str local-mname) rtype ptypes)
diff --git a/src/clj/clojure/gvec.clj b/src/clj/clojure/gvec.clj
index 6aabd1e..78bbd77 100644
--- a/src/clj/clojure/gvec.clj
+++ b/src/clj/clojure/gvec.clj
@@ -10,6 +10,8 @@
 
 (in-ns 'clojure.core)
 
+(import '(clojure.lang Murmur3))
+
 ;(set! *warn-on-reflection* true)
 
 (deftype VecNode [edit arr])
@@ -132,7 +134,9 @@
                (.equals (.nth this i) (nth o i)) (recur (inc i))
                :else false)))
      (or (instance? clojure.lang.Sequential o) (instance? java.util.List o))
-       (.equals (seq this) (seq o))
+       (if-let [st (seq this)]
+         (.equals st (seq o))
+         (nil? (seq o)))
      :else false))
 
   ;todo - cache
@@ -145,6 +149,11 @@
                                 (clojure.lang.Util/hash val)) 
                  (inc i))))))
 
+  ;todo - cache
+  clojure.lang.IHashEq
+  (hasheq [this]
+    (Murmur3/hashOrdered this))
+
   clojure.lang.Counted
   (count [_] cnt)
 
@@ -350,7 +359,7 @@
   (compareTo [this o]
     (if (identical? this o)
       0
-      (let [#^clojure.lang.IPersistentVector v (cast clojure.lang.IPersistentVector o)
+      (let [^clojure.lang.IPersistentVector v (cast clojure.lang.IPersistentVector o)
             vcnt (.count v)]
         (cond
           (< cnt vcnt) -1
diff --git a/src/clj/clojure/inspector.clj b/src/clj/clojure/inspector.clj
index aa708b5..85e79c0 100644
--- a/src/clj/clojure/inspector.clj
+++ b/src/clj/clojure/inspector.clj
@@ -22,8 +22,10 @@
 (defn collection-tag [x]
   (cond 
    (instance? java.util.Map$Entry x) :entry
-   (instance? java.util.Map x) :map
+   (instance? java.util.Map x) :seqable
+   (instance? java.util.Set x) :seqable
    (sequential? x) :seq
+   (instance? clojure.lang.Seqable x) :seqable
    :else :atom))
 
 (defmulti is-leaf collection-tag)
@@ -44,10 +46,12 @@
 (defmethod get-child-count :entry [e]
   (count (val e)))
 
-(defmethod is-leaf :map [m]
+(defmethod is-leaf :seqable [parent]
   false)
-(defmethod get-child :map [m index]
-  (nth (seq m) index))
+(defmethod get-child :seqable [parent index]
+  (nth (seq parent) index))
+(defmethod get-child-count :seqable [parent]
+  (count (seq parent)))
 
 (defn tree-model [data]
   (proxy [TreeModel] []
diff --git a/src/clj/clojure/instant.clj b/src/clj/clojure/instant.clj
index 1366f7c..c5274a7 100644
--- a/src/clj/clojure/instant.clj
+++ b/src/clj/clojure/instant.clj
@@ -135,7 +135,7 @@ specified.
 
 (defn validated
   "Return a function which constructs and instant by calling constructor
-after first validting that those arguments are in range and otherwise
+after first validating that those arguments are in range and otherwise
 plausible. The resulting function will throw an exception if called
 with invalid arguments."
   [new-instance]
@@ -235,7 +235,7 @@ with invalid arguments."
 ;;; reader integration
 
 (defn- construct-calendar
-  "Construct a java.util.Calendar, which preserves, preserving the timezone
+  "Construct a java.util.Calendar, preserving the timezone
 offset, but truncating the subsecond fraction to milliseconds."
   ^GregorianCalendar
   [years months days hours minutes seconds nanoseconds
@@ -249,7 +249,7 @@ offset, but truncating the subsecond fraction to milliseconds."
 
 (defn- construct-date
   "Construct a java.util.Date, which expresses the original instant as
-milliseconds since the epoch, GMT."
+milliseconds since the epoch, UTC."
   [years months days hours minutes seconds nanoseconds
    offset-sign offset-hours offset-minutes]
   (.getTime (construct-calendar years months days
diff --git a/src/clj/clojure/java/browse.clj b/src/clj/clojure/java/browse.clj
index 1acda37..6fcc650 100644
--- a/src/clj/clojure/java/browse.clj
+++ b/src/clj/clojure/java/browse.clj
@@ -10,14 +10,34 @@
   ^{:author "Christophe Grand",
     :doc "Start a web browser from Clojure"}
   clojure.java.browse
-  (:require [clojure.java.shell :as sh]) 
+  (:require [clojure.java.shell :as sh]
+            [clojure.string :as str])
   (:import (java.net URI)))
 
 (defn- macosx? []
   (-> "os.name" System/getProperty .toLowerCase
     (.startsWith "mac os x")))
 
-(def ^:dynamic *open-url-script* (when (macosx?) "/usr/bin/open"))
+(defn- xdg-open-loc []
+  ;; try/catch needed to mask exception on Windows without Cygwin
+  (let [which-out (try (:out (sh/sh "which" "xdg-open"))
+                       (catch Exception e ""))]
+    (if (= which-out "")
+      nil
+      (str/trim-newline which-out))))
+
+(defn- open-url-script-val []
+  (if (macosx?)
+    "/usr/bin/open"
+    (xdg-open-loc)))
+
+;; We could assign (open-url-script-val) to *open-url-script* right
+;; away in the def below, but clojure.java.shell/sh creates a future
+;; that causes a long wait for the JVM to exit during Clojure compiles
+;; (unless we can somehow here make it call (shutdown-agents) later).
+;; Better to initialize it when we first need it, in browse-url.
+
+(def ^:dynamic *open-url-script* (atom :uninitialized))
 
 (defn- open-url-in-browser
   "Opens url (a string) in the default system web browser.  May not
@@ -47,6 +67,10 @@
   "Open url in a browser"
   {:added "1.2"}
   [url]
-  (or (open-url-in-browser url)
-      (when *open-url-script* (sh/sh *open-url-script* (str url)) true)
-      (open-url-in-swing url)))
+  (let [script @*open-url-script*
+        script (if (= :uninitialized script)
+                 (reset! *open-url-script* (open-url-script-val))
+                 script)]
+    (or (when script (sh/sh script (str url)) true)
+        (open-url-in-browser url)
+        (open-url-in-swing url))))
diff --git a/src/clj/clojure/java/browse_ui.clj b/src/clj/clojure/java/browse_ui.clj
index e2c858d..bf3d7e0 100644
--- a/src/clj/clojure/java/browse_ui.clj
+++ b/src/clj/clojure/java/browse_ui.clj
@@ -18,7 +18,7 @@
     (.setEditable htmlpane false)
     (.addHyperlinkListener htmlpane
       (proxy [javax.swing.event.HyperlinkListener] []
-        (hyperlinkUpdate [#^javax.swing.event.HyperlinkEvent e]
+        (hyperlinkUpdate [^javax.swing.event.HyperlinkEvent e]
           (when (= (.getEventType e) (. javax.swing.event.HyperlinkEvent$EventType ACTIVATED))
             (if (instance? javax.swing.text.html.HTMLFrameHyperlinkEvent e)
               (-> htmlpane .getDocument (.processHTMLFrameHyperlinkEvent e))
diff --git a/src/clj/clojure/java/io.clj b/src/clj/clojure/java/io.clj
index 20553df..f2a4ec4 100644
--- a/src/clj/clojure/java/io.clj
+++ b/src/clj/clojure/java/io.clj
@@ -19,7 +19,7 @@
               StringReader ByteArrayInputStream
               BufferedInputStream BufferedOutputStream
               CharArrayReader Closeable)
-     (java.net URI URL MalformedURLException Socket)))
+     (java.net URI URL MalformedURLException Socket URLDecoder URLEncoder)))
 
 (def
     ^{:doc "Type object for a Java primitive byte array."
@@ -37,6 +37,10 @@
   (^{:tag java.io.File, :added "1.2"} as-file [x] "Coerce argument to a file.")
   (^{:tag java.net.URL, :added "1.2"} as-url [x] "Coerce argument to a URL."))
 
+(defn- escaped-utf8-urlstring->str [s]
+  (-> (clojure.string/replace s "+" (URLEncoder/encode "+" "UTF-8"))
+      (URLDecoder/decode "UTF-8")))
+
 (extend-protocol Coercions
   nil
   (as-file [_] nil)
@@ -54,16 +58,8 @@
   (as-url [u] u)
   (as-file [u]
     (if (= "file" (.getProtocol u))
-      (as-file
-        (clojure.string/replace
-          (.replace (.getFile u) \/ File/separatorChar)
-          #"%.."
-          (fn [escape]
-            (-> escape
-                (.substring 1 3)
-                (Integer/parseInt 16)
-                (char)
-                (str)))))
+      (as-file (escaped-utf8-urlstring->str
+                (.replace (.getFile u) \/ File/separatorChar)))
       (throw (IllegalArgumentException. (str "Not a file: " u)))))
 
   URI
@@ -287,13 +283,13 @@
   default-streams-impl)
 
 (defmulti
-  #^{:doc "Internal helper for copy"
+  ^{:doc "Internal helper for copy"
      :private true
      :arglists '([input output opts])}
   do-copy
   (fn [input output opts] [(type input) (type output)]))
 
-(defmethod do-copy [InputStream OutputStream] [#^InputStream input #^OutputStream output opts]
+(defmethod do-copy [InputStream OutputStream] [^InputStream input ^OutputStream output opts]
   (let [buffer (make-array Byte/TYPE (buffer-size opts))]
     (loop []
       (let [size (.read input buffer)]
@@ -301,8 +297,8 @@
           (do (.write output buffer 0 size)
               (recur)))))))
 
-(defmethod do-copy [InputStream Writer] [#^InputStream input #^Writer output opts]
-  (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))
+(defmethod do-copy [InputStream Writer] [^InputStream input ^Writer output opts]
+  (let [^"[C" buffer (make-array Character/TYPE (buffer-size opts))
         in (InputStreamReader. input (encoding opts))]
     (loop []
       (let [size (.read in buffer 0 (alength buffer))]
@@ -310,12 +306,12 @@
           (do (.write output buffer 0 size)
               (recur)))))))
 
-(defmethod do-copy [InputStream File] [#^InputStream input #^File output opts]
+(defmethod do-copy [InputStream File] [^InputStream input ^File output opts]
   (with-open [out (FileOutputStream. output)]
     (do-copy input out opts)))
 
-(defmethod do-copy [Reader OutputStream] [#^Reader input #^OutputStream output opts]
-  (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))
+(defmethod do-copy [Reader OutputStream] [^Reader input ^OutputStream output opts]
+  (let [^"[C" buffer (make-array Character/TYPE (buffer-size opts))
         out (OutputStreamWriter. output (encoding opts))]
     (loop []
       (let [size (.read input buffer)]
@@ -325,56 +321,61 @@
             (recur))
           (.flush out))))))
 
-(defmethod do-copy [Reader Writer] [#^Reader input #^Writer output opts]
-  (let [#^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
+(defmethod do-copy [Reader Writer] [^Reader input ^Writer output opts]
+  (let [^"[C" buffer (make-array Character/TYPE (buffer-size opts))]
     (loop []
       (let [size (.read input buffer)]
         (when (pos? size)
           (do (.write output buffer 0 size)
               (recur)))))))
 
-(defmethod do-copy [Reader File] [#^Reader input #^File output opts]
+(defmethod do-copy [Reader File] [^Reader input ^File output opts]
   (with-open [out (FileOutputStream. output)]
     (do-copy input out opts)))
 
-(defmethod do-copy [File OutputStream] [#^File input #^OutputStream output opts]
+(defmethod do-copy [File OutputStream] [^File input ^OutputStream output opts]
   (with-open [in (FileInputStream. input)]
     (do-copy in output opts)))
 
-(defmethod do-copy [File Writer] [#^File input #^Writer output opts]
+(defmethod do-copy [File Writer] [^File input ^Writer output opts]
   (with-open [in (FileInputStream. input)]
     (do-copy in output opts)))
 
-(defmethod do-copy [File File] [#^File input #^File output opts]
-  (with-open [in (FileInputStream. input)
-              out (FileOutputStream. output)]
-    (do-copy in out opts)))
-
-(defmethod do-copy [String OutputStream] [#^String input #^OutputStream output opts]
+(defmethod do-copy [File File] [^File input ^File output opts]
+  (with-open [in (-> input FileInputStream. .getChannel)
+              out (-> output FileOutputStream. .getChannel)]
+    (let [sz (.size in)]
+      (loop [pos 0]
+        (let [bytes-xferred (.transferTo in pos (- sz pos) out)
+              pos (+ pos bytes-xferred)]
+          (when (< pos sz)
+            (recur pos)))))))
+
+(defmethod do-copy [String OutputStream] [^String input ^OutputStream output opts]
   (do-copy (StringReader. input) output opts))
 
-(defmethod do-copy [String Writer] [#^String input #^Writer output opts]
+(defmethod do-copy [String Writer] [^String input ^Writer output opts]
   (do-copy (StringReader. input) output opts))
 
-(defmethod do-copy [String File] [#^String input #^File output opts]
+(defmethod do-copy [String File] [^String input ^File output opts]
   (do-copy (StringReader. input) output opts))
 
-(defmethod do-copy [char-array-type OutputStream] [input #^OutputStream output opts]
+(defmethod do-copy [char-array-type OutputStream] [input ^OutputStream output opts]
   (do-copy (CharArrayReader. input) output opts))
 
-(defmethod do-copy [char-array-type Writer] [input #^Writer output opts]
+(defmethod do-copy [char-array-type Writer] [input ^Writer output opts]
   (do-copy (CharArrayReader. input) output opts))
 
-(defmethod do-copy [char-array-type File] [input #^File output opts]
+(defmethod do-copy [char-array-type File] [input ^File output opts]
   (do-copy (CharArrayReader. input) output opts))
 
-(defmethod do-copy [byte-array-type OutputStream] [#^"[B" input #^OutputStream output opts]
+(defmethod do-copy [byte-array-type OutputStream] [^"[B" input ^OutputStream output opts]
   (do-copy (ByteArrayInputStream. input) output opts))
 
-(defmethod do-copy [byte-array-type Writer] [#^"[B" input #^Writer output opts]
+(defmethod do-copy [byte-array-type Writer] [^"[B" input ^Writer output opts]
   (do-copy (ByteArrayInputStream. input) output opts))
 
-(defmethod do-copy [byte-array-type File] [#^"[B" input #^Writer output opts]
+(defmethod do-copy [byte-array-type File] [^"[B" input ^Writer output opts]
   (do-copy (ByteArrayInputStream. input) output opts))
 
 (defn copy
diff --git a/src/clj/clojure/java/javadoc.clj b/src/clj/clojure/java/javadoc.clj
index afcd08c..3652709 100644
--- a/src/clj/clojure/java/javadoc.clj
+++ b/src/clj/clojure/java/javadoc.clj
@@ -20,7 +20,6 @@
  
 (def ^:dynamic *core-java-api*
   (case (System/getProperty "java.specification.version")
-    "1.5" "http://java.sun.com/j2se/1.5.0/docs/api/"
     "1.6" "http://java.sun.com/javase/6/docs/api/"
     "http://java.sun.com/javase/7/docs/api/"))
 
diff --git a/src/clj/clojure/main.clj b/src/clj/clojure/main.clj
index 487d0fd..fea693d 100644
--- a/src/clj/clojure/main.clj
+++ b/src/clj/clojure/main.clj
@@ -27,33 +27,12 @@
   [^StackTraceElement el]
   (.getClassName el))
 
-(def ^:private demunge-map
-  (into {"$" "/"} (map (fn [[k v]] [v k]) clojure.lang.Compiler/CHAR_MAP)))
-
-(def ^:private demunge-pattern
-  (re-pattern (apply str (interpose "|" (map #(str "\\Q" % "\\E")
-                                             (keys demunge-map))))))
-
-(defn- re-replace [re s f]
-  (let [m (re-matcher re s)
-        mseq (take-while identity
-                         (repeatedly #(when (re-find m)
-                                        [(re-groups m) (.start m) (.end m)])))]
-    (apply str
-           (concat
-             (mapcat (fn [[_ _ start] [groups end]]
-                       (if end
-                         [(subs s start end) (f groups)]
-                         [(subs s start)]))
-                     (cons [0 0 0] mseq)
-                     (concat mseq [nil]))))))
-
 (defn demunge
   "Given a string representation of a fn class,
   as in a stack trace element, returns a readable version."
   {:added "1.3"}
   [fn-name]
-  (re-replace demunge-pattern fn-name demunge-map))
+  (clojure.lang.Compiler/demunge fn-name))
 
 (defn root-cause
   "Returns the initial cause of an exception or error by peeling off all of
@@ -95,6 +74,7 @@
              *print-length* *print-length*
              *print-level* *print-level*
              *data-readers* *data-readers*
+             *default-data-reader-fn* *default-data-reader-fn*
              *compile-path* (System/getProperty "clojure.compile.path" "classes")
              *command-line-args* *command-line-args*
              *unchecked-math* *unchecked-math*
@@ -177,6 +157,19 @@
                     (when-not (instance? clojure.lang.Compiler$CompilerException ex)
                       (str " " (if el (stack-element-str el) "[trace missing]"))))))))
 
+(def ^{:doc "A sequence of lib specs that are applied to `require`
+by default when a new command-line REPL is started."} repl-requires
+  '[[clojure.repl :refer (source apropos dir pst doc find-doc)]
+    [clojure.java.javadoc :refer (javadoc)]
+    [clojure.pprint :refer (pp pprint)]])
+
+(defmacro with-read-known
+  "Evaluates body with *read-eval* set to a \"known\" value,
+   i.e. substituting true for :unknown if necessary."
+  [& body]
+  `(binding [*read-eval* (if (= :unknown *read-eval*) true *read-eval*)]
+     ~@body))
+
 (defn repl
   "Generic, reusable, read-eval-print loop. By default, reads from *in*,
   writes to *out*, and prints exception summaries to *err*. If you use the
@@ -210,7 +203,7 @@
          - else returns the next object read from the input stream
        default: repl-read
 
-     - :eval, funtion of one argument, returns the evaluation of its
+     - :eval, function of one argument, returns the evaluation of its
        argument
        default: eval
 
@@ -240,9 +233,10 @@
         read-eval-print
         (fn []
           (try
-           (let [input (read request-prompt request-exit)]
+            (let [read-eval *read-eval*
+                  input (with-read-known (read request-prompt request-exit))]
              (or (#{request-prompt request-exit} input)
-                 (let [value (eval input)]
+                 (let [value (binding [*read-eval* read-eval] (eval input))]
                    (print value)
                    (set! *3 *2)
                    (set! *2 *1)
@@ -256,9 +250,6 @@
       (catch Throwable e
         (caught e)
         (set! *e e)))
-     (use '[clojure.repl :only (source apropos dir pst doc find-doc)])
-     (use '[clojure.java.javadoc :only (javadoc)])
-     (use '[clojure.pprint :only (pp pprint)])
      (prompt)
      (flush)
      (loop []
@@ -292,12 +283,12 @@
   [str]
   (let [eof (Object.)
         reader (LineNumberingPushbackReader. (java.io.StringReader. str))]
-      (loop [input (read reader false eof)]
+      (loop [input (with-read-known (read reader false eof))]
         (when-not (= input eof)
           (let [value (eval input)]
             (when-not (nil? value)
               (prn value))
-            (recur (read reader false eof)))))))
+            (recur (with-read-known (read reader false eof))))))))
 
 (defn- init-dispatch
   "Returns the handler associated with an init opt"
@@ -329,7 +320,9 @@
   [[_ & args] inits]
   (when-not (some #(= eval-opt (init-dispatch (first %))) inits)
     (println "Clojure" (clojure-version)))
-  (repl :init #(initialize args inits))
+  (repl :init (fn []
+                (initialize args inits)
+                (apply require repl-requires)))
   (prn)
   (System/exit 0))
 
diff --git a/src/clj/clojure/pprint/cl_format.clj b/src/clj/clojure/pprint/cl_format.clj
index ef5c981..a8308c0 100644
--- a/src/clj/clojure/pprint/cl_format.clj
+++ b/src/clj/clojure/pprint/cl_format.clj
@@ -543,13 +543,15 @@ Note this should only be used for the last one in the sequence"
   "Produce string parts for the mantissa (normalized 1-9) and exponent"
   [^Object f]
   (let [^String s (.toLowerCase (.toString f))
-        exploc (.indexOf s (int \e))]
+        exploc (.indexOf s (int \e))
+        dotloc (.indexOf s (int \.))]
     (if (neg? exploc)
-      (let [dotloc (.indexOf s (int \.))]
-        (if (neg? dotloc)
-          [s (str (dec (count s)))]
-          [(str (subs s 0 dotloc) (subs s (inc dotloc))) (str (dec dotloc))]))
-      [(str (subs s 0 1) (subs s 2 exploc)) (subs s (inc exploc))])))
+      (if (neg? dotloc)
+        [s (str (dec (count s)))]
+        [(str (subs s 0 dotloc) (subs s (inc dotloc))) (str (dec dotloc))])
+      (if (neg? dotloc)
+        [(subs s 0 exploc) (subs s (inc exploc))]
+        [(str (subs s 0 1) (subs s 2 exploc)) (subs s (inc exploc))]))))
 
 
 (defn- float-parts
@@ -564,14 +566,43 @@ Note this should only be used for the last one in the sequence"
       ["0" 0]
       [m2 (- (Integer/valueOf e) delta)])))
 
+(defn- ^String inc-s
+  "Assumption: The input string consists of one or more decimal digits,
+and no other characters.  Return a string containing one or more
+decimal digits containing a decimal number one larger than the input
+string.  The output string will always be the same length as the input
+string, or one character longer."
+  [^String s]
+  (let [len-1 (dec (count s))]
+    (loop [i (int len-1)]
+      (cond
+       (neg? i) (apply str "1" (repeat (inc len-1) "0"))
+       (= \9 (.charAt s i)) (recur (dec i))
+       :else (apply str (subs s 0 i)
+                    (char (inc (int (.charAt s i))))
+                    (repeat (- len-1 i) "0"))))))
+
 (defn- round-str [m e d w]
   (if (or d w)
     (let [len (count m)
-          round-pos (if d (+ e d 1))
-          round-pos (if (and w (< (inc e) (dec w)) 
-                             (or (nil? round-pos) (< (dec w) round-pos)))
-                      (dec w)
-                      round-pos)
+          ;; Every formatted floating point number should include at
+          ;; least one decimal digit and a decimal point.
+          w (if w (max 2 w))
+          round-pos (cond
+                     ;; If d was given, that forces the rounding
+                     ;; position, regardless of any width that may
+                     ;; have been specified.
+                     d (+ e d 1)
+                     ;; Otherwise w was specified, so pick round-pos
+                     ;; based upon that.
+                     ;; If e>=0, then abs value of number is >= 1.0,
+                     ;; and e+1 is number of decimal digits before the
+                     ;; decimal point when the number is written
+                     ;; without scientific notation.  Never round the
+                     ;; number before the decimal point.
+                     (>= e 0) (max (inc e) (dec w))
+                     ;; e < 0, so number abs value < 1.0
+                     :else (+ w e))
           [m1 e1 round-pos len] (if (= round-pos 0) 
                                   [(str "0" m) (inc e) 1 (inc len)]
                                   [m e round-pos len])]
@@ -582,22 +613,23 @@ Note this should only be used for the last one in the sequence"
             (let [round-char (nth m1 round-pos)
                   ^String result (subs m1 0 round-pos)]
               (if (>= (int round-char) (int \5))
-                (let [result-val (Integer/valueOf result)
-                      leading-zeros (subs result 0 (min (prefix-count result \0) (- round-pos 1)))
-                      round-up-result (str leading-zeros
-                                           (String/valueOf (+ result-val 
-                                                              (if (neg? result-val) -1 1))))
+                (let [round-up-result (inc-s result)
                       expanded (> (count round-up-result) (count result))]
-                  [round-up-result e1 expanded])
+                  [(if expanded
+                     (subs round-up-result 0 (dec (count round-up-result)))
+                     round-up-result)
+                   e1 expanded])
                 [result e1 false]))
             [m e false]))
         [m e false]))
     [m e false]))
 
 (defn- expand-fixed [m e d]
-  (let [m1 (if (neg? e) (str (apply str (repeat (dec (- e)) \0)) m) m)
+  (let [[m1 e1] (if (neg? e)
+                  [(str (apply str (repeat (dec (- e)) \0)) m) -1]
+                  [m e])
         len (count m1)
-        target-len (if d (+ e d 1) (inc e))]
+        target-len (if d (+ e1 d 1) (inc e1))]
     (if (< len target-len) 
       (str m1 (apply str (repeat (- target-len len) \0))) 
       m1)))
@@ -620,6 +652,21 @@ Note this should only be used for the last one in the sequence"
     (str "." m)
     (str (subs m 0 k) "." (subs m k))))
 
+(defn- convert-ratio [x]
+  (if (ratio? x)
+    ;; Usually convert to a double, only resorting to the slower
+    ;; bigdec conversion if the result does not fit within the range
+    ;; of a double.
+    (let [d (double x)]
+      (if (== d 0.0)
+        (if (not= x 0)
+          (bigdec x)
+          d)
+        (if (or (== d Double/POSITIVE_INFINITY) (== d Double/NEGATIVE_INFINITY))
+          (bigdec x)
+          d)))
+    x))
+
 ;; the function to render ~F directives
 ;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
 (defn- fixed-float [params navigator offsets]
@@ -627,6 +674,7 @@ Note this should only be used for the last one in the sequence"
         d (:d params)
         [arg navigator] (next-arg navigator)
         [sign abs] (if (neg? arg) ["-" (- arg)] ["+" arg])
+        abs (convert-ratio abs)
         [mantissa exp] (float-parts abs)
         scaled-exp (+ exp (:k params))
         add-sign (or (:at params) (neg? arg))
@@ -634,6 +682,13 @@ Note this should only be used for the last one in the sequence"
         [rounded-mantissa scaled-exp expanded] (round-str mantissa scaled-exp 
                                                           d (if w (- w (if add-sign 1 0))))
         fixed-repr (get-fixed rounded-mantissa (if expanded (inc scaled-exp) scaled-exp) d)
+        fixed-repr (if (and w d
+                            (>= d 1)
+                            (= (.charAt fixed-repr 0) \0)
+                            (= (.charAt fixed-repr 1) \.)
+                            (> (count fixed-repr) (- w (if add-sign 1 0))))
+                     (subs fixed-repr 1)  ; chop off leading 0
+                     fixed-repr)
         prepend-zero (= (first fixed-repr) \.)]
     (if w
       (let [len (count fixed-repr)
@@ -663,7 +718,8 @@ Note this should only be used for the last one in the sequence"
 ;; TODO: support rationals. Back off to ~D/~A is the appropriate cases
 ;; TODO: define ~E representation for Infinity
 (defn- exponential-float [params navigator offsets]
-  (let [[arg navigator] (next-arg navigator)]
+  (let [[arg navigator] (next-arg navigator)
+        arg (convert-ratio arg)]
     (loop [[mantissa exp] (float-parts (if (neg? arg) (- arg) arg))]
       (let [w (:w params)
             d (:d params)
@@ -737,6 +793,7 @@ Note this should only be used for the last one in the sequence"
 ;; TODO: refactor so that float-parts isn't called twice
 (defn- general-float [params navigator offsets]
   (let [[arg _] (next-arg navigator)
+        arg (convert-ratio arg)
         [mantissa exp] (float-parts (if (neg? arg) (- arg) arg))
         w (:w params)
         d (:d params)
@@ -1073,7 +1130,7 @@ Note this should only be used for the last one in the sequence"
              s)))))
 
 (defn- capitalize-word-writer
-  "Returns a proxy that wraps writer, captializing all words"
+  "Returns a proxy that wraps writer, capitalizing all words"
   [^java.io.Writer writer]
   (let [last-was-whitespace? (ref true)] 
     (proxy [java.io.Writer] []
@@ -1145,7 +1202,7 @@ Note this should only be used for the last one in the sequence"
 
 (defn get-pretty-writer 
   "Returns the java.io.Writer passed in wrapped in a pretty writer proxy, unless it's 
-already a pretty writer. Generally, it is unneccesary to call this function, since pprint,
+already a pretty writer. Generally, it is unnecessary to call this function, since pprint,
 write, and cl-format all call it if they need to. However if you want the state to be 
 preserved across calls, you will want to wrap them with this. 
 
diff --git a/src/clj/clojure/pprint/column_writer.clj b/src/clj/clojure/pprint/column_writer.clj
index 0449d43..7e75445 100644
--- a/src/clj/clojure/pprint/column_writer.clj
+++ b/src/clj/clojure/pprint/column_writer.clj
@@ -58,6 +58,8 @@
      (let [fields (ref {:max max-columns, :cur 0, :line 0 :base writer})]
        (proxy [Writer IDeref] []
          (deref [] fields)
+         (flush []
+           (.flush writer))
          (write
           ([^chars cbuf ^Integer off ^Integer len] 
              (let [^Writer writer (get-field this :base)] 
diff --git a/src/clj/clojure/pprint/dispatch.clj b/src/clj/clojure/pprint/dispatch.clj
index 68d6d8a..105a89d 100644
--- a/src/clj/clojure/pprint/dispatch.clj
+++ b/src/clj/clojure/pprint/dispatch.clj
@@ -29,7 +29,7 @@
 ;;; Handle forms that can be "back-translated" to reader macros
 ;;; Not all reader macros can be dealt with this way or at all. 
 ;;; Macros that we can't deal with at all are:
-;;; ;  - The comment character is aborbed by the reader and never is part of the form
+;;; ;  - The comment character is absorbed by the reader and never is part of the form
 ;;; `  - Is fully processed at read time into a lisp expression (which will contain concats
 ;;;      and regular quotes).
 ;;; ~@ - Also fully eaten by the processing of ` and can't be used outside.
@@ -166,6 +166,79 @@
 (declare pprint-simple-code-list)
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;; Format the namespace ("ns") macro. This is quite complicated because of all the
+;;; different forms supported and because programmers can choose lists or vectors
+;;; in various places.
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defn- brackets
+  "Figure out which kind of brackets to use"
+  [form]
+  (if (vector? form)
+    ["[" "]"]
+    ["(" ")"]))
+
+(defn- pprint-ns-reference
+  "Pretty print a single reference (import, use, etc.) from a namespace decl"
+  [reference]
+  (if (sequential? reference)
+    (let [[start end] (brackets reference)
+          [keyw & args] reference]
+      (pprint-logical-block :prefix start :suffix end
+        ((formatter-out "~w~:i") keyw)
+        (loop [args args]
+          (when (seq args)
+            ((formatter-out " "))
+            (let [arg (first args)]
+              (if (sequential? arg)
+                (let [[start end] (brackets arg)]
+                  (pprint-logical-block :prefix start :suffix end
+                    (if (and (= (count arg) 3) (keyword? (second arg)))
+                      (let [[ns kw lis] arg]
+                        ((formatter-out "~w ~w ") ns kw)
+                        (if (sequential? lis)
+                          ((formatter-out (if (vector? lis)
+                                            "~<[~;~@{~w~^ ~:_~}~;]~:>"
+                                            "~<(~;~@{~w~^ ~:_~}~;)~:>"))
+                           lis)
+                          (write-out lis)))
+                      (apply (formatter-out "~w ~:i~@{~w~^ ~:_~}") arg)))
+                  (when (next args)
+                    ((formatter-out "~_"))))
+                (do
+                  (write-out arg)
+                  (when (next args)
+                    ((formatter-out "~:_"))))))
+            (recur (next args))))))
+    (write-out reference)))
+
+(defn- pprint-ns
+  "The pretty print dispatch chunk for the ns macro"
+  [alis]
+  (if (next alis) 
+    (let [[ns-sym ns-name & stuff] alis
+          [doc-str stuff] (if (string? (first stuff))
+                            [(first stuff) (next stuff)]
+                            [nil stuff])
+          [attr-map references] (if (map? (first stuff))
+                                  [(first stuff) (next stuff)]
+                                  [nil stuff])]
+      (pprint-logical-block :prefix "(" :suffix ")"
+        ((formatter-out "~w ~1I~@_~w") ns-sym ns-name)
+        (when (or doc-str attr-map (seq references))
+          ((formatter-out "~@:_")))
+        (when doc-str
+          (cl-format true "\"~a\"~:[~;~:@_~]" doc-str (or attr-map (seq references))))
+        (when attr-map
+          ((formatter-out "~w~:[~;~:@_~]") attr-map (seq references)))
+        (loop [references references]
+          (pprint-ns-reference (first references))
+          (when-let [references (next references)]
+            (pprint-newline :linear)
+            (recur references)))))
+    (write-out alis)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; Format something that looks like a simple def (sans metadata, since the reader
 ;;; won't give it to us now).
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -356,7 +429,7 @@
         'fn* pprint-anon-func,
         '. pprint-hold-first, '.. pprint-hold-first, '-> pprint-hold-first,
         'locking pprint-hold-first, 'struct pprint-hold-first,
-        'struct-map pprint-hold-first, 
+        'struct-map pprint-hold-first, 'ns pprint-ns 
         })))
 
 (defn- pprint-code-list [alis]
diff --git a/src/clj/clojure/pprint/pprint_base.clj b/src/clj/clojure/pprint/pprint_base.clj
index ea035d4..2d450d8 100644
--- a/src/clj/clojure/pprint/pprint_base.clj
+++ b/src/clj/clojure/pprint/pprint_base.clj
@@ -164,7 +164,7 @@ radix specifier is in the form #XXr where XX is the decimal value of *print-base
                       (make-pretty-writer base-writer# *print-right-margin* *print-miser-width*)
                       base-writer#)]
        ~@body
-       (.flush *out*))))
+       (.ppflush *out*))))
 
 
 ;;;TODO: if pretty print is not set, don't use pr but rather something that respects *print-base*, etc.
diff --git a/src/clj/clojure/pprint/pretty_writer.clj b/src/clj/clojure/pprint/pretty_writer.clj
index 8b6aba4..c31142c 100644
--- a/src/clj/clojure/pprint/pretty_writer.clj
+++ b/src/clj/clojure/pprint/pretty_writer.clj
@@ -380,7 +380,7 @@
                      :miser-width miser-width
                      :trailing-white-space nil
                      :pos 0})]
-    (proxy [Writer IDeref] []
+    (proxy [Writer IDeref PrettyFlush] []
       (deref [] fields)
 
       (write 
@@ -408,13 +408,17 @@
             Long
             (p-write-char this x))))
 
-      (flush []
+      (ppflush []
              (if (= (getf :mode) :buffering)
-               (dosync 
+               (dosync
                 (write-tokens this (getf :buffer) true)
                 (setf :buffer []))
                (write-white-space this)))
 
+      (flush []
+             (.ppflush this)
+             (.flush (getf :base)))
+
       (close []
              (.flush this)))))
 
diff --git a/src/clj/clojure/pprint/print_table.clj b/src/clj/clojure/pprint/print_table.clj
index f285b8c..337f45d 100644
--- a/src/clj/clojure/pprint/print_table.clj
+++ b/src/clj/clojure/pprint/print_table.clj
@@ -9,8 +9,7 @@
 (in-ns 'clojure.pprint)
 
 (defn print-table
-  "Alpha - subject to change.
-   Prints a collection of maps in a textual table. Prints table headings
+  "Prints a collection of maps in a textual table. Prints table headings
    ks, and then a line of output for each row, corresponding to the keys
    in ks. If ks are not specified, use the keys of the first item in rows."
   {:added "1.3"}
@@ -20,17 +19,17 @@
                      (fn [k]
                        (apply max (count (str k)) (map #(count (str (get % k))) rows)))
                      ks)
-             fmts (map #(str "%-" % "s") widths)
-             fmt-row (fn [row]
-                       (apply str (interpose " | "
-                                             (for [[col fmt] (map vector (map #(get row %) ks) fmts)]
-                                               (format fmt (str col))))))
-             header (fmt-row (zipmap ks ks))
-             bar (apply str (repeat (count header) "="))]
-         (println bar)
-         (println header)
-         (println bar)
+             spacers (map #(apply str (repeat % "-")) widths)
+             fmts (map #(str "%" % "s") widths)
+             fmt-row (fn [leader divider trailer row]
+                       (str leader
+                            (apply str (interpose divider
+                                                  (for [[col fmt] (map vector (map #(get row %) ks) fmts)]
+                                                    (format fmt (str col)))))
+                            trailer))]
+         (println)
+         (println (fmt-row "| " " | " " |" (zipmap ks ks)))
+         (println (fmt-row "|-" "-+-" "-|" (zipmap ks spacers)))
          (doseq [row rows]
-           (println (fmt-row row)))
-         (println bar))))
+           (println (fmt-row "| " " | " " |" row))))))
   ([rows] (print-table (keys (first rows)) rows)))
diff --git a/src/clj/clojure/pprint/utilities.clj b/src/clj/clojure/pprint/utilities.clj
index 0385fd3..53c4e97 100644
--- a/src/clj/clojure/pprint/utilities.clj
+++ b/src/clj/clojure/pprint/utilities.clj
@@ -102,3 +102,6 @@ beginning of aseq"
   `(prerr ~@(cons (list 'quote prefix) (mapcat #(list (list 'quote %) "=" %) 
                                                   (cons arg (seq more-args))))))
 
+;; Flush the pretty-print buffer without flushing the underlying stream
+(definterface PrettyFlush
+  (^void ppflush []))
diff --git a/src/clj/clojure/reflect/java.clj b/src/clj/clojure/reflect/java.clj
index 9a03089..1348244 100644
--- a/src/clj/clojure/reflect/java.clj
+++ b/src/clj/clojure/reflect/java.clj
@@ -11,7 +11,7 @@
 
 (require '[clojure.set :as set]
          '[clojure.string :as str])
-(import '[clojure.asm ClassReader ClassVisitor Type]
+(import '[clojure.asm ClassReader ClassVisitor Type Opcodes]
          '[java.lang.reflect Modifier]
          java.io.InputStream)
 
@@ -202,9 +202,10 @@ the kinds of objects to which they can apply."}
             result (atom {:bases #{} :flags #{} :members #{}})]
         (.accept
          r
-         (reify
-          ClassVisitor
-          (visit [_ version access name signature superName interfaces]
+         (proxy
+          [ClassVisitor]
+          [Opcodes/ASM4]
+          (visit [version access name signature superName interfaces]
                  (let [flags (parse-flags access :class)
                        ;; ignore java.lang.Object on interfaces to match reflection
                        superName (if (and (flags :interface)
@@ -219,16 +220,17 @@ the kinds of objects to which they can apply."}
                                   (not-empty))]
                    (swap! result merge {:bases bases 
                                         :flags flags})))
-          (visitSource [_ name debug])
-          (visitInnerClass [_ name outerName innerName access])
-          (visitField [_ access name desc signature value]
+          (visitAnnotation [desc visible])
+          (visitSource [name debug])
+          (visitInnerClass [name outerName innerName access])
+          (visitField [access name desc signature value]
                       (swap! result update-in [:members] (fnil conj #{})
                              (Field. (symbol name)
                                      (field-descriptor->class-symbol desc)
                                      class-symbol
                                      (parse-flags access :field)))
                       nil)
-          (visitMethod [_ access name desc signature exceptions]
+          (visitMethod [access name desc signature exceptions]
                        (when-not (= name "<clinit>")
                          (let [constructor? (= name "<init>")]
                            (swap! result update-in [:members] (fnil conj #{})
@@ -247,7 +249,7 @@ the kinds of objects to which they can apply."}
                                                (vec (map internal-name->class-symbol exceptions))
                                                flags))))))
                        nil)
-          (visitEnd [_])
+          (visitEnd [])
           ) 0)
         @result))))
 
diff --git a/src/clj/clojure/repl.clj b/src/clj/clojure/repl.clj
index e6ea66f..761b84f 100644
--- a/src/clj/clojure/repl.clj
+++ b/src/clj/clojure/repl.clj
@@ -9,8 +9,8 @@
 ; Utilities meant to be used interactively at the REPL
 
 (ns
-  #^{:author "Chris Houser, Christophe Grand, Stephen Gilardi, Michel Salim"
-     :doc "Utilities meant to be used interactively at the REPL"}
+  ^{:author "Chris Houser, Christophe Grand, Stephen Gilardi, Michel Salim"
+    :doc "Utilities meant to be used interactively at the REPL"}
   clojure.repl
   (:import (java.io LineNumberReader InputStreamReader PushbackReader)
            (clojure.lang RT Reflector)))
@@ -125,8 +125,8 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}})
     (#'print-doc (#'special-doc special-name))
     (cond
       (special-doc-map name) `(#'print-doc (#'special-doc '~name))
-      (resolve name) `(#'print-doc (meta (var ~name)))
-      (find-ns name) `(#'print-doc (namespace-doc (find-ns '~name))))))
+      (find-ns name) `(#'print-doc (#'namespace-doc (find-ns '~name)))
+      (resolve name) `(#'print-doc (meta (var ~name))))))
 
 ;; ----------------------------------------------------------------------
 ;; Examine Clojure functions (Vars, really)
@@ -150,7 +150,9 @@ itself (not its value) is returned. The reader macro #'x expands to (var x)."}})
                       (read [] (let [i (proxy-super read)]
                                  (.append text (char i))
                                  i)))]
-            (read (PushbackReader. pbr))
+            (if (= :unknown *read-eval*)
+              (throw (IllegalStateException. "Unable to read source while *read-eval* is :unknown."))
+              (read (PushbackReader. pbr)))
             (str text)))))))
 
 (defmacro source
@@ -186,33 +188,12 @@ str-or-pattern."
   `(doseq [v# (dir-fn '~nsname)]
      (println v#)))
 
-(def ^:private demunge-map
-  (into {"$" "/"} (map (fn [[k v]] [v k]) clojure.lang.Compiler/CHAR_MAP)))
-
-(def ^:private demunge-pattern
-  (re-pattern (apply str (interpose "|" (map #(str "\\Q" % "\\E")
-                                             (keys demunge-map))))))
-
-(defn- re-replace [re s f]
-  (let [m (re-matcher re s)
-        mseq (take-while identity
-                         (repeatedly #(when (re-find m)
-                                        [(re-groups m) (.start m) (.end m)])))]
-    (apply str
-           (concat
-             (mapcat (fn [[_ _ start] [groups end]]
-                       (if end
-                         [(subs s start end) (f groups)]
-                         [(subs s start)]))
-                     (cons [0 0 0] mseq)
-                     (concat mseq [nil]))))))
-
 (defn demunge
   "Given a string representation of a fn class,
   as in a stack trace element, returns a readable version."
   {:added "1.3"}
   [fn-name]
-  (re-replace demunge-pattern fn-name demunge-map))
+  (clojure.lang.Compiler/demunge fn-name))
 
 (defn root-cause
   "Returns the initial cause of an exception or error by peeling off all of
diff --git a/src/clj/clojure/set.clj b/src/clj/clojure/set.clj
index 69233ad..6a60d4f 100644
--- a/src/clj/clojure/set.clj
+++ b/src/clj/clojure/set.clj
@@ -72,7 +72,7 @@
   "Returns a rel of the elements of xrel with only the keys in ks"
   {:added "1.0"}
   [xrel ks]
-    (set (map #(select-keys % ks) xrel)))
+  (with-meta (set (map #(select-keys % ks) xrel)) (meta xrel)))
 
 (defn rename-keys
   "Returns the map with the keys in kmap renamed to the vals in kmap"
@@ -80,17 +80,16 @@
   [map kmap]
     (reduce 
      (fn [m [old new]]
-       (if (and (not= old new)
-                (contains? m old))
-         (-> m (assoc new (get m old)) (dissoc old))
+       (if (contains? map old)
+         (assoc m new (get map old))
          m)) 
-     map kmap))
+     (apply dissoc map (keys kmap)) kmap))
 
 (defn rename
   "Returns a rel of the maps in xrel with the keys in kmap renamed to the vals in kmap"
   {:added "1.0"}
   [xrel kmap]
-    (set (map #(rename-keys % kmap) xrel)))
+  (with-meta (set (map #(rename-keys % kmap) xrel)) (meta xrel)))
 
 (defn index
   "Returns a map of the distinct values of ks in the xrel mapped to a
diff --git a/src/clj/clojure/stacktrace.clj b/src/clj/clojure/stacktrace.clj
index d86fc31..977a8bb 100644
--- a/src/clj/clojure/stacktrace.clj
+++ b/src/clj/clojure/stacktrace.clj
@@ -47,12 +47,14 @@
   Does not print chained exceptions (causes)."
   {:added "1.1"}
   ([tr] (print-stack-trace tr nil))
-  ([tr n]
+  ([^Throwable tr n]
      (let [st (.getStackTrace tr)]
        (print-throwable tr)
        (newline)
        (print " at ") 
-       (print-trace-element (first st))
+       (if-let [e (first st)]
+         (print-trace-element e)
+         (print "[empty stack trace]"))
        (newline)
        (doseq [e (if (nil? n)
 		   (rest st)
diff --git a/src/clj/clojure/string.clj b/src/clj/clojure/string.clj
index 188b518..bc3b4e4 100644
--- a/src/clj/clojure/string.clj
+++ b/src/clj/clojure/string.clj
@@ -36,11 +36,11 @@ Design notes for clojure.string:
    general than String. In ordinary usage you will almost always
    pass concrete strings. If you are doing something unusual,
    e.g. passing a mutable implementation of CharSequence, then
-   thead-safety is your responsibility."
+   thread-safety is your responsibility."
       :author "Stuart Sierra, Stuart Halloway, David Liebke"}
   clojure.string
   (:refer-clojure :exclude (replace reverse))
-  (:import (java.util.regex Pattern)
+  (:import (java.util.regex Pattern Matcher)
            clojure.lang.LazilyPersistentVector))
 
 (defn ^String reverse
@@ -49,16 +49,26 @@ Design notes for clojure.string:
   [^CharSequence s]
   (.toString (.reverse (StringBuilder. s))))
 
+(defn ^String re-quote-replacement
+  "Given a replacement string that you wish to be a literal
+   replacement for a pattern match in replace or replace-first, do the
+   necessary escaping of special characters in the replacement."
+  {:added "1.5"}
+  [^CharSequence replacement]
+  (Matcher/quoteReplacement (.toString ^CharSequence replacement)))
+
 (defn- replace-by
   [^CharSequence s re f]
   (let [m (re-matcher re s)]
-    (let [buffer (StringBuffer. (.length s))]
-      (loop []
-        (if (.find m)
-          (do (.appendReplacement m buffer (f (re-groups m)))
-              (recur))
-          (do (.appendTail m buffer)
-              (.toString buffer)))))))
+    (if (.find m)
+      (let [buffer (StringBuffer. (.length s))]
+        (loop [found true]
+          (if found
+            (do (.appendReplacement m buffer (Matcher/quoteReplacement (f (re-groups m))))
+                (recur (.find m)))
+            (do (.appendTail m buffer)
+                (.toString buffer)))))
+      s)))
 
 (defn ^String replace
   "Replaces all instance of match with replacement in s.
@@ -69,7 +79,21 @@ Design notes for clojure.string:
    char / char
    pattern / (string or function of match).
 
-   See also replace-first."
+   See also replace-first.
+
+   The replacement is literal (i.e. none of its characters are treated
+   specially) for all cases above except pattern / string.
+
+   For pattern / string, $1, $2, etc. in the replacement string are
+   substituted with the string that matched the corresponding
+   parenthesized group in the pattern.  If you wish your replacement
+   string r to be used literally, use (re-quote-replacement r) as the
+   replacement argument.  See also documentation for
+   java.util.regex.Matcher's appendReplacement method.
+
+   Example:
+   (clojure.string/replace \"Almost Pig Latin\" #\"\\b(\\w)(\\w+)\\b\" \"$2$1ay\")
+   -> \"lmostAay igPay atinLay\""
   {:added "1.2"}
   [^CharSequence s match replacement]
   (let [s (.toString s)]
@@ -85,12 +109,13 @@ Design notes for clojure.string:
 (defn- replace-first-by
   [^CharSequence s ^Pattern re f]
   (let [m (re-matcher re s)]
-    (let [buffer (StringBuffer. (.length s))]
-      (if (.find m)
-        (let [rep (f (re-groups m))]
-          (.appendReplacement m buffer rep)
-          (.appendTail m buffer)
-          (str buffer))))))
+    (if (.find m)
+      (let [buffer (StringBuffer. (.length s))
+            rep (Matcher/quoteReplacement (f (re-groups m)))]
+        (.appendReplacement m buffer rep)
+        (.appendTail m buffer)
+        (str buffer))
+      s)))
 
 (defn- replace-first-char
   [^CharSequence s ^Character match replace]
@@ -100,6 +125,14 @@ Design notes for clojure.string:
       s
       (str (subs s 0 i) replace (subs s (inc i))))))
 
+(defn- replace-first-str
+  [^CharSequence s ^String match ^String replace]
+  (let [^String s (.toString s)
+        i (.indexOf s match)]
+    (if (= -1 i)
+      s
+      (str (subs s 0 i) replace (subs s (+ i (.length match)))))))
+
 (defn ^String replace-first
   "Replaces the first instance of match with replacement in s.
 
@@ -109,7 +142,22 @@ Design notes for clojure.string:
    string / string
    pattern / (string or function of match).
 
-   See also replace-all."
+   See also replace.
+
+   The replacement is literal (i.e. none of its characters are treated
+   specially) for all cases above except pattern / string.
+
+   For pattern / string, $1, $2, etc. in the replacement string are
+   substituted with the string that matched the corresponding
+   parenthesized group in the pattern.  If you wish your replacement
+   string r to be used literally, use (re-quote-replacement r) as the
+   replacement argument.  See also documentation for
+   java.util.regex.Matcher's appendReplacement method.
+
+   Example:
+   (clojure.string/replace-first \"swap first two words\"
+                                 #\"(\\w+)(\\s+)(\\w+)\" \"$3$2$1\")
+   -> \"first swap two words\""
   {:added "1.2"}
   [^CharSequence s match replacement]
   (let [s (.toString s)]
@@ -117,8 +165,8 @@ Design notes for clojure.string:
      (instance? Character match)
      (replace-first-char s match replacement)
      (instance? CharSequence match)
-     (.replaceFirst s (Pattern/quote (.toString ^CharSequence match))
-                    (.toString ^CharSequence replacement))
+     (replace-first-str s (.toString ^CharSequence match)
+                        (.toString ^CharSequence replacement))
      (instance? Pattern match)
      (if (instance? CharSequence replacement)
        (.replaceFirst (re-matcher ^Pattern match s)
@@ -185,18 +233,30 @@ Design notes for clojure.string:
   "Removes whitespace from both ends of string."
   {:added "1.2"}
   [^CharSequence s]
-  (.. s toString trim))
+  (let [len (.length s)]
+    (loop [rindex len]
+      (if (zero? rindex)
+        ""
+        (if (Character/isWhitespace (.charAt s (dec rindex)))
+          (recur (dec rindex))
+          ;; there is at least one non-whitespace char in the string,
+          ;; so no need to check for lindex reaching len.
+          (loop [lindex 0]
+            (if (Character/isWhitespace (.charAt s lindex))
+              (recur (inc lindex))
+              (.. s (subSequence lindex rindex) toString))))))))
 
 (defn ^String triml
   "Removes whitespace from the left side of string."
   {:added "1.2"}
   [^CharSequence s]
-  (loop [index (int 0)]
-    (if (= (.length s) index)
-      ""
-      (if (Character/isWhitespace (.charAt s index))
-        (recur (inc index))
-        (.. s (subSequence index (.length s)) toString)))))
+  (let [len (.length s)]
+    (loop [index 0]
+      (if (= len index)
+        ""
+        (if (Character/isWhitespace (.charAt s index))
+          (recur (unchecked-inc index))
+          (.. s (subSequence index len) toString))))))
 
 (defn ^String trimr
   "Removes whitespace from the right side of string."
@@ -205,8 +265,8 @@ Design notes for clojure.string:
   (loop [index (.length s)]
     (if (zero? index)
       ""
-      (if (Character/isWhitespace (.charAt s (dec index)))
-        (recur (dec index))
+      (if (Character/isWhitespace (.charAt s (unchecked-dec index)))
+        (recur (unchecked-dec index))
         (.. s (subSequence 0 index) toString)))))
 
 (defn ^String trim-newline
diff --git a/src/clj/clojure/test.clj b/src/clj/clojure/test.clj
index a393b4f..55e00f7 100644
--- a/src/clj/clojure/test.clj
+++ b/src/clj/clojure/test.clj
@@ -333,9 +333,12 @@
   report :type)
 
 (defn- file-and-line 
-  [exception depth]
-  (let [^StackTraceElement s (nth (.getStackTrace exception) depth)]
-    {:file (.getFileName s) :line (.getLineNumber s)}))
+  [^Throwable exception depth]
+  (let [stacktrace (.getStackTrace exception)]
+    (if (< depth (count stacktrace))
+      (let [^StackTraceElement s (nth stacktrace depth)]
+        {:file (.getFileName s) :line (.getLineNumber s)})
+      {:file nil :line nil})))
 
 (defn do-report
   "Add file and line information to a test result and call report.
@@ -704,17 +707,25 @@
                       :expected nil, :actual e})))
       (do-report {:type :end-test-var, :var v}))))
 
+(defn test-vars
+  "Groups vars by their namespace and runs test-vars on them with
+   appropriate fixtures applied."
+  {:added "1.6"}
+  [vars]
+  (doseq [[ns vars] (group-by (comp :ns meta) vars)]
+    (let [once-fixture-fn (join-fixtures (::once-fixtures (meta ns)))
+          each-fixture-fn (join-fixtures (::each-fixtures (meta ns)))]
+      (once-fixture-fn
+       (fn []
+         (doseq [v vars]
+           (when (:test (meta v))
+             (each-fixture-fn (fn [] (test-var v))))))))))
+
 (defn test-all-vars
-  "Calls test-var on every var interned in the namespace, with fixtures."
+  "Calls test-vars on every var interned in the namespace, with fixtures."
   {:added "1.1"}
   [ns]
-  (let [once-fixture-fn (join-fixtures (::once-fixtures (meta ns)))
-        each-fixture-fn (join-fixtures (::each-fixtures (meta ns)))]
-    (once-fixture-fn
-     (fn []
-       (doseq [v (vals (ns-interns ns))]
-         (when (:test (meta v))
-           (each-fixture-fn (fn [] (test-var v)))))))))
+  (test-vars (vals (ns-interns ns))))
 
 (defn test-ns
   "If the namespace defines a function named test-ns-hook, calls that.
@@ -722,7 +733,7 @@
   namespace object or a symbol.
 
   Internally binds *report-counters* to a ref initialized to
-  *inital-report-counters*.  Returns the final, dereferenced state of
+  *initial-report-counters*.  Returns the final, dereferenced state of
   *report-counters*."
   {:added "1.1"}
   [ns]
diff --git a/src/clj/clojure/test/tap.clj b/src/clj/clojure/test/tap.clj
index 37527e6..c3a3a82 100644
--- a/src/clj/clojure/test/tap.clj
+++ b/src/clj/clojure/test/tap.clj
@@ -23,7 +23,7 @@
 (ns ^{:doc "clojure.test extensions for the Test Anything Protocol (TAP)
 
   TAP is a simple text-based syntax for reporting test results.  TAP
-  was originally develped for Perl, and now has implementations in
+  was originally developed for Perl, and now has implementations in
   several languages.  For more information on TAP, see
   http://testanything.org/ and
   http://search.cpan.org/~petdance/TAP-1.0.0/TAP.pm
@@ -69,38 +69,45 @@
   (println "not ok" msg))
 
 ;; This multimethod will override test/report
-(defmulti ^:dynamic tap-report (fn [data] (:type data)))
+(defmulti ^:dynamic tap-report :type)
 
 (defmethod tap-report :default [data]
   (t/with-test-out
    (print-tap-diagnostic (pr-str data))))
 
+(defn print-diagnostics [data]
+  (when (seq t/*testing-contexts*)
+    (print-tap-diagnostic (t/testing-contexts-str)))
+  (when (:message data)
+    (print-tap-diagnostic (:message data)))
+  (print-tap-diagnostic (str "expected:" (pr-str (:expected data))))
+  (if (= :pass (:type data))
+    (print-tap-diagnostic (str "  actual:" (pr-str (:actual data))))
+    (do
+      (print-tap-diagnostic
+       (str "  actual:"
+        (with-out-str
+          (if (instance? Throwable (:actual data))
+            (stack/print-cause-trace (:actual data) t/*stack-trace-depth*)
+            (prn (:actual data)))))))))
+
 (defmethod tap-report :pass [data]
   (t/with-test-out
    (t/inc-report-counter :pass)
-   (print-tap-pass (t/testing-vars-str))
-   (when (seq t/*testing-contexts*)
-     (print-tap-diagnostic (t/testing-contexts-str)))
-   (when (:message data)
-     (print-tap-diagnostic (:message data)))
-   (print-tap-diagnostic (str "expected:" (pr-str (:expected data))))
-   (print-tap-diagnostic (str "  actual:" (pr-str (:actual data))))))
+   (print-tap-pass (t/testing-vars-str data))
+   (print-diagnostics data)))
 
 (defmethod tap-report :error [data]
   (t/with-test-out
    (t/inc-report-counter :error)
-   (print-tap-fail (t/testing-vars-str))
-   (when (seq t/*testing-contexts*)
-     (print-tap-diagnostic (t/testing-contexts-str)))
-   (when (:message data)
-     (print-tap-diagnostic (:message data)))
-   (print-tap-diagnostic "expected:" (pr-str (:expected data)))
-   (print-tap-diagnostic "  actual: ")
-   (print-tap-diagnostic
-    (with-out-str
-      (if (instance? Throwable (:actual data))
-        (stack/print-cause-trace (:actual data) t/*stack-trace-depth*)
-        (prn (:actual data)))))))
+   (print-tap-fail (t/testing-vars-str data))
+   (print-diagnostics data)))
+
+(defmethod tap-report :fail [data]
+  (t/with-test-out
+   (t/inc-report-counter :fail)
+   (print-tap-fail (t/testing-vars-str data))
+   (print-diagnostics data)))
 
 (defmethod tap-report :summary [data]
   (t/with-test-out
diff --git a/src/clj/clojure/walk.clj b/src/clj/clojure/walk.clj
index 10215b0..0d9e0e1 100644
--- a/src/clj/clojure/walk.clj
+++ b/src/clj/clojure/walk.clj
@@ -44,6 +44,8 @@ the sorting function."}
    (list? form) (outer (apply list (map inner form)))
    (instance? clojure.lang.IMapEntry form) (outer (vec (map inner form)))
    (seq? form) (outer (doall (map inner form)))
+   (instance? clojure.lang.IRecord form)
+     (outer (reduce (fn [r x] (conj r (inner x))) form form))
    (coll? form) (outer (into (empty form) (map inner form)))
    :else (outer form)))
 
diff --git a/src/jvm/clojure/asm/AnnotationVisitor.java b/src/jvm/clojure/asm/AnnotationVisitor.java
new file mode 100644
index 0000000..eb01145
--- /dev/null
+++ b/src/jvm/clojure/asm/AnnotationVisitor.java
@@ -0,0 +1,169 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A visitor to visit a Java annotation. The methods of this class must be
+ * called in the following order: ( <tt>visit</tt> | <tt>visitEnum</tt> |
+ * <tt>visitAnnotation</tt> | <tt>visitArray</tt> )* <tt>visitEnd</tt>.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public abstract class AnnotationVisitor {
+
+    /**
+     * The ASM API version implemented by this visitor. The value of this field
+     * must be one of {@link Opcodes#ASM4}.
+     */
+    protected final int api;
+
+    /**
+     * The annotation visitor to which this visitor must delegate method calls.
+     * May be null.
+     */
+    protected AnnotationVisitor av;
+
+    /**
+     * Constructs a new {@link AnnotationVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     */
+    public AnnotationVisitor(final int api) {
+        this(api, null);
+    }
+
+    /**
+     * Constructs a new {@link AnnotationVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param av
+     *            the annotation visitor to which this visitor must delegate
+     *            method calls. May be null.
+     */
+    public AnnotationVisitor(final int api, final AnnotationVisitor av) {
+        if (api != Opcodes.ASM4) {
+            throw new IllegalArgumentException();
+        }
+        this.api = api;
+        this.av = av;
+    }
+
+    /**
+     * Visits a primitive value of the annotation.
+     *
+     * @param name
+     *            the value name.
+     * @param value
+     *            the actual value, whose type must be {@link Byte},
+     *            {@link Boolean}, {@link Character}, {@link Short},
+     *            {@link Integer} , {@link Long}, {@link Float}, {@link Double},
+     *            {@link String} or {@link Type} or OBJECT or ARRAY sort. This
+     *            value can also be an array of byte, boolean, short, char, int,
+     *            long, float or double values (this is equivalent to using
+     *            {@link #visitArray visitArray} and visiting each array element
+     *            in turn, but is more convenient).
+     */
+    public void visit(String name, Object value) {
+        if (av != null) {
+            av.visit(name, value);
+        }
+    }
+
+    /**
+     * Visits an enumeration value of the annotation.
+     *
+     * @param name
+     *            the value name.
+     * @param desc
+     *            the class descriptor of the enumeration class.
+     * @param value
+     *            the actual enumeration value.
+     */
+    public void visitEnum(String name, String desc, String value) {
+        if (av != null) {
+            av.visitEnum(name, desc, value);
+        }
+    }
+
+    /**
+     * Visits a nested annotation value of the annotation.
+     *
+     * @param name
+     *            the value name.
+     * @param desc
+     *            the class descriptor of the nested annotation class.
+     * @return a visitor to visit the actual nested annotation value, or
+     *         <tt>null</tt> if this visitor is not interested in visiting this
+     *         nested annotation. <i>The nested annotation value must be fully
+     *         visited before calling other methods on this annotation
+     *         visitor</i>.
+     */
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+        if (av != null) {
+            return av.visitAnnotation(name, desc);
+        }
+        return null;
+    }
+
+    /**
+     * Visits an array value of the annotation. Note that arrays of primitive
+     * types (such as byte, boolean, short, char, int, long, float or double)
+     * can be passed as value to {@link #visit visit}. This is what
+     * {@link ClassReader} does.
+     *
+     * @param name
+     *            the value name.
+     * @return a visitor to visit the actual array value elements, or
+     *         <tt>null</tt> if this visitor is not interested in visiting these
+     *         values. The 'name' parameters passed to the methods of this
+     *         visitor are ignored. <i>All the array values must be visited
+     *         before calling other methods on this annotation visitor</i>.
+     */
+    public AnnotationVisitor visitArray(String name) {
+        if (av != null) {
+            return av.visitArray(name);
+        }
+        return null;
+    }
+
+    /**
+     * Visits the end of the annotation.
+     */
+    public void visitEnd() {
+        if (av != null) {
+            av.visitEnd();
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/AnnotationWriter.java b/src/jvm/clojure/asm/AnnotationWriter.java
new file mode 100644
index 0000000..9a40bf8
--- /dev/null
+++ b/src/jvm/clojure/asm/AnnotationWriter.java
@@ -0,0 +1,318 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * An {@link AnnotationVisitor} that generates annotations in bytecode form.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+final class AnnotationWriter extends AnnotationVisitor {
+
+    /**
+     * The class writer to which this annotation must be added.
+     */
+    private final ClassWriter cw;
+
+    /**
+     * The number of values in this annotation.
+     */
+    private int size;
+
+    /**
+     * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
+     * writers used for annotation default and annotation arrays use unnamed
+     * values.
+     */
+    private final boolean named;
+
+    /**
+     * The annotation values in bytecode form. This byte vector only contains
+     * the values themselves, i.e. the number of values must be stored as a
+     * unsigned short just before these bytes.
+     */
+    private final ByteVector bv;
+
+    /**
+     * The byte vector to be used to store the number of values of this
+     * annotation. See {@link #bv}.
+     */
+    private final ByteVector parent;
+
+    /**
+     * Where the number of values of this annotation must be stored in
+     * {@link #parent}.
+     */
+    private final int offset;
+
+    /**
+     * Next annotation writer. This field is used to store annotation lists.
+     */
+    AnnotationWriter next;
+
+    /**
+     * Previous annotation writer. This field is used to store annotation lists.
+     */
+    AnnotationWriter prev;
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a new {@link AnnotationWriter}.
+     *
+     * @param cw
+     *            the class writer to which this annotation must be added.
+     * @param named
+     *            <tt>true<tt> if values are named, <tt>false</tt> otherwise.
+     * @param bv
+     *            where the annotation values must be stored.
+     * @param parent
+     *            where the number of annotation values must be stored.
+     * @param offset
+     *            where in <tt>parent</tt> the number of annotation values must
+     *            be stored.
+     */
+    AnnotationWriter(final ClassWriter cw, final boolean named,
+            final ByteVector bv, final ByteVector parent, final int offset) {
+        super(Opcodes.ASM4);
+        this.cw = cw;
+        this.named = named;
+        this.bv = bv;
+        this.parent = parent;
+        this.offset = offset;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the AnnotationVisitor abstract class
+    // ------------------------------------------------------------------------
+
+    @Override
+    public void visit(final String name, final Object value) {
+        ++size;
+        if (named) {
+            bv.putShort(cw.newUTF8(name));
+        }
+        if (value instanceof String) {
+            bv.put12('s', cw.newUTF8((String) value));
+        } else if (value instanceof Byte) {
+            bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index);
+        } else if (value instanceof Boolean) {
+            int v = ((Boolean) value).booleanValue() ? 1 : 0;
+            bv.put12('Z', cw.newInteger(v).index);
+        } else if (value instanceof Character) {
+            bv.put12('C', cw.newInteger(((Character) value).charValue()).index);
+        } else if (value instanceof Short) {
+            bv.put12('S', cw.newInteger(((Short) value).shortValue()).index);
+        } else if (value instanceof Type) {
+            bv.put12('c', cw.newUTF8(((Type) value).getDescriptor()));
+        } else if (value instanceof byte[]) {
+            byte[] v = (byte[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('B', cw.newInteger(v[i]).index);
+            }
+        } else if (value instanceof boolean[]) {
+            boolean[] v = (boolean[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index);
+            }
+        } else if (value instanceof short[]) {
+            short[] v = (short[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('S', cw.newInteger(v[i]).index);
+            }
+        } else if (value instanceof char[]) {
+            char[] v = (char[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('C', cw.newInteger(v[i]).index);
+            }
+        } else if (value instanceof int[]) {
+            int[] v = (int[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('I', cw.newInteger(v[i]).index);
+            }
+        } else if (value instanceof long[]) {
+            long[] v = (long[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('J', cw.newLong(v[i]).index);
+            }
+        } else if (value instanceof float[]) {
+            float[] v = (float[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('F', cw.newFloat(v[i]).index);
+            }
+        } else if (value instanceof double[]) {
+            double[] v = (double[]) value;
+            bv.put12('[', v.length);
+            for (int i = 0; i < v.length; i++) {
+                bv.put12('D', cw.newDouble(v[i]).index);
+            }
+        } else {
+            Item i = cw.newConstItem(value);
+            bv.put12(".s.IFJDCS".charAt(i.type), i.index);
+        }
+    }
+
+    @Override
+    public void visitEnum(final String name, final String desc,
+            final String value) {
+        ++size;
+        if (named) {
+            bv.putShort(cw.newUTF8(name));
+        }
+        bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value));
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String name,
+            final String desc) {
+        ++size;
+        if (named) {
+            bv.putShort(cw.newUTF8(name));
+        }
+        // write tag and type, and reserve space for values count
+        bv.put12('@', cw.newUTF8(desc)).putShort(0);
+        return new AnnotationWriter(cw, true, bv, bv, bv.length - 2);
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(final String name) {
+        ++size;
+        if (named) {
+            bv.putShort(cw.newUTF8(name));
+        }
+        // write tag, and reserve space for array size
+        bv.put12('[', 0);
+        return new AnnotationWriter(cw, false, bv, bv, bv.length - 2);
+    }
+
+    @Override
+    public void visitEnd() {
+        if (parent != null) {
+            byte[] data = parent.data;
+            data[offset] = (byte) (size >>> 8);
+            data[offset + 1] = (byte) size;
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the size of this annotation writer list.
+     *
+     * @return the size of this annotation writer list.
+     */
+    int getSize() {
+        int size = 0;
+        AnnotationWriter aw = this;
+        while (aw != null) {
+            size += aw.bv.length;
+            aw = aw.next;
+        }
+        return size;
+    }
+
+    /**
+     * Puts the annotations of this annotation writer list into the given byte
+     * vector.
+     *
+     * @param out
+     *            where the annotations must be put.
+     */
+    void put(final ByteVector out) {
+        int n = 0;
+        int size = 2;
+        AnnotationWriter aw = this;
+        AnnotationWriter last = null;
+        while (aw != null) {
+            ++n;
+            size += aw.bv.length;
+            aw.visitEnd(); // in case user forgot to call visitEnd
+            aw.prev = last;
+            last = aw;
+            aw = aw.next;
+        }
+        out.putInt(size);
+        out.putShort(n);
+        aw = last;
+        while (aw != null) {
+            out.putByteArray(aw.bv.data, 0, aw.bv.length);
+            aw = aw.prev;
+        }
+    }
+
+    /**
+     * Puts the given annotation lists into the given byte vector.
+     *
+     * @param panns
+     *            an array of annotation writer lists.
+     * @param off
+     *            index of the first annotation to be written.
+     * @param out
+     *            where the annotations must be put.
+     */
+    static void put(final AnnotationWriter[] panns, final int off,
+            final ByteVector out) {
+        int size = 1 + 2 * (panns.length - off);
+        for (int i = off; i < panns.length; ++i) {
+            size += panns[i] == null ? 0 : panns[i].getSize();
+        }
+        out.putInt(size).putByte(panns.length - off);
+        for (int i = off; i < panns.length; ++i) {
+            AnnotationWriter aw = panns[i];
+            AnnotationWriter last = null;
+            int n = 0;
+            while (aw != null) {
+                ++n;
+                aw.visitEnd(); // in case user forgot to call visitEnd
+                aw.prev = last;
+                last = aw;
+                aw = aw.next;
+            }
+            out.putShort(n);
+            aw = last;
+            while (aw != null) {
+                out.putByteArray(aw.bv.data, 0, aw.bv.length);
+                aw = aw.prev;
+            }
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/Attribute.java b/src/jvm/clojure/asm/Attribute.java
new file mode 100644
index 0000000..1259c3c
--- /dev/null
+++ b/src/jvm/clojure/asm/Attribute.java
@@ -0,0 +1,255 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A non standard class, field, method or code attribute.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class Attribute {
+
+    /**
+     * The type of this attribute.
+     */
+    public final String type;
+
+    /**
+     * The raw value of this attribute, used only for unknown attributes.
+     */
+    byte[] value;
+
+    /**
+     * The next attribute in this attribute list. May be <tt>null</tt>.
+     */
+    Attribute next;
+
+    /**
+     * Constructs a new empty attribute.
+     *
+     * @param type
+     *            the type of the attribute.
+     */
+    protected Attribute(final String type) {
+        this.type = type;
+    }
+
+    /**
+     * Returns <tt>true</tt> if this type of attribute is unknown. The default
+     * implementation of this method always returns <tt>true</tt>.
+     *
+     * @return <tt>true</tt> if this type of attribute is unknown.
+     */
+    public boolean isUnknown() {
+        return true;
+    }
+
+    /**
+     * Returns <tt>true</tt> if this type of attribute is a code attribute.
+     *
+     * @return <tt>true</tt> if this type of attribute is a code attribute.
+     */
+    public boolean isCodeAttribute() {
+        return false;
+    }
+
+    /**
+     * Returns the labels corresponding to this attribute.
+     *
+     * @return the labels corresponding to this attribute, or <tt>null</tt> if
+     *         this attribute is not a code attribute that contains labels.
+     */
+    protected Label[] getLabels() {
+        return null;
+    }
+
+    /**
+     * Reads a {@link #type type} attribute. This method must return a
+     * <i>new</i> {@link Attribute} object, of type {@link #type type},
+     * corresponding to the <tt>len</tt> bytes starting at the given offset, in
+     * the given class reader.
+     *
+     * @param cr
+     *            the class that contains the attribute to be read.
+     * @param off
+     *            index of the first byte of the attribute's content in
+     *            {@link ClassReader#b cr.b}. The 6 attribute header bytes,
+     *            containing the type and the length of the attribute, are not
+     *            taken into account here.
+     * @param len
+     *            the length of the attribute's content.
+     * @param buf
+     *            buffer to be used to call {@link ClassReader#readUTF8
+     *            readUTF8}, {@link ClassReader#readClass(int,char[]) readClass}
+     *            or {@link ClassReader#readConst readConst}.
+     * @param codeOff
+     *            index of the first byte of code's attribute content in
+     *            {@link ClassReader#b cr.b}, or -1 if the attribute to be read
+     *            is not a code attribute. The 6 attribute header bytes,
+     *            containing the type and the length of the attribute, are not
+     *            taken into account here.
+     * @param labels
+     *            the labels of the method's code, or <tt>null</tt> if the
+     *            attribute to be read is not a code attribute.
+     * @return a <i>new</i> {@link Attribute} object corresponding to the given
+     *         bytes.
+     */
+    protected Attribute read(final ClassReader cr, final int off,
+            final int len, final char[] buf, final int codeOff,
+            final Label[] labels) {
+        Attribute attr = new Attribute(type);
+        attr.value = new byte[len];
+        System.arraycopy(cr.b, off, attr.value, 0, len);
+        return attr;
+    }
+
+    /**
+     * Returns the byte array form of this attribute.
+     *
+     * @param cw
+     *            the class to which this attribute must be added. This
+     *            parameter can be used to add to the constant pool of this
+     *            class the items that corresponds to this attribute.
+     * @param code
+     *            the bytecode of the method corresponding to this code
+     *            attribute, or <tt>null</tt> if this attribute is not a code
+     *            attributes.
+     * @param len
+     *            the length of the bytecode of the method corresponding to this
+     *            code attribute, or <tt>null</tt> if this attribute is not a
+     *            code attribute.
+     * @param maxStack
+     *            the maximum stack size of the method corresponding to this
+     *            code attribute, or -1 if this attribute is not a code
+     *            attribute.
+     * @param maxLocals
+     *            the maximum number of local variables of the method
+     *            corresponding to this code attribute, or -1 if this attribute
+     *            is not a code attribute.
+     * @return the byte array form of this attribute.
+     */
+    protected ByteVector write(final ClassWriter cw, final byte[] code,
+            final int len, final int maxStack, final int maxLocals) {
+        ByteVector v = new ByteVector();
+        v.data = value;
+        v.length = value.length;
+        return v;
+    }
+
+    /**
+     * Returns the length of the attribute list that begins with this attribute.
+     *
+     * @return the length of the attribute list that begins with this attribute.
+     */
+    final int getCount() {
+        int count = 0;
+        Attribute attr = this;
+        while (attr != null) {
+            count += 1;
+            attr = attr.next;
+        }
+        return count;
+    }
+
+    /**
+     * Returns the size of all the attributes in this attribute list.
+     *
+     * @param cw
+     *            the class writer to be used to convert the attributes into
+     *            byte arrays, with the {@link #write write} method.
+     * @param code
+     *            the bytecode of the method corresponding to these code
+     *            attributes, or <tt>null</tt> if these attributes are not code
+     *            attributes.
+     * @param len
+     *            the length of the bytecode of the method corresponding to
+     *            these code attributes, or <tt>null</tt> if these attributes
+     *            are not code attributes.
+     * @param maxStack
+     *            the maximum stack size of the method corresponding to these
+     *            code attributes, or -1 if these attributes are not code
+     *            attributes.
+     * @param maxLocals
+     *            the maximum number of local variables of the method
+     *            corresponding to these code attributes, or -1 if these
+     *            attributes are not code attributes.
+     * @return the size of all the attributes in this attribute list. This size
+     *         includes the size of the attribute headers.
+     */
+    final int getSize(final ClassWriter cw, final byte[] code, final int len,
+            final int maxStack, final int maxLocals) {
+        Attribute attr = this;
+        int size = 0;
+        while (attr != null) {
+            cw.newUTF8(attr.type);
+            size += attr.write(cw, code, len, maxStack, maxLocals).length + 6;
+            attr = attr.next;
+        }
+        return size;
+    }
+
+    /**
+     * Writes all the attributes of this attribute list in the given byte
+     * vector.
+     *
+     * @param cw
+     *            the class writer to be used to convert the attributes into
+     *            byte arrays, with the {@link #write write} method.
+     * @param code
+     *            the bytecode of the method corresponding to these code
+     *            attributes, or <tt>null</tt> if these attributes are not code
+     *            attributes.
+     * @param len
+     *            the length of the bytecode of the method corresponding to
+     *            these code attributes, or <tt>null</tt> if these attributes
+     *            are not code attributes.
+     * @param maxStack
+     *            the maximum stack size of the method corresponding to these
+     *            code attributes, or -1 if these attributes are not code
+     *            attributes.
+     * @param maxLocals
+     *            the maximum number of local variables of the method
+     *            corresponding to these code attributes, or -1 if these
+     *            attributes are not code attributes.
+     * @param out
+     *            where the attributes must be written.
+     */
+    final void put(final ClassWriter cw, final byte[] code, final int len,
+            final int maxStack, final int maxLocals, final ByteVector out) {
+        Attribute attr = this;
+        while (attr != null) {
+            ByteVector b = attr.write(cw, code, len, maxStack, maxLocals);
+            out.putShort(cw.newUTF8(attr.type)).putInt(b.length);
+            out.putByteArray(b.data, 0, b.length);
+            attr = attr.next;
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/ByteVector.java b/src/jvm/clojure/asm/ByteVector.java
new file mode 100644
index 0000000..0e4f861
--- /dev/null
+++ b/src/jvm/clojure/asm/ByteVector.java
@@ -0,0 +1,306 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A dynamically extensible vector of bytes. This class is roughly equivalent to
+ * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient.
+ *
+ * @author Eric Bruneton
+ */
+public class ByteVector {
+
+    /**
+     * The content of this vector.
+     */
+    byte[] data;
+
+    /**
+     * Actual number of bytes in this vector.
+     */
+    int length;
+
+    /**
+     * Constructs a new {@link ByteVector ByteVector} with a default initial
+     * size.
+     */
+    public ByteVector() {
+        data = new byte[64];
+    }
+
+    /**
+     * Constructs a new {@link ByteVector ByteVector} with the given initial
+     * size.
+     *
+     * @param initialSize
+     *            the initial size of the byte vector to be constructed.
+     */
+    public ByteVector(final int initialSize) {
+        data = new byte[initialSize];
+    }
+
+    /**
+     * Puts a byte into this byte vector. The byte vector is automatically
+     * enlarged if necessary.
+     *
+     * @param b
+     *            a byte.
+     * @return this byte vector.
+     */
+    public ByteVector putByte(final int b) {
+        int length = this.length;
+        if (length + 1 > data.length) {
+            enlarge(1);
+        }
+        data[length++] = (byte) b;
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * Puts two bytes into this byte vector. The byte vector is automatically
+     * enlarged if necessary.
+     *
+     * @param b1
+     *            a byte.
+     * @param b2
+     *            another byte.
+     * @return this byte vector.
+     */
+    ByteVector put11(final int b1, final int b2) {
+        int length = this.length;
+        if (length + 2 > data.length) {
+            enlarge(2);
+        }
+        byte[] data = this.data;
+        data[length++] = (byte) b1;
+        data[length++] = (byte) b2;
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * Puts a short into this byte vector. The byte vector is automatically
+     * enlarged if necessary.
+     *
+     * @param s
+     *            a short.
+     * @return this byte vector.
+     */
+    public ByteVector putShort(final int s) {
+        int length = this.length;
+        if (length + 2 > data.length) {
+            enlarge(2);
+        }
+        byte[] data = this.data;
+        data[length++] = (byte) (s >>> 8);
+        data[length++] = (byte) s;
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * Puts a byte and a short into this byte vector. The byte vector is
+     * automatically enlarged if necessary.
+     *
+     * @param b
+     *            a byte.
+     * @param s
+     *            a short.
+     * @return this byte vector.
+     */
+    ByteVector put12(final int b, final int s) {
+        int length = this.length;
+        if (length + 3 > data.length) {
+            enlarge(3);
+        }
+        byte[] data = this.data;
+        data[length++] = (byte) b;
+        data[length++] = (byte) (s >>> 8);
+        data[length++] = (byte) s;
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * Puts an int into this byte vector. The byte vector is automatically
+     * enlarged if necessary.
+     *
+     * @param i
+     *            an int.
+     * @return this byte vector.
+     */
+    public ByteVector putInt(final int i) {
+        int length = this.length;
+        if (length + 4 > data.length) {
+            enlarge(4);
+        }
+        byte[] data = this.data;
+        data[length++] = (byte) (i >>> 24);
+        data[length++] = (byte) (i >>> 16);
+        data[length++] = (byte) (i >>> 8);
+        data[length++] = (byte) i;
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * Puts a long into this byte vector. The byte vector is automatically
+     * enlarged if necessary.
+     *
+     * @param l
+     *            a long.
+     * @return this byte vector.
+     */
+    public ByteVector putLong(final long l) {
+        int length = this.length;
+        if (length + 8 > data.length) {
+            enlarge(8);
+        }
+        byte[] data = this.data;
+        int i = (int) (l >>> 32);
+        data[length++] = (byte) (i >>> 24);
+        data[length++] = (byte) (i >>> 16);
+        data[length++] = (byte) (i >>> 8);
+        data[length++] = (byte) i;
+        i = (int) l;
+        data[length++] = (byte) (i >>> 24);
+        data[length++] = (byte) (i >>> 16);
+        data[length++] = (byte) (i >>> 8);
+        data[length++] = (byte) i;
+        this.length = length;
+        return this;
+    }
+
+    /**
+     * Puts an UTF8 string into this byte vector. The byte vector is
+     * automatically enlarged if necessary.
+     *
+     * @param s
+     *            a String.
+     * @return this byte vector.
+     */
+    public ByteVector putUTF8(final String s) {
+        int charLength = s.length();
+        int len = length;
+        if (len + 2 + charLength > data.length) {
+            enlarge(2 + charLength);
+        }
+        byte[] data = this.data;
+        // optimistic algorithm: instead of computing the byte length and then
+        // serializing the string (which requires two loops), we assume the byte
+        // length is equal to char length (which is the most frequent case), and
+        // we start serializing the string right away. During the serialization,
+        // if we find that this assumption is wrong, we continue with the
+        // general method.
+        data[len++] = (byte) (charLength >>> 8);
+        data[len++] = (byte) charLength;
+        for (int i = 0; i < charLength; ++i) {
+            char c = s.charAt(i);
+            if (c >= '\001' && c <= '\177') {
+                data[len++] = (byte) c;
+            } else {
+                int byteLength = i;
+                for (int j = i; j < charLength; ++j) {
+                    c = s.charAt(j);
+                    if (c >= '\001' && c <= '\177') {
+                        byteLength++;
+                    } else if (c > '\u07FF') {
+                        byteLength += 3;
+                    } else {
+                        byteLength += 2;
+                    }
+                }
+                data[length] = (byte) (byteLength >>> 8);
+                data[length + 1] = (byte) byteLength;
+                if (length + 2 + byteLength > data.length) {
+                    length = len;
+                    enlarge(2 + byteLength);
+                    data = this.data;
+                }
+                for (int j = i; j < charLength; ++j) {
+                    c = s.charAt(j);
+                    if (c >= '\001' && c <= '\177') {
+                        data[len++] = (byte) c;
+                    } else if (c > '\u07FF') {
+                        data[len++] = (byte) (0xE0 | c >> 12 & 0xF);
+                        data[len++] = (byte) (0x80 | c >> 6 & 0x3F);
+                        data[len++] = (byte) (0x80 | c & 0x3F);
+                    } else {
+                        data[len++] = (byte) (0xC0 | c >> 6 & 0x1F);
+                        data[len++] = (byte) (0x80 | c & 0x3F);
+                    }
+                }
+                break;
+            }
+        }
+        length = len;
+        return this;
+    }
+
+    /**
+     * Puts an array of bytes into this byte vector. The byte vector is
+     * automatically enlarged if necessary.
+     *
+     * @param b
+     *            an array of bytes. May be <tt>null</tt> to put <tt>len</tt>
+     *            null bytes into this byte vector.
+     * @param off
+     *            index of the fist byte of b that must be copied.
+     * @param len
+     *            number of bytes of b that must be copied.
+     * @return this byte vector.
+     */
+    public ByteVector putByteArray(final byte[] b, final int off, final int len) {
+        if (length + len > data.length) {
+            enlarge(len);
+        }
+        if (b != null) {
+            System.arraycopy(b, off, data, length, len);
+        }
+        length += len;
+        return this;
+    }
+
+    /**
+     * Enlarge this byte vector so that it can receive n more bytes.
+     *
+     * @param size
+     *            number of additional bytes that this byte vector should be
+     *            able to receive.
+     */
+    private void enlarge(final int size) {
+        int length1 = 2 * data.length;
+        int length2 = length + size;
+        byte[] newData = new byte[length1 > length2 ? length1 : length2];
+        System.arraycopy(data, 0, newData, 0, length);
+        data = newData;
+    }
+}
diff --git a/src/jvm/clojure/asm/ClassReader.java b/src/jvm/clojure/asm/ClassReader.java
new file mode 100644
index 0000000..9265a37
--- /dev/null
+++ b/src/jvm/clojure/asm/ClassReader.java
@@ -0,0 +1,2202 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A Java class parser to make a {@link ClassVisitor} visit an existing class.
+ * This class parses a byte array conforming to the Java class file format and
+ * calls the appropriate visit methods of a given class visitor for each field,
+ * method and bytecode instruction encountered.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class ClassReader {
+
+    /**
+     * True to enable signatures support.
+     */
+    static final boolean SIGNATURES = true;
+
+    /**
+     * True to enable annotations support.
+     */
+    static final boolean ANNOTATIONS = true;
+
+    /**
+     * True to enable stack map frames support.
+     */
+    static final boolean FRAMES = true;
+
+    /**
+     * True to enable bytecode writing support.
+     */
+    static final boolean WRITER = true;
+
+    /**
+     * True to enable JSR_W and GOTO_W support.
+     */
+    static final boolean RESIZE = true;
+
+    /**
+     * Flag to skip method code. If this class is set <code>CODE</code>
+     * attribute won't be visited. This can be used, for example, to retrieve
+     * annotations for methods and method parameters.
+     */
+    public static final int SKIP_CODE = 1;
+
+    /**
+     * Flag to skip the debug information in the class. If this flag is set the
+     * debug information of the class is not visited, i.e. the
+     * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
+     * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be
+     * called.
+     */
+    public static final int SKIP_DEBUG = 2;
+
+    /**
+     * Flag to skip the stack map frames in the class. If this flag is set the
+     * stack map frames of the class is not visited, i.e. the
+     * {@link MethodVisitor#visitFrame visitFrame} method will not be called.
+     * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is
+     * used: it avoids visiting frames that will be ignored and recomputed from
+     * scratch in the class writer.
+     */
+    public static final int SKIP_FRAMES = 4;
+
+    /**
+     * Flag to expand the stack map frames. By default stack map frames are
+     * visited in their original format (i.e. "expanded" for classes whose
+     * version is less than V1_6, and "compressed" for the other classes). If
+     * this flag is set, stack map frames are always visited in expanded format
+     * (this option adds a decompression/recompression step in ClassReader and
+     * ClassWriter which degrades performances quite a lot).
+     */
+    public static final int EXPAND_FRAMES = 8;
+
+    /**
+     * The class to be parsed. <i>The content of this array must not be
+     * modified. This field is intended for {@link Attribute} sub classes, and
+     * is normally not needed by class generators or adapters.</i>
+     */
+    public final byte[] b;
+
+    /**
+     * The start index of each constant pool item in {@link #b b}, plus one. The
+     * one byte offset skips the constant pool item tag that indicates its type.
+     */
+    private final int[] items;
+
+    /**
+     * The String objects corresponding to the CONSTANT_Utf8 items. This cache
+     * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item,
+     * which GREATLY improves performances (by a factor 2 to 3). This caching
+     * strategy could be extended to all constant pool items, but its benefit
+     * would not be so great for these items (because they are much less
+     * expensive to parse than CONSTANT_Utf8 items).
+     */
+    private final String[] strings;
+
+    /**
+     * Maximum length of the strings contained in the constant pool of the
+     * class.
+     */
+    private final int maxStringLength;
+
+    /**
+     * Start index of the class header information (access, name...) in
+     * {@link #b b}.
+     */
+    public final int header;
+
+    // ------------------------------------------------------------------------
+    // Constructors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a new {@link ClassReader} object.
+     *
+     * @param b
+     *            the bytecode of the class to be read.
+     */
+    public ClassReader(final byte[] b) {
+        this(b, 0, b.length);
+    }
+
+    /**
+     * Constructs a new {@link ClassReader} object.
+     *
+     * @param b
+     *            the bytecode of the class to be read.
+     * @param off
+     *            the start offset of the class data.
+     * @param len
+     *            the length of the class data.
+     */
+    public ClassReader(final byte[] b, final int off, final int len) {
+        this.b = b;
+        // checks the class version
+        if (readShort(off + 6) > Opcodes.V1_7) {
+            throw new IllegalArgumentException();
+        }
+        // parses the constant pool
+        items = new int[readUnsignedShort(off + 8)];
+        int n = items.length;
+        strings = new String[n];
+        int max = 0;
+        int index = off + 10;
+        for (int i = 1; i < n; ++i) {
+            items[i] = index + 1;
+            int size;
+            switch (b[index]) {
+            case ClassWriter.FIELD:
+            case ClassWriter.METH:
+            case ClassWriter.IMETH:
+            case ClassWriter.INT:
+            case ClassWriter.FLOAT:
+            case ClassWriter.NAME_TYPE:
+            case ClassWriter.INDY:
+                size = 5;
+                break;
+            case ClassWriter.LONG:
+            case ClassWriter.DOUBLE:
+                size = 9;
+                ++i;
+                break;
+            case ClassWriter.UTF8:
+                size = 3 + readUnsignedShort(index + 1);
+                if (size > max) {
+                    max = size;
+                }
+                break;
+            case ClassWriter.HANDLE:
+                size = 4;
+                break;
+            // case ClassWriter.CLASS:
+            // case ClassWriter.STR:
+            // case ClassWriter.MTYPE
+            default:
+                size = 3;
+                break;
+            }
+            index += size;
+        }
+        maxStringLength = max;
+        // the class header information starts just after the constant pool
+        header = index;
+    }
+
+    /**
+     * Returns the class's access flags (see {@link Opcodes}). This value may
+     * not reflect Deprecated and Synthetic flags when bytecode is before 1.5
+     * and those flags are represented by attributes.
+     *
+     * @return the class access flags
+     *
+     * @see ClassVisitor#visit(int, int, String, String, String, String[])
+     */
+    public int getAccess() {
+        return readUnsignedShort(header);
+    }
+
+    /**
+     * Returns the internal name of the class (see
+     * {@link Type#getInternalName() getInternalName}).
+     *
+     * @return the internal class name
+     *
+     * @see ClassVisitor#visit(int, int, String, String, String, String[])
+     */
+    public String getClassName() {
+        return readClass(header + 2, new char[maxStringLength]);
+    }
+
+    /**
+     * Returns the internal of name of the super class (see
+     * {@link Type#getInternalName() getInternalName}). For interfaces, the
+     * super class is {@link Object}.
+     *
+     * @return the internal name of super class, or <tt>null</tt> for
+     *         {@link Object} class.
+     *
+     * @see ClassVisitor#visit(int, int, String, String, String, String[])
+     */
+    public String getSuperName() {
+        return readClass(header + 4, new char[maxStringLength]);
+    }
+
+    /**
+     * Returns the internal names of the class's interfaces (see
+     * {@link Type#getInternalName() getInternalName}).
+     *
+     * @return the array of internal names for all implemented interfaces or
+     *         <tt>null</tt>.
+     *
+     * @see ClassVisitor#visit(int, int, String, String, String, String[])
+     */
+    public String[] getInterfaces() {
+        int index = header + 6;
+        int n = readUnsignedShort(index);
+        String[] interfaces = new String[n];
+        if (n > 0) {
+            char[] buf = new char[maxStringLength];
+            for (int i = 0; i < n; ++i) {
+                index += 2;
+                interfaces[i] = readClass(index, buf);
+            }
+        }
+        return interfaces;
+    }
+
+    /**
+     * Copies the constant pool data into the given {@link ClassWriter}. Should
+     * be called before the {@link #accept(ClassVisitor,int)} method.
+     *
+     * @param classWriter
+     *            the {@link ClassWriter} to copy constant pool into.
+     */
+    void copyPool(final ClassWriter classWriter) {
+        char[] buf = new char[maxStringLength];
+        int ll = items.length;
+        Item[] items2 = new Item[ll];
+        for (int i = 1; i < ll; i++) {
+            int index = items[i];
+            int tag = b[index - 1];
+            Item item = new Item(i);
+            int nameType;
+            switch (tag) {
+            case ClassWriter.FIELD:
+            case ClassWriter.METH:
+            case ClassWriter.IMETH:
+                nameType = items[readUnsignedShort(index + 2)];
+                item.set(tag, readClass(index, buf), readUTF8(nameType, buf),
+                        readUTF8(nameType + 2, buf));
+                break;
+            case ClassWriter.INT:
+                item.set(readInt(index));
+                break;
+            case ClassWriter.FLOAT:
+                item.set(Float.intBitsToFloat(readInt(index)));
+                break;
+            case ClassWriter.NAME_TYPE:
+                item.set(tag, readUTF8(index, buf), readUTF8(index + 2, buf),
+                        null);
+                break;
+            case ClassWriter.LONG:
+                item.set(readLong(index));
+                ++i;
+                break;
+            case ClassWriter.DOUBLE:
+                item.set(Double.longBitsToDouble(readLong(index)));
+                ++i;
+                break;
+            case ClassWriter.UTF8: {
+                String s = strings[i];
+                if (s == null) {
+                    index = items[i];
+                    s = strings[i] = readUTF(index + 2,
+                            readUnsignedShort(index), buf);
+                }
+                item.set(tag, s, null, null);
+                break;
+            }
+            case ClassWriter.HANDLE: {
+                int fieldOrMethodRef = items[readUnsignedShort(index + 1)];
+                nameType = items[readUnsignedShort(fieldOrMethodRef + 2)];
+                item.set(ClassWriter.HANDLE_BASE + readByte(index),
+                        readClass(fieldOrMethodRef, buf),
+                        readUTF8(nameType, buf), readUTF8(nameType + 2, buf));
+                break;
+            }
+            case ClassWriter.INDY:
+                if (classWriter.bootstrapMethods == null) {
+                    copyBootstrapMethods(classWriter, items2, buf);
+                }
+                nameType = items[readUnsignedShort(index + 2)];
+                item.set(readUTF8(nameType, buf), readUTF8(nameType + 2, buf),
+                        readUnsignedShort(index));
+                break;
+            // case ClassWriter.STR:
+            // case ClassWriter.CLASS:
+            // case ClassWriter.MTYPE
+            default:
+                item.set(tag, readUTF8(index, buf), null, null);
+                break;
+            }
+
+            int index2 = item.hashCode % items2.length;
+            item.next = items2[index2];
+            items2[index2] = item;
+        }
+
+        int off = items[1] - 1;
+        classWriter.pool.putByteArray(b, off, header - off);
+        classWriter.items = items2;
+        classWriter.threshold = (int) (0.75d * ll);
+        classWriter.index = ll;
+    }
+
+    /**
+     * Copies the bootstrap method data into the given {@link ClassWriter}.
+     * Should be called before the {@link #accept(ClassVisitor,int)} method.
+     *
+     * @param classWriter
+     *            the {@link ClassWriter} to copy bootstrap methods into.
+     */
+    private void copyBootstrapMethods(final ClassWriter classWriter,
+            final Item[] items, final char[] c) {
+        // finds the "BootstrapMethods" attribute
+        int u = getAttributes();
+        boolean found = false;
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            String attrName = readUTF8(u + 2, c);
+            if ("BootstrapMethods".equals(attrName)) {
+                found = true;
+                break;
+            }
+            u += 6 + readInt(u + 4);
+        }
+        if (!found) {
+            return;
+        }
+        // copies the bootstrap methods in the class writer
+        int boostrapMethodCount = readUnsignedShort(u + 8);
+        for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) {
+            int position = v - u - 10;
+            int hashCode = readConst(readUnsignedShort(v), c).hashCode();
+            for (int k = readUnsignedShort(v + 2); k > 0; --k) {
+                hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode();
+                v += 2;
+            }
+            v += 4;
+            Item item = new Item(j);
+            item.set(position, hashCode & 0x7FFFFFFF);
+            int index = item.hashCode % items.length;
+            item.next = items[index];
+            items[index] = item;
+        }
+        int attrSize = readInt(u + 4);
+        ByteVector bootstrapMethods = new ByteVector(attrSize + 62);
+        bootstrapMethods.putByteArray(b, u + 10, attrSize - 2);
+        classWriter.bootstrapMethodsCount = boostrapMethodCount;
+        classWriter.bootstrapMethods = bootstrapMethods;
+    }
+
+    /**
+     * Constructs a new {@link ClassReader} object.
+     *
+     * @param is
+     *            an input stream from which to read the class.
+     * @throws IOException
+     *             if a problem occurs during reading.
+     */
+    public ClassReader(final InputStream is) throws IOException {
+        this(readClass(is, false));
+    }
+
+    /**
+     * Constructs a new {@link ClassReader} object.
+     *
+     * @param name
+     *            the binary qualified name of the class to be read.
+     * @throws IOException
+     *             if an exception occurs during reading.
+     */
+    public ClassReader(final String name) throws IOException {
+        this(readClass(
+                ClassLoader.getSystemResourceAsStream(name.replace('.', '/')
+                        + ".class"), true));
+    }
+
+    /**
+     * Reads the bytecode of a class.
+     *
+     * @param is
+     *            an input stream from which to read the class.
+     * @param close
+     *            true to close the input stream after reading.
+     * @return the bytecode read from the given input stream.
+     * @throws IOException
+     *             if a problem occurs during reading.
+     */
+    private static byte[] readClass(final InputStream is, boolean close)
+            throws IOException {
+        if (is == null) {
+            throw new IOException("Class not found");
+        }
+        try {
+            byte[] b = new byte[is.available()];
+            int len = 0;
+            while (true) {
+                int n = is.read(b, len, b.length - len);
+                if (n == -1) {
+                    if (len < b.length) {
+                        byte[] c = new byte[len];
+                        System.arraycopy(b, 0, c, 0, len);
+                        b = c;
+                    }
+                    return b;
+                }
+                len += n;
+                if (len == b.length) {
+                    int last = is.read();
+                    if (last < 0) {
+                        return b;
+                    }
+                    byte[] c = new byte[b.length + 1000];
+                    System.arraycopy(b, 0, c, 0, len);
+                    c[len++] = (byte) last;
+                    b = c;
+                }
+            }
+        } finally {
+            if (close) {
+                is.close();
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Public methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Makes the given visitor visit the Java class of this {@link ClassReader}
+     * . This class is the one specified in the constructor (see
+     * {@link #ClassReader(byte[]) ClassReader}).
+     *
+     * @param classVisitor
+     *            the visitor that must visit this class.
+     * @param flags
+     *            option flags that can be used to modify the default behavior
+     *            of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}
+     *            , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
+     */
+    public void accept(final ClassVisitor classVisitor, final int flags) {
+        accept(classVisitor, new Attribute[0], flags);
+    }
+
+    /**
+     * Makes the given visitor visit the Java class of this {@link ClassReader}.
+     * This class is the one specified in the constructor (see
+     * {@link #ClassReader(byte[]) ClassReader}).
+     *
+     * @param classVisitor
+     *            the visitor that must visit this class.
+     * @param attrs
+     *            prototypes of the attributes that must be parsed during the
+     *            visit of the class. Any attribute whose type is not equal to
+     *            the type of one the prototypes will not be parsed: its byte
+     *            array value will be passed unchanged to the ClassWriter.
+     *            <i>This may corrupt it if this value contains references to
+     *            the constant pool, or has syntactic or semantic links with a
+     *            class element that has been transformed by a class adapter
+     *            between the reader and the writer</i>.
+     * @param flags
+     *            option flags that can be used to modify the default behavior
+     *            of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}
+     *            , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
+     */
+    public void accept(final ClassVisitor classVisitor,
+            final Attribute[] attrs, final int flags) {
+        int u = header; // current offset in the class file
+        char[] c = new char[maxStringLength]; // buffer used to read strings
+
+        Context context = new Context();
+        context.attrs = attrs;
+        context.flags = flags;
+        context.buffer = c;
+
+        // reads the class declaration
+        int access = readUnsignedShort(u);
+        String name = readClass(u + 2, c);
+        String superClass = readClass(u + 4, c);
+        String[] interfaces = new String[readUnsignedShort(u + 6)];
+        u += 8;
+        for (int i = 0; i < interfaces.length; ++i) {
+            interfaces[i] = readClass(u, c);
+            u += 2;
+        }
+
+        // reads the class attributes
+        String signature = null;
+        String sourceFile = null;
+        String sourceDebug = null;
+        String enclosingOwner = null;
+        String enclosingName = null;
+        String enclosingDesc = null;
+        int anns = 0;
+        int ianns = 0;
+        int innerClasses = 0;
+        Attribute attributes = null;
+
+        u = getAttributes();
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            String attrName = readUTF8(u + 2, c);
+            // tests are sorted in decreasing frequency order
+            // (based on frequencies observed on typical classes)
+            if ("SourceFile".equals(attrName)) {
+                sourceFile = readUTF8(u + 8, c);
+            } else if ("InnerClasses".equals(attrName)) {
+                innerClasses = u + 8;
+            } else if ("EnclosingMethod".equals(attrName)) {
+                enclosingOwner = readClass(u + 8, c);
+                int item = readUnsignedShort(u + 10);
+                if (item != 0) {
+                    enclosingName = readUTF8(items[item], c);
+                    enclosingDesc = readUTF8(items[item] + 2, c);
+                }
+            } else if (SIGNATURES && "Signature".equals(attrName)) {
+                signature = readUTF8(u + 8, c);
+            } else if (ANNOTATIONS
+                    && "RuntimeVisibleAnnotations".equals(attrName)) {
+                anns = u + 8;
+            } else if ("Deprecated".equals(attrName)) {
+                access |= Opcodes.ACC_DEPRECATED;
+            } else if ("Synthetic".equals(attrName)) {
+                access |= Opcodes.ACC_SYNTHETIC
+                        | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
+            } else if ("SourceDebugExtension".equals(attrName)) {
+                int len = readInt(u + 4);
+                sourceDebug = readUTF(u + 8, len, new char[len]);
+            } else if (ANNOTATIONS
+                    && "RuntimeInvisibleAnnotations".equals(attrName)) {
+                ianns = u + 8;
+            } else if ("BootstrapMethods".equals(attrName)) {
+                int[] bootstrapMethods = new int[readUnsignedShort(u + 8)];
+                for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) {
+                    bootstrapMethods[j] = v;
+                    v += 2 + readUnsignedShort(v + 2) << 1;
+                }
+                context.bootstrapMethods = bootstrapMethods;
+            } else {
+                Attribute attr = readAttribute(attrs, attrName, u + 8,
+                        readInt(u + 4), c, -1, null);
+                if (attr != null) {
+                    attr.next = attributes;
+                    attributes = attr;
+                }
+            }
+            u += 6 + readInt(u + 4);
+        }
+
+        // visits the class declaration
+        classVisitor.visit(readInt(items[1] - 7), access, name, signature,
+                superClass, interfaces);
+
+        // visits the source and debug info
+        if ((flags & SKIP_DEBUG) == 0
+                && (sourceFile != null || sourceDebug != null)) {
+            classVisitor.visitSource(sourceFile, sourceDebug);
+        }
+
+        // visits the outer class
+        if (enclosingOwner != null) {
+            classVisitor.visitOuterClass(enclosingOwner, enclosingName,
+                    enclosingDesc);
+        }
+
+        // visits the class annotations
+        if (ANNOTATIONS && anns != 0) {
+            for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
+                v = readAnnotationValues(v + 2, c, true,
+                        classVisitor.visitAnnotation(readUTF8(v, c), true));
+            }
+        }
+        if (ANNOTATIONS && ianns != 0) {
+            for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
+                v = readAnnotationValues(v + 2, c, true,
+                        classVisitor.visitAnnotation(readUTF8(v, c), false));
+            }
+        }
+
+        // visits the attributes
+        while (attributes != null) {
+            Attribute attr = attributes.next;
+            attributes.next = null;
+            classVisitor.visitAttribute(attributes);
+            attributes = attr;
+        }
+
+        // visits the inner classes
+        if (innerClasses != 0) {
+            int v = innerClasses + 2;
+            for (int i = readUnsignedShort(innerClasses); i > 0; --i) {
+                classVisitor.visitInnerClass(readClass(v, c),
+                        readClass(v + 2, c), readUTF8(v + 4, c),
+                        readUnsignedShort(v + 6));
+                v += 8;
+            }
+        }
+
+        // visits the fields and methods
+        u = header + 10 + 2 * interfaces.length;
+        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
+            u = readField(classVisitor, context, u);
+        }
+        u += 2;
+        for (int i = readUnsignedShort(u - 2); i > 0; --i) {
+            u = readMethod(classVisitor, context, u);
+        }
+
+        // visits the end of the class
+        classVisitor.visitEnd();
+    }
+
+    /**
+     * Reads a field and makes the given visitor visit it.
+     *
+     * @param classVisitor
+     *            the visitor that must visit the field.
+     * @param context
+     *            information about the class being parsed.
+     * @param u
+     *            the start offset of the field in the class file.
+     * @return the offset of the first byte following the field in the class.
+     */
+    private int readField(final ClassVisitor classVisitor,
+            final Context context, int u) {
+        // reads the field declaration
+        char[] c = context.buffer;
+        int access = readUnsignedShort(u);
+        String name = readUTF8(u + 2, c);
+        String desc = readUTF8(u + 4, c);
+        u += 6;
+
+        // reads the field attributes
+        String signature = null;
+        int anns = 0;
+        int ianns = 0;
+        Object value = null;
+        Attribute attributes = null;
+
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            String attrName = readUTF8(u + 2, c);
+            // tests are sorted in decreasing frequency order
+            // (based on frequencies observed on typical classes)
+            if ("ConstantValue".equals(attrName)) {
+                int item = readUnsignedShort(u + 8);
+                value = item == 0 ? null : readConst(item, c);
+            } else if (SIGNATURES && "Signature".equals(attrName)) {
+                signature = readUTF8(u + 8, c);
+            } else if ("Deprecated".equals(attrName)) {
+                access |= Opcodes.ACC_DEPRECATED;
+            } else if ("Synthetic".equals(attrName)) {
+                access |= Opcodes.ACC_SYNTHETIC
+                        | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
+            } else if (ANNOTATIONS
+                    && "RuntimeVisibleAnnotations".equals(attrName)) {
+                anns = u + 8;
+            } else if (ANNOTATIONS
+                    && "RuntimeInvisibleAnnotations".equals(attrName)) {
+                ianns = u + 8;
+            } else {
+                Attribute attr = readAttribute(context.attrs, attrName, u + 8,
+                        readInt(u + 4), c, -1, null);
+                if (attr != null) {
+                    attr.next = attributes;
+                    attributes = attr;
+                }
+            }
+            u += 6 + readInt(u + 4);
+        }
+        u += 2;
+
+        // visits the field declaration
+        FieldVisitor fv = classVisitor.visitField(access, name, desc,
+                signature, value);
+        if (fv == null) {
+            return u;
+        }
+
+        // visits the field annotations
+        if (ANNOTATIONS && anns != 0) {
+            for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
+                v = readAnnotationValues(v + 2, c, true,
+                        fv.visitAnnotation(readUTF8(v, c), true));
+            }
+        }
+        if (ANNOTATIONS && ianns != 0) {
+            for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
+                v = readAnnotationValues(v + 2, c, true,
+                        fv.visitAnnotation(readUTF8(v, c), false));
+            }
+        }
+
+        // visits the field attributes
+        while (attributes != null) {
+            Attribute attr = attributes.next;
+            attributes.next = null;
+            fv.visitAttribute(attributes);
+            attributes = attr;
+        }
+
+        // visits the end of the field
+        fv.visitEnd();
+
+        return u;
+    }
+
+    /**
+     * Reads a method and makes the given visitor visit it.
+     *
+     * @param classVisitor
+     *            the visitor that must visit the method.
+     * @param context
+     *            information about the class being parsed.
+     * @param u
+     *            the start offset of the method in the class file.
+     * @return the offset of the first byte following the method in the class.
+     */
+    private int readMethod(final ClassVisitor classVisitor,
+            final Context context, int u) {
+        // reads the method declaration
+        char[] c = context.buffer;
+        int access = readUnsignedShort(u);
+        String name = readUTF8(u + 2, c);
+        String desc = readUTF8(u + 4, c);
+        u += 6;
+
+        // reads the method attributes
+        int code = 0;
+        int exception = 0;
+        String[] exceptions = null;
+        String signature = null;
+        int anns = 0;
+        int ianns = 0;
+        int dann = 0;
+        int mpanns = 0;
+        int impanns = 0;
+        int firstAttribute = u;
+        Attribute attributes = null;
+
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            String attrName = readUTF8(u + 2, c);
+            // tests are sorted in decreasing frequency order
+            // (based on frequencies observed on typical classes)
+            if ("Code".equals(attrName)) {
+                if ((context.flags & SKIP_CODE) == 0) {
+                    code = u + 8;
+                }
+            } else if ("Exceptions".equals(attrName)) {
+                exceptions = new String[readUnsignedShort(u + 8)];
+                exception = u + 10;
+                for (int j = 0; j < exceptions.length; ++j) {
+                    exceptions[j] = readClass(exception, c);
+                    exception += 2;
+                }
+            } else if (SIGNATURES && "Signature".equals(attrName)) {
+                signature = readUTF8(u + 8, c);
+            } else if ("Deprecated".equals(attrName)) {
+                access |= Opcodes.ACC_DEPRECATED;
+            } else if (ANNOTATIONS
+                    && "RuntimeVisibleAnnotations".equals(attrName)) {
+                anns = u + 8;
+            } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) {
+                dann = u + 8;
+            } else if ("Synthetic".equals(attrName)) {
+                access |= Opcodes.ACC_SYNTHETIC
+                        | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
+            } else if (ANNOTATIONS
+                    && "RuntimeInvisibleAnnotations".equals(attrName)) {
+                ianns = u + 8;
+            } else if (ANNOTATIONS
+                    && "RuntimeVisibleParameterAnnotations".equals(attrName)) {
+                mpanns = u + 8;
+            } else if (ANNOTATIONS
+                    && "RuntimeInvisibleParameterAnnotations".equals(attrName)) {
+                impanns = u + 8;
+            } else {
+                Attribute attr = readAttribute(context.attrs, attrName, u + 8,
+                        readInt(u + 4), c, -1, null);
+                if (attr != null) {
+                    attr.next = attributes;
+                    attributes = attr;
+                }
+            }
+            u += 6 + readInt(u + 4);
+        }
+        u += 2;
+
+        // visits the method declaration
+        MethodVisitor mv = classVisitor.visitMethod(access, name, desc,
+                signature, exceptions);
+        if (mv == null) {
+            return u;
+        }
+
+        /*
+         * if the returned MethodVisitor is in fact a MethodWriter, it means
+         * there is no method adapter between the reader and the writer. If, in
+         * addition, the writer's constant pool was copied from this reader
+         * (mw.cw.cr == this), and the signature and exceptions of the method
+         * have not been changed, then it is possible to skip all visit events
+         * and just copy the original code of the method to the writer (the
+         * access, name and descriptor can have been changed, this is not
+         * important since they are not copied as is from the reader).
+         */
+        if (WRITER && mv instanceof MethodWriter) {
+            MethodWriter mw = (MethodWriter) mv;
+            if (mw.cw.cr == this && signature == mw.signature) {
+                boolean sameExceptions = false;
+                if (exceptions == null) {
+                    sameExceptions = mw.exceptionCount == 0;
+                } else if (exceptions.length == mw.exceptionCount) {
+                    sameExceptions = true;
+                    for (int j = exceptions.length - 1; j >= 0; --j) {
+                        exception -= 2;
+                        if (mw.exceptions[j] != readUnsignedShort(exception)) {
+                            sameExceptions = false;
+                            break;
+                        }
+                    }
+                }
+                if (sameExceptions) {
+                    /*
+                     * we do not copy directly the code into MethodWriter to
+                     * save a byte array copy operation. The real copy will be
+                     * done in ClassWriter.toByteArray().
+                     */
+                    mw.classReaderOffset = firstAttribute;
+                    mw.classReaderLength = u - firstAttribute;
+                    return u;
+                }
+            }
+        }
+
+        // visits the method annotations
+        if (ANNOTATIONS && dann != 0) {
+            AnnotationVisitor dv = mv.visitAnnotationDefault();
+            readAnnotationValue(dann, c, null, dv);
+            if (dv != null) {
+                dv.visitEnd();
+            }
+        }
+        if (ANNOTATIONS && anns != 0) {
+            for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) {
+                v = readAnnotationValues(v + 2, c, true,
+                        mv.visitAnnotation(readUTF8(v, c), true));
+            }
+        }
+        if (ANNOTATIONS && ianns != 0) {
+            for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) {
+                v = readAnnotationValues(v + 2, c, true,
+                        mv.visitAnnotation(readUTF8(v, c), false));
+            }
+        }
+        if (ANNOTATIONS && mpanns != 0) {
+            readParameterAnnotations(mpanns, desc, c, true, mv);
+        }
+        if (ANNOTATIONS && impanns != 0) {
+            readParameterAnnotations(impanns, desc, c, false, mv);
+        }
+
+        // visits the method attributes
+        while (attributes != null) {
+            Attribute attr = attributes.next;
+            attributes.next = null;
+            mv.visitAttribute(attributes);
+            attributes = attr;
+        }
+
+        // visits the method code
+        if (code != 0) {
+            context.access = access;
+            context.name = name;
+            context.desc = desc;
+            mv.visitCode();
+            readCode(mv, context, code);
+        }
+
+        // visits the end of the method
+        mv.visitEnd();
+
+        return u;
+    }
+
+    /**
+     * Reads the bytecode of a method and makes the given visitor visit it.
+     *
+     * @param mv
+     *            the visitor that must visit the method's code.
+     * @param context
+     *            information about the class being parsed.
+     * @param u
+     *            the start offset of the code attribute in the class file.
+     */
+    private void readCode(final MethodVisitor mv, final Context context, int u) {
+        // reads the header
+        byte[] b = this.b;
+        char[] c = context.buffer;
+        int maxStack = readUnsignedShort(u);
+        int maxLocals = readUnsignedShort(u + 2);
+        int codeLength = readInt(u + 4);
+        u += 8;
+
+        // reads the bytecode to find the labels
+        int codeStart = u;
+        int codeEnd = u + codeLength;
+        Label[] labels = new Label[codeLength + 2];
+        readLabel(codeLength + 1, labels);
+        while (u < codeEnd) {
+            int offset = u - codeStart;
+            int opcode = b[u] & 0xFF;
+            switch (ClassWriter.TYPE[opcode]) {
+            case ClassWriter.NOARG_INSN:
+            case ClassWriter.IMPLVAR_INSN:
+                u += 1;
+                break;
+            case ClassWriter.LABEL_INSN:
+                readLabel(offset + readShort(u + 1), labels);
+                u += 3;
+                break;
+            case ClassWriter.LABELW_INSN:
+                readLabel(offset + readInt(u + 1), labels);
+                u += 5;
+                break;
+            case ClassWriter.WIDE_INSN:
+                opcode = b[u + 1] & 0xFF;
+                if (opcode == Opcodes.IINC) {
+                    u += 6;
+                } else {
+                    u += 4;
+                }
+                break;
+            case ClassWriter.TABL_INSN:
+                // skips 0 to 3 padding bytes
+                u = u + 4 - (offset & 3);
+                // reads instruction
+                readLabel(offset + readInt(u), labels);
+                for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) {
+                    readLabel(offset + readInt(u + 12), labels);
+                    u += 4;
+                }
+                u += 12;
+                break;
+            case ClassWriter.LOOK_INSN:
+                // skips 0 to 3 padding bytes
+                u = u + 4 - (offset & 3);
+                // reads instruction
+                readLabel(offset + readInt(u), labels);
+                for (int i = readInt(u + 4); i > 0; --i) {
+                    readLabel(offset + readInt(u + 12), labels);
+                    u += 8;
+                }
+                u += 8;
+                break;
+            case ClassWriter.VAR_INSN:
+            case ClassWriter.SBYTE_INSN:
+            case ClassWriter.LDC_INSN:
+                u += 2;
+                break;
+            case ClassWriter.SHORT_INSN:
+            case ClassWriter.LDCW_INSN:
+            case ClassWriter.FIELDORMETH_INSN:
+            case ClassWriter.TYPE_INSN:
+            case ClassWriter.IINC_INSN:
+                u += 3;
+                break;
+            case ClassWriter.ITFMETH_INSN:
+            case ClassWriter.INDYMETH_INSN:
+                u += 5;
+                break;
+            // case MANA_INSN:
+            default:
+                u += 4;
+                break;
+            }
+        }
+
+        // reads the try catch entries to find the labels, and also visits them
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            Label start = readLabel(readUnsignedShort(u + 2), labels);
+            Label end = readLabel(readUnsignedShort(u + 4), labels);
+            Label handler = readLabel(readUnsignedShort(u + 6), labels);
+            String type = readUTF8(items[readUnsignedShort(u + 8)], c);
+            mv.visitTryCatchBlock(start, end, handler, type);
+            u += 8;
+        }
+        u += 2;
+
+        // reads the code attributes
+        int varTable = 0;
+        int varTypeTable = 0;
+        boolean zip = true;
+        boolean unzip = (context.flags & EXPAND_FRAMES) != 0;
+        int stackMap = 0;
+        int stackMapSize = 0;
+        int frameCount = 0;
+        Context frame = null;
+        Attribute attributes = null;
+
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            String attrName = readUTF8(u + 2, c);
+            if ("LocalVariableTable".equals(attrName)) {
+                if ((context.flags & SKIP_DEBUG) == 0) {
+                    varTable = u + 8;
+                    for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
+                        int label = readUnsignedShort(v + 10);
+                        if (labels[label] == null) {
+                            readLabel(label, labels).status |= Label.DEBUG;
+                        }
+                        label += readUnsignedShort(v + 12);
+                        if (labels[label] == null) {
+                            readLabel(label, labels).status |= Label.DEBUG;
+                        }
+                        v += 10;
+                    }
+                }
+            } else if ("LocalVariableTypeTable".equals(attrName)) {
+                varTypeTable = u + 8;
+            } else if ("LineNumberTable".equals(attrName)) {
+                if ((context.flags & SKIP_DEBUG) == 0) {
+                    for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) {
+                        int label = readUnsignedShort(v + 10);
+                        if (labels[label] == null) {
+                            readLabel(label, labels).status |= Label.DEBUG;
+                        }
+                        labels[label].line = readUnsignedShort(v + 12);
+                        v += 4;
+                    }
+                }
+            } else if (FRAMES && "StackMapTable".equals(attrName)) {
+                if ((context.flags & SKIP_FRAMES) == 0) {
+                    stackMap = u + 10;
+                    stackMapSize = readInt(u + 4);
+                    frameCount = readUnsignedShort(u + 8);
+                }
+                /*
+                 * here we do not extract the labels corresponding to the
+                 * attribute content. This would require a full parsing of the
+                 * attribute, which would need to be repeated in the second
+                 * phase (see below). Instead the content of the attribute is
+                 * read one frame at a time (i.e. after a frame has been
+                 * visited, the next frame is read), and the labels it contains
+                 * are also extracted one frame at a time. Thanks to the
+                 * ordering of frames, having only a "one frame lookahead" is
+                 * not a problem, i.e. it is not possible to see an offset
+                 * smaller than the offset of the current insn and for which no
+                 * Label exist.
+                 */
+                /*
+                 * This is not true for UNINITIALIZED type offsets. We solve
+                 * this by parsing the stack map table without a full decoding
+                 * (see below).
+                 */
+            } else if (FRAMES && "StackMap".equals(attrName)) {
+                if ((context.flags & SKIP_FRAMES) == 0) {
+                    zip = false;
+                    stackMap = u + 10;
+                    stackMapSize = readInt(u + 4);
+                    frameCount = readUnsignedShort(u + 8);
+                }
+                /*
+                 * IMPORTANT! here we assume that the frames are ordered, as in
+                 * the StackMapTable attribute, although this is not guaranteed
+                 * by the attribute format.
+                 */
+            } else {
+                for (int j = 0; j < context.attrs.length; ++j) {
+                    if (context.attrs[j].type.equals(attrName)) {
+                        Attribute attr = context.attrs[j].read(this, u + 8,
+                                readInt(u + 4), c, codeStart - 8, labels);
+                        if (attr != null) {
+                            attr.next = attributes;
+                            attributes = attr;
+                        }
+                    }
+                }
+            }
+            u += 6 + readInt(u + 4);
+        }
+        u += 2;
+
+        // generates the first (implicit) stack map frame
+        if (FRAMES && stackMap != 0) {
+            /*
+             * for the first explicit frame the offset is not offset_delta + 1
+             * but only offset_delta; setting the implicit frame offset to -1
+             * allow the use of the "offset_delta + 1" rule in all cases
+             */
+            frame = context;
+            frame.offset = -1;
+            frame.mode = 0;
+            frame.localCount = 0;
+            frame.localDiff = 0;
+            frame.stackCount = 0;
+            frame.local = new Object[maxLocals];
+            frame.stack = new Object[maxStack];
+            if (unzip) {
+                getImplicitFrame(context);
+            }
+            /*
+             * Finds labels for UNINITIALIZED frame types. Instead of decoding
+             * each element of the stack map table, we look for 3 consecutive
+             * bytes that "look like" an UNINITIALIZED type (tag 8, offset
+             * within code bounds, NEW instruction at this offset). We may find
+             * false positives (i.e. not real UNINITIALIZED types), but this
+             * should be rare, and the only consequence will be the creation of
+             * an unneeded label. This is better than creating a label for each
+             * NEW instruction, and faster than fully decoding the whole stack
+             * map table.
+             */
+            for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) {
+                if (b[i] == 8) { // UNINITIALIZED FRAME TYPE
+                    int v = readUnsignedShort(i + 1);
+                    if (v >= 0 && v < codeLength) {
+                        if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) {
+                            readLabel(v, labels);
+                        }
+                    }
+                }
+            }
+        }
+
+        // visits the instructions
+        u = codeStart;
+        while (u < codeEnd) {
+            int offset = u - codeStart;
+
+            // visits the label and line number for this offset, if any
+            Label l = labels[offset];
+            if (l != null) {
+                mv.visitLabel(l);
+                if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) {
+                    mv.visitLineNumber(l.line, l);
+                }
+            }
+
+            // visits the frame for this offset, if any
+            while (FRAMES && frame != null
+                    && (frame.offset == offset || frame.offset == -1)) {
+                // if there is a frame for this offset, makes the visitor visit
+                // it, and reads the next frame if there is one.
+                if (frame.offset != -1) {
+                    if (!zip || unzip) {
+                        mv.visitFrame(Opcodes.F_NEW, frame.localCount,
+                                frame.local, frame.stackCount, frame.stack);
+                    } else {
+                        mv.visitFrame(frame.mode, frame.localDiff, frame.local,
+                                frame.stackCount, frame.stack);
+                    }
+                }
+                if (frameCount > 0) {
+                    stackMap = readFrame(stackMap, zip, unzip, labels, frame);
+                    --frameCount;
+                } else {
+                    frame = null;
+                }
+            }
+
+            // visits the instruction at this offset
+            int opcode = b[u] & 0xFF;
+            switch (ClassWriter.TYPE[opcode]) {
+            case ClassWriter.NOARG_INSN:
+                mv.visitInsn(opcode);
+                u += 1;
+                break;
+            case ClassWriter.IMPLVAR_INSN:
+                if (opcode > Opcodes.ISTORE) {
+                    opcode -= 59; // ISTORE_0
+                    mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2),
+                            opcode & 0x3);
+                } else {
+                    opcode -= 26; // ILOAD_0
+                    mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3);
+                }
+                u += 1;
+                break;
+            case ClassWriter.LABEL_INSN:
+                mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]);
+                u += 3;
+                break;
+            case ClassWriter.LABELW_INSN:
+                mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]);
+                u += 5;
+                break;
+            case ClassWriter.WIDE_INSN:
+                opcode = b[u + 1] & 0xFF;
+                if (opcode == Opcodes.IINC) {
+                    mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4));
+                    u += 6;
+                } else {
+                    mv.visitVarInsn(opcode, readUnsignedShort(u + 2));
+                    u += 4;
+                }
+                break;
+            case ClassWriter.TABL_INSN: {
+                // skips 0 to 3 padding bytes
+                u = u + 4 - (offset & 3);
+                // reads instruction
+                int label = offset + readInt(u);
+                int min = readInt(u + 4);
+                int max = readInt(u + 8);
+                Label[] table = new Label[max - min + 1];
+                u += 12;
+                for (int i = 0; i < table.length; ++i) {
+                    table[i] = labels[offset + readInt(u)];
+                    u += 4;
+                }
+                mv.visitTableSwitchInsn(min, max, labels[label], table);
+                break;
+            }
+            case ClassWriter.LOOK_INSN: {
+                // skips 0 to 3 padding bytes
+                u = u + 4 - (offset & 3);
+                // reads instruction
+                int label = offset + readInt(u);
+                int len = readInt(u + 4);
+                int[] keys = new int[len];
+                Label[] values = new Label[len];
+                u += 8;
+                for (int i = 0; i < len; ++i) {
+                    keys[i] = readInt(u);
+                    values[i] = labels[offset + readInt(u + 4)];
+                    u += 8;
+                }
+                mv.visitLookupSwitchInsn(labels[label], keys, values);
+                break;
+            }
+            case ClassWriter.VAR_INSN:
+                mv.visitVarInsn(opcode, b[u + 1] & 0xFF);
+                u += 2;
+                break;
+            case ClassWriter.SBYTE_INSN:
+                mv.visitIntInsn(opcode, b[u + 1]);
+                u += 2;
+                break;
+            case ClassWriter.SHORT_INSN:
+                mv.visitIntInsn(opcode, readShort(u + 1));
+                u += 3;
+                break;
+            case ClassWriter.LDC_INSN:
+                mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c));
+                u += 2;
+                break;
+            case ClassWriter.LDCW_INSN:
+                mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c));
+                u += 3;
+                break;
+            case ClassWriter.FIELDORMETH_INSN:
+            case ClassWriter.ITFMETH_INSN: {
+                int cpIndex = items[readUnsignedShort(u + 1)];
+                String iowner = readClass(cpIndex, c);
+                cpIndex = items[readUnsignedShort(cpIndex + 2)];
+                String iname = readUTF8(cpIndex, c);
+                String idesc = readUTF8(cpIndex + 2, c);
+                if (opcode < Opcodes.INVOKEVIRTUAL) {
+                    mv.visitFieldInsn(opcode, iowner, iname, idesc);
+                } else {
+                    mv.visitMethodInsn(opcode, iowner, iname, idesc);
+                }
+                if (opcode == Opcodes.INVOKEINTERFACE) {
+                    u += 5;
+                } else {
+                    u += 3;
+                }
+                break;
+            }
+            case ClassWriter.INDYMETH_INSN: {
+                int cpIndex = items[readUnsignedShort(u + 1)];
+                int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)];
+                Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c);
+                int bsmArgCount = readUnsignedShort(bsmIndex + 2);
+                Object[] bsmArgs = new Object[bsmArgCount];
+                bsmIndex += 4;
+                for (int i = 0; i < bsmArgCount; i++) {
+                    bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c);
+                    bsmIndex += 2;
+                }
+                cpIndex = items[readUnsignedShort(cpIndex + 2)];
+                String iname = readUTF8(cpIndex, c);
+                String idesc = readUTF8(cpIndex + 2, c);
+                mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs);
+                u += 5;
+                break;
+            }
+            case ClassWriter.TYPE_INSN:
+                mv.visitTypeInsn(opcode, readClass(u + 1, c));
+                u += 3;
+                break;
+            case ClassWriter.IINC_INSN:
+                mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]);
+                u += 3;
+                break;
+            // case MANA_INSN:
+            default:
+                mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF);
+                u += 4;
+                break;
+            }
+        }
+        if (labels[codeLength] != null) {
+            mv.visitLabel(labels[codeLength]);
+        }
+
+        // visits the local variable tables
+        if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) {
+            int[] typeTable = null;
+            if (varTypeTable != 0) {
+                u = varTypeTable + 2;
+                typeTable = new int[readUnsignedShort(varTypeTable) * 3];
+                for (int i = typeTable.length; i > 0;) {
+                    typeTable[--i] = u + 6; // signature
+                    typeTable[--i] = readUnsignedShort(u + 8); // index
+                    typeTable[--i] = readUnsignedShort(u); // start
+                    u += 10;
+                }
+            }
+            u = varTable + 2;
+            for (int i = readUnsignedShort(varTable); i > 0; --i) {
+                int start = readUnsignedShort(u);
+                int length = readUnsignedShort(u + 2);
+                int index = readUnsignedShort(u + 8);
+                String vsignature = null;
+                if (typeTable != null) {
+                    for (int j = 0; j < typeTable.length; j += 3) {
+                        if (typeTable[j] == start && typeTable[j + 1] == index) {
+                            vsignature = readUTF8(typeTable[j + 2], c);
+                            break;
+                        }
+                    }
+                }
+                mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c),
+                        vsignature, labels[start], labels[start + length],
+                        index);
+                u += 10;
+            }
+        }
+
+        // visits the code attributes
+        while (attributes != null) {
+            Attribute attr = attributes.next;
+            attributes.next = null;
+            mv.visitAttribute(attributes);
+            attributes = attr;
+        }
+
+        // visits the max stack and max locals values
+        mv.visitMaxs(maxStack, maxLocals);
+    }
+
+    /**
+     * Reads parameter annotations and makes the given visitor visit them.
+     *
+     * @param v
+     *            start offset in {@link #b b} of the annotations to be read.
+     * @param desc
+     *            the method descriptor.
+     * @param buf
+     *            buffer to be used to call {@link #readUTF8 readUTF8},
+     *            {@link #readClass(int,char[]) readClass} or {@link #readConst
+     *            readConst}.
+     * @param visible
+     *            <tt>true</tt> if the annotations to be read are visible at
+     *            runtime.
+     * @param mv
+     *            the visitor that must visit the annotations.
+     */
+    private void readParameterAnnotations(int v, final String desc,
+            final char[] buf, final boolean visible, final MethodVisitor mv) {
+        int i;
+        int n = b[v++] & 0xFF;
+        // workaround for a bug in javac (javac compiler generates a parameter
+        // annotation array whose size is equal to the number of parameters in
+        // the Java source file, while it should generate an array whose size is
+        // equal to the number of parameters in the method descriptor - which
+        // includes the synthetic parameters added by the compiler). This work-
+        // around supposes that the synthetic parameters are the first ones.
+        int synthetics = Type.getArgumentTypes(desc).length - n;
+        AnnotationVisitor av;
+        for (i = 0; i < synthetics; ++i) {
+            // virtual annotation to detect synthetic parameters in MethodWriter
+            av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false);
+            if (av != null) {
+                av.visitEnd();
+            }
+        }
+        for (; i < n + synthetics; ++i) {
+            int j = readUnsignedShort(v);
+            v += 2;
+            for (; j > 0; --j) {
+                av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible);
+                v = readAnnotationValues(v + 2, buf, true, av);
+            }
+        }
+    }
+
+    /**
+     * Reads the values of an annotation and makes the given visitor visit them.
+     *
+     * @param v
+     *            the start offset in {@link #b b} of the values to be read
+     *            (including the unsigned short that gives the number of
+     *            values).
+     * @param buf
+     *            buffer to be used to call {@link #readUTF8 readUTF8},
+     *            {@link #readClass(int,char[]) readClass} or {@link #readConst
+     *            readConst}.
+     * @param named
+     *            if the annotation values are named or not.
+     * @param av
+     *            the visitor that must visit the values.
+     * @return the end offset of the annotation values.
+     */
+    private int readAnnotationValues(int v, final char[] buf,
+            final boolean named, final AnnotationVisitor av) {
+        int i = readUnsignedShort(v);
+        v += 2;
+        if (named) {
+            for (; i > 0; --i) {
+                v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av);
+            }
+        } else {
+            for (; i > 0; --i) {
+                v = readAnnotationValue(v, buf, null, av);
+            }
+        }
+        if (av != null) {
+            av.visitEnd();
+        }
+        return v;
+    }
+
+    /**
+     * Reads a value of an annotation and makes the given visitor visit it.
+     *
+     * @param v
+     *            the start offset in {@link #b b} of the value to be read
+     *            (<i>not including the value name constant pool index</i>).
+     * @param buf
+     *            buffer to be used to call {@link #readUTF8 readUTF8},
+     *            {@link #readClass(int,char[]) readClass} or {@link #readConst
+     *            readConst}.
+     * @param name
+     *            the name of the value to be read.
+     * @param av
+     *            the visitor that must visit the value.
+     * @return the end offset of the annotation value.
+     */
+    private int readAnnotationValue(int v, final char[] buf, final String name,
+            final AnnotationVisitor av) {
+        int i;
+        if (av == null) {
+            switch (b[v] & 0xFF) {
+            case 'e': // enum_const_value
+                return v + 5;
+            case '@': // annotation_value
+                return readAnnotationValues(v + 3, buf, true, null);
+            case '[': // array_value
+                return readAnnotationValues(v + 1, buf, false, null);
+            default:
+                return v + 3;
+            }
+        }
+        switch (b[v++] & 0xFF) {
+        case 'I': // pointer to CONSTANT_Integer
+        case 'J': // pointer to CONSTANT_Long
+        case 'F': // pointer to CONSTANT_Float
+        case 'D': // pointer to CONSTANT_Double
+            av.visit(name, readConst(readUnsignedShort(v), buf));
+            v += 2;
+            break;
+        case 'B': // pointer to CONSTANT_Byte
+            av.visit(name,
+                    new Byte((byte) readInt(items[readUnsignedShort(v)])));
+            v += 2;
+            break;
+        case 'Z': // pointer to CONSTANT_Boolean
+            av.visit(name,
+                    readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE
+                            : Boolean.TRUE);
+            v += 2;
+            break;
+        case 'S': // pointer to CONSTANT_Short
+            av.visit(name, new Short(
+                    (short) readInt(items[readUnsignedShort(v)])));
+            v += 2;
+            break;
+        case 'C': // pointer to CONSTANT_Char
+            av.visit(name, new Character(
+                    (char) readInt(items[readUnsignedShort(v)])));
+            v += 2;
+            break;
+        case 's': // pointer to CONSTANT_Utf8
+            av.visit(name, readUTF8(v, buf));
+            v += 2;
+            break;
+        case 'e': // enum_const_value
+            av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf));
+            v += 4;
+            break;
+        case 'c': // class_info
+            av.visit(name, Type.getType(readUTF8(v, buf)));
+            v += 2;
+            break;
+        case '@': // annotation_value
+            v = readAnnotationValues(v + 2, buf, true,
+                    av.visitAnnotation(name, readUTF8(v, buf)));
+            break;
+        case '[': // array_value
+            int size = readUnsignedShort(v);
+            v += 2;
+            if (size == 0) {
+                return readAnnotationValues(v - 2, buf, false,
+                        av.visitArray(name));
+            }
+            switch (this.b[v++] & 0xFF) {
+            case 'B':
+                byte[] bv = new byte[size];
+                for (i = 0; i < size; i++) {
+                    bv[i] = (byte) readInt(items[readUnsignedShort(v)]);
+                    v += 3;
+                }
+                av.visit(name, bv);
+                --v;
+                break;
+            case 'Z':
+                boolean[] zv = new boolean[size];
+                for (i = 0; i < size; i++) {
+                    zv[i] = readInt(items[readUnsignedShort(v)]) != 0;
+                    v += 3;
+                }
+                av.visit(name, zv);
+                --v;
+                break;
+            case 'S':
+                short[] sv = new short[size];
+                for (i = 0; i < size; i++) {
+                    sv[i] = (short) readInt(items[readUnsignedShort(v)]);
+                    v += 3;
+                }
+                av.visit(name, sv);
+                --v;
+                break;
+            case 'C':
+                char[] cv = new char[size];
+                for (i = 0; i < size; i++) {
+                    cv[i] = (char) readInt(items[readUnsignedShort(v)]);
+                    v += 3;
+                }
+                av.visit(name, cv);
+                --v;
+                break;
+            case 'I':
+                int[] iv = new int[size];
+                for (i = 0; i < size; i++) {
+                    iv[i] = readInt(items[readUnsignedShort(v)]);
+                    v += 3;
+                }
+                av.visit(name, iv);
+                --v;
+                break;
+            case 'J':
+                long[] lv = new long[size];
+                for (i = 0; i < size; i++) {
+                    lv[i] = readLong(items[readUnsignedShort(v)]);
+                    v += 3;
+                }
+                av.visit(name, lv);
+                --v;
+                break;
+            case 'F':
+                float[] fv = new float[size];
+                for (i = 0; i < size; i++) {
+                    fv[i] = Float
+                            .intBitsToFloat(readInt(items[readUnsignedShort(v)]));
+                    v += 3;
+                }
+                av.visit(name, fv);
+                --v;
+                break;
+            case 'D':
+                double[] dv = new double[size];
+                for (i = 0; i < size; i++) {
+                    dv[i] = Double
+                            .longBitsToDouble(readLong(items[readUnsignedShort(v)]));
+                    v += 3;
+                }
+                av.visit(name, dv);
+                --v;
+                break;
+            default:
+                v = readAnnotationValues(v - 3, buf, false, av.visitArray(name));
+            }
+        }
+        return v;
+    }
+
+    /**
+     * Computes the implicit frame of the method currently being parsed (as
+     * defined in the given {@link Context}) and stores it in the given context.
+     *
+     * @param frame
+     *            information about the class being parsed.
+     */
+    private void getImplicitFrame(final Context frame) {
+        String desc = frame.desc;
+        Object[] locals = frame.local;
+        int local = 0;
+        if ((frame.access & Opcodes.ACC_STATIC) == 0) {
+            if ("<init>".equals(frame.name)) {
+                locals[local++] = Opcodes.UNINITIALIZED_THIS;
+            } else {
+                locals[local++] = readClass(header + 2, frame.buffer);
+            }
+        }
+        int i = 1;
+        loop: while (true) {
+            int j = i;
+            switch (desc.charAt(i++)) {
+            case 'Z':
+            case 'C':
+            case 'B':
+            case 'S':
+            case 'I':
+                locals[local++] = Opcodes.INTEGER;
+                break;
+            case 'F':
+                locals[local++] = Opcodes.FLOAT;
+                break;
+            case 'J':
+                locals[local++] = Opcodes.LONG;
+                break;
+            case 'D':
+                locals[local++] = Opcodes.DOUBLE;
+                break;
+            case '[':
+                while (desc.charAt(i) == '[') {
+                    ++i;
+                }
+                if (desc.charAt(i) == 'L') {
+                    ++i;
+                    while (desc.charAt(i) != ';') {
+                        ++i;
+                    }
+                }
+                locals[local++] = desc.substring(j, ++i);
+                break;
+            case 'L':
+                while (desc.charAt(i) != ';') {
+                    ++i;
+                }
+                locals[local++] = desc.substring(j + 1, i++);
+                break;
+            default:
+                break loop;
+            }
+        }
+        frame.localCount = local;
+    }
+
+    /**
+     * Reads a stack map frame and stores the result in the given
+     * {@link Context} object.
+     *
+     * @param stackMap
+     *            the start offset of a stack map frame in the class file.
+     * @param zip
+     *            if the stack map frame at stackMap is compressed or not.
+     * @param unzip
+     *            if the stack map frame must be uncompressed.
+     * @param labels
+     *            the labels of the method currently being parsed, indexed by
+     *            their offset. A new label for the parsed stack map frame is
+     *            stored in this array if it does not already exist.
+     * @param frame
+     *            where the parsed stack map frame must be stored.
+     * @return the offset of the first byte following the parsed frame.
+     */
+    private int readFrame(int stackMap, boolean zip, boolean unzip,
+            Label[] labels, Context frame) {
+        char[] c = frame.buffer;
+        int tag;
+        int delta;
+        if (zip) {
+            tag = b[stackMap++] & 0xFF;
+        } else {
+            tag = MethodWriter.FULL_FRAME;
+            frame.offset = -1;
+        }
+        frame.localDiff = 0;
+        if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) {
+            delta = tag;
+            frame.mode = Opcodes.F_SAME;
+            frame.stackCount = 0;
+        } else if (tag < MethodWriter.RESERVED) {
+            delta = tag - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME;
+            stackMap = readFrameType(frame.stack, 0, stackMap, c, labels);
+            frame.mode = Opcodes.F_SAME1;
+            frame.stackCount = 1;
+        } else {
+            delta = readUnsignedShort(stackMap);
+            stackMap += 2;
+            if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {
+                stackMap = readFrameType(frame.stack, 0, stackMap, c, labels);
+                frame.mode = Opcodes.F_SAME1;
+                frame.stackCount = 1;
+            } else if (tag >= MethodWriter.CHOP_FRAME
+                    && tag < MethodWriter.SAME_FRAME_EXTENDED) {
+                frame.mode = Opcodes.F_CHOP;
+                frame.localDiff = MethodWriter.SAME_FRAME_EXTENDED - tag;
+                frame.localCount -= frame.localDiff;
+                frame.stackCount = 0;
+            } else if (tag == MethodWriter.SAME_FRAME_EXTENDED) {
+                frame.mode = Opcodes.F_SAME;
+                frame.stackCount = 0;
+            } else if (tag < MethodWriter.FULL_FRAME) {
+                int local = unzip ? frame.localCount : 0;
+                for (int i = tag - MethodWriter.SAME_FRAME_EXTENDED; i > 0; i--) {
+                    stackMap = readFrameType(frame.local, local++, stackMap, c,
+                            labels);
+                }
+                frame.mode = Opcodes.F_APPEND;
+                frame.localDiff = tag - MethodWriter.SAME_FRAME_EXTENDED;
+                frame.localCount += frame.localDiff;
+                frame.stackCount = 0;
+            } else { // if (tag == FULL_FRAME) {
+                frame.mode = Opcodes.F_FULL;
+                int n = readUnsignedShort(stackMap);
+                stackMap += 2;
+                frame.localDiff = n;
+                frame.localCount = n;
+                for (int local = 0; n > 0; n--) {
+                    stackMap = readFrameType(frame.local, local++, stackMap, c,
+                            labels);
+                }
+                n = readUnsignedShort(stackMap);
+                stackMap += 2;
+                frame.stackCount = n;
+                for (int stack = 0; n > 0; n--) {
+                    stackMap = readFrameType(frame.stack, stack++, stackMap, c,
+                            labels);
+                }
+            }
+        }
+        frame.offset += delta + 1;
+        readLabel(frame.offset, labels);
+        return stackMap;
+    }
+
+    /**
+     * Reads a stack map frame type and stores it at the given index in the
+     * given array.
+     *
+     * @param frame
+     *            the array where the parsed type must be stored.
+     * @param index
+     *            the index in 'frame' where the parsed type must be stored.
+     * @param v
+     *            the start offset of the stack map frame type to read.
+     * @param buf
+     *            a buffer to read strings.
+     * @param labels
+     *            the labels of the method currently being parsed, indexed by
+     *            their offset. If the parsed type is an Uninitialized type, a
+     *            new label for the corresponding NEW instruction is stored in
+     *            this array if it does not already exist.
+     * @return the offset of the first byte after the parsed type.
+     */
+    private int readFrameType(final Object[] frame, final int index, int v,
+            final char[] buf, final Label[] labels) {
+        int type = b[v++] & 0xFF;
+        switch (type) {
+        case 0:
+            frame[index] = Opcodes.TOP;
+            break;
+        case 1:
+            frame[index] = Opcodes.INTEGER;
+            break;
+        case 2:
+            frame[index] = Opcodes.FLOAT;
+            break;
+        case 3:
+            frame[index] = Opcodes.DOUBLE;
+            break;
+        case 4:
+            frame[index] = Opcodes.LONG;
+            break;
+        case 5:
+            frame[index] = Opcodes.NULL;
+            break;
+        case 6:
+            frame[index] = Opcodes.UNINITIALIZED_THIS;
+            break;
+        case 7: // Object
+            frame[index] = readClass(v, buf);
+            v += 2;
+            break;
+        default: // Uninitialized
+            frame[index] = readLabel(readUnsignedShort(v), labels);
+            v += 2;
+        }
+        return v;
+    }
+
+    /**
+     * Returns the label corresponding to the given offset. The default
+     * implementation of this method creates a label for the given offset if it
+     * has not been already created.
+     *
+     * @param offset
+     *            a bytecode offset in a method.
+     * @param labels
+     *            the already created labels, indexed by their offset. If a
+     *            label already exists for offset this method must not create a
+     *            new one. Otherwise it must store the new label in this array.
+     * @return a non null Label, which must be equal to labels[offset].
+     */
+    protected Label readLabel(int offset, Label[] labels) {
+        if (labels[offset] == null) {
+            labels[offset] = new Label();
+        }
+        return labels[offset];
+    }
+
+    /**
+     * Returns the start index of the attribute_info structure of this class.
+     *
+     * @return the start index of the attribute_info structure of this class.
+     */
+    private int getAttributes() {
+        // skips the header
+        int u = header + 8 + readUnsignedShort(header + 6) * 2;
+        // skips fields and methods
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            for (int j = readUnsignedShort(u + 8); j > 0; --j) {
+                u += 6 + readInt(u + 12);
+            }
+            u += 8;
+        }
+        u += 2;
+        for (int i = readUnsignedShort(u); i > 0; --i) {
+            for (int j = readUnsignedShort(u + 8); j > 0; --j) {
+                u += 6 + readInt(u + 12);
+            }
+            u += 8;
+        }
+        // the attribute_info structure starts just after the methods
+        return u + 2;
+    }
+
+    /**
+     * Reads an attribute in {@link #b b}.
+     *
+     * @param attrs
+     *            prototypes of the attributes that must be parsed during the
+     *            visit of the class. Any attribute whose type is not equal to
+     *            the type of one the prototypes is ignored (i.e. an empty
+     *            {@link Attribute} instance is returned).
+     * @param type
+     *            the type of the attribute.
+     * @param off
+     *            index of the first byte of the attribute's content in
+     *            {@link #b b}. The 6 attribute header bytes, containing the
+     *            type and the length of the attribute, are not taken into
+     *            account here (they have already been read).
+     * @param len
+     *            the length of the attribute's content.
+     * @param buf
+     *            buffer to be used to call {@link #readUTF8 readUTF8},
+     *            {@link #readClass(int,char[]) readClass} or {@link #readConst
+     *            readConst}.
+     * @param codeOff
+     *            index of the first byte of code's attribute content in
+     *            {@link #b b}, or -1 if the attribute to be read is not a code
+     *            attribute. The 6 attribute header bytes, containing the type
+     *            and the length of the attribute, are not taken into account
+     *            here.
+     * @param labels
+     *            the labels of the method's code, or <tt>null</tt> if the
+     *            attribute to be read is not a code attribute.
+     * @return the attribute that has been read, or <tt>null</tt> to skip this
+     *         attribute.
+     */
+    private Attribute readAttribute(final Attribute[] attrs, final String type,
+            final int off, final int len, final char[] buf, final int codeOff,
+            final Label[] labels) {
+        for (int i = 0; i < attrs.length; ++i) {
+            if (attrs[i].type.equals(type)) {
+                return attrs[i].read(this, off, len, buf, codeOff, labels);
+            }
+        }
+        return new Attribute(type).read(this, off, len, null, -1, null);
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods: low level parsing
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the number of constant pool items in {@link #b b}.
+     *
+     * @return the number of constant pool items in {@link #b b}.
+     */
+    public int getItemCount() {
+        return items.length;
+    }
+
+    /**
+     * Returns the start index of the constant pool item in {@link #b b}, plus
+     * one. <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param item
+     *            the index a constant pool item.
+     * @return the start index of the constant pool item in {@link #b b}, plus
+     *         one.
+     */
+    public int getItem(final int item) {
+        return items[item];
+    }
+
+    /**
+     * Returns the maximum length of the strings contained in the constant pool
+     * of the class.
+     *
+     * @return the maximum length of the strings contained in the constant pool
+     *         of the class.
+     */
+    public int getMaxStringLength() {
+        return maxStringLength;
+    }
+
+    /**
+     * Reads a byte value in {@link #b b}. <i>This method is intended for
+     * {@link Attribute} sub classes, and is normally not needed by class
+     * generators or adapters.</i>
+     *
+     * @param index
+     *            the start index of the value to be read in {@link #b b}.
+     * @return the read value.
+     */
+    public int readByte(final int index) {
+        return b[index] & 0xFF;
+    }
+
+    /**
+     * Reads an unsigned short value in {@link #b b}. <i>This method is intended
+     * for {@link Attribute} sub classes, and is normally not needed by class
+     * generators or adapters.</i>
+     *
+     * @param index
+     *            the start index of the value to be read in {@link #b b}.
+     * @return the read value.
+     */
+    public int readUnsignedShort(final int index) {
+        byte[] b = this.b;
+        return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
+    }
+
+    /**
+     * Reads a signed short value in {@link #b b}. <i>This method is intended
+     * for {@link Attribute} sub classes, and is normally not needed by class
+     * generators or adapters.</i>
+     *
+     * @param index
+     *            the start index of the value to be read in {@link #b b}.
+     * @return the read value.
+     */
+    public short readShort(final int index) {
+        byte[] b = this.b;
+        return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
+    }
+
+    /**
+     * Reads a signed int value in {@link #b b}. <i>This method is intended for
+     * {@link Attribute} sub classes, and is normally not needed by class
+     * generators or adapters.</i>
+     *
+     * @param index
+     *            the start index of the value to be read in {@link #b b}.
+     * @return the read value.
+     */
+    public int readInt(final int index) {
+        byte[] b = this.b;
+        return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
+                | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
+    }
+
+    /**
+     * Reads a signed long value in {@link #b b}. <i>This method is intended for
+     * {@link Attribute} sub classes, and is normally not needed by class
+     * generators or adapters.</i>
+     *
+     * @param index
+     *            the start index of the value to be read in {@link #b b}.
+     * @return the read value.
+     */
+    public long readLong(final int index) {
+        long l1 = readInt(index);
+        long l0 = readInt(index + 4) & 0xFFFFFFFFL;
+        return (l1 << 32) | l0;
+    }
+
+    /**
+     * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method
+     * is intended for {@link Attribute} sub classes, and is normally not needed
+     * by class generators or adapters.</i>
+     *
+     * @param index
+     *            the start index of an unsigned short value in {@link #b b},
+     *            whose value is the index of an UTF8 constant pool item.
+     * @param buf
+     *            buffer to be used to read the item. This buffer must be
+     *            sufficiently large. It is not automatically resized.
+     * @return the String corresponding to the specified UTF8 item.
+     */
+    public String readUTF8(int index, final char[] buf) {
+        int item = readUnsignedShort(index);
+        if (index == 0 || item == 0) {
+            return null;
+        }
+        String s = strings[item];
+        if (s != null) {
+            return s;
+        }
+        index = items[item];
+        return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf);
+    }
+
+    /**
+     * Reads UTF8 string in {@link #b b}.
+     *
+     * @param index
+     *            start offset of the UTF8 string to be read.
+     * @param utfLen
+     *            length of the UTF8 string to be read.
+     * @param buf
+     *            buffer to be used to read the string. This buffer must be
+     *            sufficiently large. It is not automatically resized.
+     * @return the String corresponding to the specified UTF8 string.
+     */
+    private String readUTF(int index, final int utfLen, final char[] buf) {
+        int endIndex = index + utfLen;
+        byte[] b = this.b;
+        int strLen = 0;
+        int c;
+        int st = 0;
+        char cc = 0;
+        while (index < endIndex) {
+            c = b[index++];
+            switch (st) {
+            case 0:
+                c = c & 0xFF;
+                if (c < 0x80) { // 0xxxxxxx
+                    buf[strLen++] = (char) c;
+                } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx
+                    cc = (char) (c & 0x1F);
+                    st = 1;
+                } else { // 1110 xxxx 10xx xxxx 10xx xxxx
+                    cc = (char) (c & 0x0F);
+                    st = 2;
+                }
+                break;
+
+            case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char
+                buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
+                st = 0;
+                break;
+
+            case 2: // byte 2 of 3-byte char
+                cc = (char) ((cc << 6) | (c & 0x3F));
+                st = 1;
+                break;
+            }
+        }
+        return new String(buf, 0, strLen);
+    }
+
+    /**
+     * Reads a class constant pool item in {@link #b b}. <i>This method is
+     * intended for {@link Attribute} sub classes, and is normally not needed by
+     * class generators or adapters.</i>
+     *
+     * @param index
+     *            the start index of an unsigned short value in {@link #b b},
+     *            whose value is the index of a class constant pool item.
+     * @param buf
+     *            buffer to be used to read the item. This buffer must be
+     *            sufficiently large. It is not automatically resized.
+     * @return the String corresponding to the specified class item.
+     */
+    public String readClass(final int index, final char[] buf) {
+        // computes the start index of the CONSTANT_Class item in b
+        // and reads the CONSTANT_Utf8 item designated by
+        // the first two bytes of this CONSTANT_Class item
+        return readUTF8(items[readUnsignedShort(index)], buf);
+    }
+
+    /**
+     * Reads a numeric or string constant pool item in {@link #b b}. <i>This
+     * method is intended for {@link Attribute} sub classes, and is normally not
+     * needed by class generators or adapters.</i>
+     *
+     * @param item
+     *            the index of a constant pool item.
+     * @param buf
+     *            buffer to be used to read the item. This buffer must be
+     *            sufficiently large. It is not automatically resized.
+     * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double},
+     *         {@link String}, {@link Type} or {@link Handle} corresponding to
+     *         the given constant pool item.
+     */
+    public Object readConst(final int item, final char[] buf) {
+        int index = items[item];
+        switch (b[index - 1]) {
+        case ClassWriter.INT:
+            return new Integer(readInt(index));
+        case ClassWriter.FLOAT:
+            return new Float(Float.intBitsToFloat(readInt(index)));
+        case ClassWriter.LONG:
+            return new Long(readLong(index));
+        case ClassWriter.DOUBLE:
+            return new Double(Double.longBitsToDouble(readLong(index)));
+        case ClassWriter.CLASS:
+            return Type.getObjectType(readUTF8(index, buf));
+        case ClassWriter.STR:
+            return readUTF8(index, buf);
+        case ClassWriter.MTYPE:
+            return Type.getMethodType(readUTF8(index, buf));
+        default: // case ClassWriter.HANDLE_BASE + [1..9]:
+            int tag = readByte(index);
+            int[] items = this.items;
+            int cpIndex = items[readUnsignedShort(index + 1)];
+            String owner = readClass(cpIndex, buf);
+            cpIndex = items[readUnsignedShort(cpIndex + 2)];
+            String name = readUTF8(cpIndex, buf);
+            String desc = readUTF8(cpIndex + 2, buf);
+            return new Handle(tag, owner, name, desc);
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/ClassVisitor.java b/src/jvm/clojure/asm/ClassVisitor.java
new file mode 100644
index 0000000..bc5919b
--- /dev/null
+++ b/src/jvm/clojure/asm/ClassVisitor.java
@@ -0,0 +1,286 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A visitor to visit a Java class. The methods of this class must be called in
+ * the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [
+ * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* ( <tt>visitInnerClass</tt> | <tt>visitField</tt> |
+ * <tt>visitMethod</tt> )* <tt>visitEnd</tt>.
+ *
+ * @author Eric Bruneton
+ */
+public abstract class ClassVisitor {
+
+    /**
+     * The ASM API version implemented by this visitor. The value of this field
+     * must be one of {@link Opcodes#ASM4}.
+     */
+    protected final int api;
+
+    /**
+     * The class visitor to which this visitor must delegate method calls. May
+     * be null.
+     */
+    protected ClassVisitor cv;
+
+    /**
+     * Constructs a new {@link ClassVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     */
+    public ClassVisitor(final int api) {
+        this(api, null);
+    }
+
+    /**
+     * Constructs a new {@link ClassVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param cv
+     *            the class visitor to which this visitor must delegate method
+     *            calls. May be null.
+     */
+    public ClassVisitor(final int api, final ClassVisitor cv) {
+        if (api != Opcodes.ASM4) {
+            throw new IllegalArgumentException();
+        }
+        this.api = api;
+        this.cv = cv;
+    }
+
+    /**
+     * Visits the header of the class.
+     *
+     * @param version
+     *            the class version.
+     * @param access
+     *            the class's access flags (see {@link Opcodes}). This parameter
+     *            also indicates if the class is deprecated.
+     * @param name
+     *            the internal name of the class (see
+     *            {@link Type#getInternalName() getInternalName}).
+     * @param signature
+     *            the signature of this class. May be <tt>null</tt> if the class
+     *            is not a generic one, and does not extend or implement generic
+     *            classes or interfaces.
+     * @param superName
+     *            the internal of name of the super class (see
+     *            {@link Type#getInternalName() getInternalName}). For
+     *            interfaces, the super class is {@link Object}. May be
+     *            <tt>null</tt>, but only for the {@link Object} class.
+     * @param interfaces
+     *            the internal names of the class's interfaces (see
+     *            {@link Type#getInternalName() getInternalName}). May be
+     *            <tt>null</tt>.
+     */
+    public void visit(int version, int access, String name, String signature,
+            String superName, String[] interfaces) {
+        if (cv != null) {
+            cv.visit(version, access, name, signature, superName, interfaces);
+        }
+    }
+
+    /**
+     * Visits the source of the class.
+     *
+     * @param source
+     *            the name of the source file from which the class was compiled.
+     *            May be <tt>null</tt>.
+     * @param debug
+     *            additional debug information to compute the correspondance
+     *            between source and compiled elements of the class. May be
+     *            <tt>null</tt>.
+     */
+    public void visitSource(String source, String debug) {
+        if (cv != null) {
+            cv.visitSource(source, debug);
+        }
+    }
+
+    /**
+     * Visits the enclosing class of the class. This method must be called only
+     * if the class has an enclosing class.
+     *
+     * @param owner
+     *            internal name of the enclosing class of the class.
+     * @param name
+     *            the name of the method that contains the class, or
+     *            <tt>null</tt> if the class is not enclosed in a method of its
+     *            enclosing class.
+     * @param desc
+     *            the descriptor of the method that contains the class, or
+     *            <tt>null</tt> if the class is not enclosed in a method of its
+     *            enclosing class.
+     */
+    public void visitOuterClass(String owner, String name, String desc) {
+        if (cv != null) {
+            cv.visitOuterClass(owner, name, desc);
+        }
+    }
+
+    /**
+     * Visits an annotation of the class.
+     *
+     * @param desc
+     *            the class descriptor of the annotation class.
+     * @param visible
+     *            <tt>true</tt> if the annotation is visible at runtime.
+     * @return a visitor to visit the annotation values, or <tt>null</tt> if
+     *         this visitor is not interested in visiting this annotation.
+     */
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        if (cv != null) {
+            return cv.visitAnnotation(desc, visible);
+        }
+        return null;
+    }
+
+    /**
+     * Visits a non standard attribute of the class.
+     *
+     * @param attr
+     *            an attribute.
+     */
+    public void visitAttribute(Attribute attr) {
+        if (cv != null) {
+            cv.visitAttribute(attr);
+        }
+    }
+
+    /**
+     * Visits information about an inner class. This inner class is not
+     * necessarily a member of the class being visited.
+     *
+     * @param name
+     *            the internal name of an inner class (see
+     *            {@link Type#getInternalName() getInternalName}).
+     * @param outerName
+     *            the internal name of the class to which the inner class
+     *            belongs (see {@link Type#getInternalName() getInternalName}).
+     *            May be <tt>null</tt> for not member classes.
+     * @param innerName
+     *            the (simple) name of the inner class inside its enclosing
+     *            class. May be <tt>null</tt> for anonymous inner classes.
+     * @param access
+     *            the access flags of the inner class as originally declared in
+     *            the enclosing class.
+     */
+    public void visitInnerClass(String name, String outerName,
+            String innerName, int access) {
+        if (cv != null) {
+            cv.visitInnerClass(name, outerName, innerName, access);
+        }
+    }
+
+    /**
+     * Visits a field of the class.
+     *
+     * @param access
+     *            the field's access flags (see {@link Opcodes}). This parameter
+     *            also indicates if the field is synthetic and/or deprecated.
+     * @param name
+     *            the field's name.
+     * @param desc
+     *            the field's descriptor (see {@link Type Type}).
+     * @param signature
+     *            the field's signature. May be <tt>null</tt> if the field's
+     *            type does not use generic types.
+     * @param value
+     *            the field's initial value. This parameter, which may be
+     *            <tt>null</tt> if the field does not have an initial value,
+     *            must be an {@link Integer}, a {@link Float}, a {@link Long}, a
+     *            {@link Double} or a {@link String} (for <tt>int</tt>,
+     *            <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields
+     *            respectively). <i>This parameter is only used for static
+     *            fields</i>. Its value is ignored for non static fields, which
+     *            must be initialized through bytecode instructions in
+     *            constructors or methods.
+     * @return a visitor to visit field annotations and attributes, or
+     *         <tt>null</tt> if this class visitor is not interested in visiting
+     *         these annotations and attributes.
+     */
+    public FieldVisitor visitField(int access, String name, String desc,
+            String signature, Object value) {
+        if (cv != null) {
+            return cv.visitField(access, name, desc, signature, value);
+        }
+        return null;
+    }
+
+    /**
+     * Visits a method of the class. This method <i>must</i> return a new
+     * {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called,
+     * i.e., it should not return a previously returned visitor.
+     *
+     * @param access
+     *            the method's access flags (see {@link Opcodes}). This
+     *            parameter also indicates if the method is synthetic and/or
+     *            deprecated.
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     * @param signature
+     *            the method's signature. May be <tt>null</tt> if the method
+     *            parameters, return type and exceptions do not use generic
+     *            types.
+     * @param exceptions
+     *            the internal names of the method's exception classes (see
+     *            {@link Type#getInternalName() getInternalName}). May be
+     *            <tt>null</tt>.
+     * @return an object to visit the byte code of the method, or <tt>null</tt>
+     *         if this class visitor is not interested in visiting the code of
+     *         this method.
+     */
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        if (cv != null) {
+            return cv.visitMethod(access, name, desc, signature, exceptions);
+        }
+        return null;
+    }
+
+    /**
+     * Visits the end of the class. This method, which is the last one to be
+     * called, is used to inform the visitor that all the fields and methods of
+     * the class have been visited.
+     */
+    public void visitEnd() {
+        if (cv != null) {
+            cv.visitEnd();
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/ClassWriter.java b/src/jvm/clojure/asm/ClassWriter.java
new file mode 100644
index 0000000..3d78868
--- /dev/null
+++ b/src/jvm/clojure/asm/ClassWriter.java
@@ -0,0 +1,1693 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A {@link ClassVisitor} that generates classes in bytecode form. More
+ * precisely this visitor generates a byte array conforming to the Java class
+ * file format. It can be used alone, to generate a Java class "from scratch",
+ * or with one or more {@link ClassReader ClassReader} and adapter class visitor
+ * to generate a modified class from one or more existing Java classes.
+ *
+ * @author Eric Bruneton
+ */
+public class ClassWriter extends ClassVisitor {
+
+    /**
+     * Flag to automatically compute the maximum stack size and the maximum
+     * number of local variables of methods. If this flag is set, then the
+     * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the
+     * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod}
+     * method will be ignored, and computed automatically from the signature and
+     * the bytecode of each method.
+     *
+     * @see #ClassWriter(int)
+     */
+    public static final int COMPUTE_MAXS = 1;
+
+    /**
+     * Flag to automatically compute the stack map frames of methods from
+     * scratch. If this flag is set, then the calls to the
+     * {@link MethodVisitor#visitFrame} method are ignored, and the stack map
+     * frames are recomputed from the methods bytecode. The arguments of the
+     * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and
+     * recomputed from the bytecode. In other words, computeFrames implies
+     * computeMaxs.
+     *
+     * @see #ClassWriter(int)
+     */
+    public static final int COMPUTE_FRAMES = 2;
+
+    /**
+     * Pseudo access flag to distinguish between the synthetic attribute and the
+     * synthetic access flag.
+     */
+    static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000;
+
+    /**
+     * Factor to convert from ACC_SYNTHETIC_ATTRIBUTE to Opcode.ACC_SYNTHETIC.
+     */
+    static final int TO_ACC_SYNTHETIC = ACC_SYNTHETIC_ATTRIBUTE
+            / Opcodes.ACC_SYNTHETIC;
+
+    /**
+     * The type of instructions without any argument.
+     */
+    static final int NOARG_INSN = 0;
+
+    /**
+     * The type of instructions with an signed byte argument.
+     */
+    static final int SBYTE_INSN = 1;
+
+    /**
+     * The type of instructions with an signed short argument.
+     */
+    static final int SHORT_INSN = 2;
+
+    /**
+     * The type of instructions with a local variable index argument.
+     */
+    static final int VAR_INSN = 3;
+
+    /**
+     * The type of instructions with an implicit local variable index argument.
+     */
+    static final int IMPLVAR_INSN = 4;
+
+    /**
+     * The type of instructions with a type descriptor argument.
+     */
+    static final int TYPE_INSN = 5;
+
+    /**
+     * The type of field and method invocations instructions.
+     */
+    static final int FIELDORMETH_INSN = 6;
+
+    /**
+     * The type of the INVOKEINTERFACE/INVOKEDYNAMIC instruction.
+     */
+    static final int ITFMETH_INSN = 7;
+
+    /**
+     * The type of the INVOKEDYNAMIC instruction.
+     */
+    static final int INDYMETH_INSN = 8;
+
+    /**
+     * The type of instructions with a 2 bytes bytecode offset label.
+     */
+    static final int LABEL_INSN = 9;
+
+    /**
+     * The type of instructions with a 4 bytes bytecode offset label.
+     */
+    static final int LABELW_INSN = 10;
+
+    /**
+     * The type of the LDC instruction.
+     */
+    static final int LDC_INSN = 11;
+
+    /**
+     * The type of the LDC_W and LDC2_W instructions.
+     */
+    static final int LDCW_INSN = 12;
+
+    /**
+     * The type of the IINC instruction.
+     */
+    static final int IINC_INSN = 13;
+
+    /**
+     * The type of the TABLESWITCH instruction.
+     */
+    static final int TABL_INSN = 14;
+
+    /**
+     * The type of the LOOKUPSWITCH instruction.
+     */
+    static final int LOOK_INSN = 15;
+
+    /**
+     * The type of the MULTIANEWARRAY instruction.
+     */
+    static final int MANA_INSN = 16;
+
+    /**
+     * The type of the WIDE instruction.
+     */
+    static final int WIDE_INSN = 17;
+
+    /**
+     * The instruction types of all JVM opcodes.
+     */
+    static final byte[] TYPE;
+
+    /**
+     * The type of CONSTANT_Class constant pool items.
+     */
+    static final int CLASS = 7;
+
+    /**
+     * The type of CONSTANT_Fieldref constant pool items.
+     */
+    static final int FIELD = 9;
+
+    /**
+     * The type of CONSTANT_Methodref constant pool items.
+     */
+    static final int METH = 10;
+
+    /**
+     * The type of CONSTANT_InterfaceMethodref constant pool items.
+     */
+    static final int IMETH = 11;
+
+    /**
+     * The type of CONSTANT_String constant pool items.
+     */
+    static final int STR = 8;
+
+    /**
+     * The type of CONSTANT_Integer constant pool items.
+     */
+    static final int INT = 3;
+
+    /**
+     * The type of CONSTANT_Float constant pool items.
+     */
+    static final int FLOAT = 4;
+
+    /**
+     * The type of CONSTANT_Long constant pool items.
+     */
+    static final int LONG = 5;
+
+    /**
+     * The type of CONSTANT_Double constant pool items.
+     */
+    static final int DOUBLE = 6;
+
+    /**
+     * The type of CONSTANT_NameAndType constant pool items.
+     */
+    static final int NAME_TYPE = 12;
+
+    /**
+     * The type of CONSTANT_Utf8 constant pool items.
+     */
+    static final int UTF8 = 1;
+
+    /**
+     * The type of CONSTANT_MethodType constant pool items.
+     */
+    static final int MTYPE = 16;
+
+    /**
+     * The type of CONSTANT_MethodHandle constant pool items.
+     */
+    static final int HANDLE = 15;
+
+    /**
+     * The type of CONSTANT_InvokeDynamic constant pool items.
+     */
+    static final int INDY = 18;
+
+    /**
+     * The base value for all CONSTANT_MethodHandle constant pool items.
+     * Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9
+     * different items.
+     */
+    static final int HANDLE_BASE = 20;
+
+    /**
+     * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable},
+     * instead of the constant pool, in order to avoid clashes with normal
+     * constant pool items in the ClassWriter constant pool's hash table.
+     */
+    static final int TYPE_NORMAL = 30;
+
+    /**
+     * Uninitialized type Item stored in the ClassWriter
+     * {@link ClassWriter#typeTable}, instead of the constant pool, in order to
+     * avoid clashes with normal constant pool items in the ClassWriter constant
+     * pool's hash table.
+     */
+    static final int TYPE_UNINIT = 31;
+
+    /**
+     * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable},
+     * instead of the constant pool, in order to avoid clashes with normal
+     * constant pool items in the ClassWriter constant pool's hash table.
+     */
+    static final int TYPE_MERGED = 32;
+
+    /**
+     * The type of BootstrapMethods items. These items are stored in a special
+     * class attribute named BootstrapMethods and not in the constant pool.
+     */
+    static final int BSM = 33;
+
+    /**
+     * The class reader from which this class writer was constructed, if any.
+     */
+    ClassReader cr;
+
+    /**
+     * Minor and major version numbers of the class to be generated.
+     */
+    int version;
+
+    /**
+     * Index of the next item to be added in the constant pool.
+     */
+    int index;
+
+    /**
+     * The constant pool of this class.
+     */
+    final ByteVector pool;
+
+    /**
+     * The constant pool's hash table data.
+     */
+    Item[] items;
+
+    /**
+     * The threshold of the constant pool's hash table.
+     */
+    int threshold;
+
+    /**
+     * A reusable key used to look for items in the {@link #items} hash table.
+     */
+    final Item key;
+
+    /**
+     * A reusable key used to look for items in the {@link #items} hash table.
+     */
+    final Item key2;
+
+    /**
+     * A reusable key used to look for items in the {@link #items} hash table.
+     */
+    final Item key3;
+
+    /**
+     * A reusable key used to look for items in the {@link #items} hash table.
+     */
+    final Item key4;
+
+    /**
+     * A type table used to temporarily store internal names that will not
+     * necessarily be stored in the constant pool. This type table is used by
+     * the control flow and data flow analysis algorithm used to compute stack
+     * map frames from scratch. This array associates to each index <tt>i</tt>
+     * the Item whose index is <tt>i</tt>. All Item objects stored in this array
+     * are also stored in the {@link #items} hash table. These two arrays allow
+     * to retrieve an Item from its index or, conversely, to get the index of an
+     * Item from its value. Each Item stores an internal name in its
+     * {@link Item#strVal1} field.
+     */
+    Item[] typeTable;
+
+    /**
+     * Number of elements in the {@link #typeTable} array.
+     */
+    private short typeCount;
+
+    /**
+     * The access flags of this class.
+     */
+    private int access;
+
+    /**
+     * The constant pool item that contains the internal name of this class.
+     */
+    private int name;
+
+    /**
+     * The internal name of this class.
+     */
+    String thisName;
+
+    /**
+     * The constant pool item that contains the signature of this class.
+     */
+    private int signature;
+
+    /**
+     * The constant pool item that contains the internal name of the super class
+     * of this class.
+     */
+    private int superName;
+
+    /**
+     * Number of interfaces implemented or extended by this class or interface.
+     */
+    private int interfaceCount;
+
+    /**
+     * The interfaces implemented or extended by this class or interface. More
+     * precisely, this array contains the indexes of the constant pool items
+     * that contain the internal names of these interfaces.
+     */
+    private int[] interfaces;
+
+    /**
+     * The index of the constant pool item that contains the name of the source
+     * file from which this class was compiled.
+     */
+    private int sourceFile;
+
+    /**
+     * The SourceDebug attribute of this class.
+     */
+    private ByteVector sourceDebug;
+
+    /**
+     * The constant pool item that contains the name of the enclosing class of
+     * this class.
+     */
+    private int enclosingMethodOwner;
+
+    /**
+     * The constant pool item that contains the name and descriptor of the
+     * enclosing method of this class.
+     */
+    private int enclosingMethod;
+
+    /**
+     * The runtime visible annotations of this class.
+     */
+    private AnnotationWriter anns;
+
+    /**
+     * The runtime invisible annotations of this class.
+     */
+    private AnnotationWriter ianns;
+
+    /**
+     * The non standard attributes of this class.
+     */
+    private Attribute attrs;
+
+    /**
+     * The number of entries in the InnerClasses attribute.
+     */
+    private int innerClassesCount;
+
+    /**
+     * The InnerClasses attribute.
+     */
+    private ByteVector innerClasses;
+
+    /**
+     * The number of entries in the BootstrapMethods attribute.
+     */
+    int bootstrapMethodsCount;
+
+    /**
+     * The BootstrapMethods attribute.
+     */
+    ByteVector bootstrapMethods;
+
+    /**
+     * The fields of this class. These fields are stored in a linked list of
+     * {@link FieldWriter} objects, linked to each other by their
+     * {@link FieldWriter#fv} field. This field stores the first element of this
+     * list.
+     */
+    FieldWriter firstField;
+
+    /**
+     * The fields of this class. These fields are stored in a linked list of
+     * {@link FieldWriter} objects, linked to each other by their
+     * {@link FieldWriter#fv} field. This field stores the last element of this
+     * list.
+     */
+    FieldWriter lastField;
+
+    /**
+     * The methods of this class. These methods are stored in a linked list of
+     * {@link MethodWriter} objects, linked to each other by their
+     * {@link MethodWriter#mv} field. This field stores the first element of
+     * this list.
+     */
+    MethodWriter firstMethod;
+
+    /**
+     * The methods of this class. These methods are stored in a linked list of
+     * {@link MethodWriter} objects, linked to each other by their
+     * {@link MethodWriter#mv} field. This field stores the last element of this
+     * list.
+     */
+    MethodWriter lastMethod;
+
+    /**
+     * <tt>true</tt> if the maximum stack size and number of local variables
+     * must be automatically computed.
+     */
+    private final boolean computeMaxs;
+
+    /**
+     * <tt>true</tt> if the stack map frames must be recomputed from scratch.
+     */
+    private final boolean computeFrames;
+
+    /**
+     * <tt>true</tt> if the stack map tables of this class are invalid. The
+     * {@link MethodWriter#resizeInstructions} method cannot transform existing
+     * stack map tables, and so produces potentially invalid classes when it is
+     * executed. In this case the class is reread and rewritten with the
+     * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize
+     * stack map tables when this option is used).
+     */
+    boolean invalidFrames;
+
+    // ------------------------------------------------------------------------
+    // Static initializer
+    // ------------------------------------------------------------------------
+
+    /**
+     * Computes the instruction types of JVM opcodes.
+     */
+    static {
+        int i;
+        byte[] b = new byte[220];
+        String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+                + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+                + "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA"
+                + "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ";
+        for (i = 0; i < b.length; ++i) {
+            b[i] = (byte) (s.charAt(i) - 'A');
+        }
+        TYPE = b;
+
+        // code to generate the above string
+        //
+        // // SBYTE_INSN instructions
+        // b[Constants.NEWARRAY] = SBYTE_INSN;
+        // b[Constants.BIPUSH] = SBYTE_INSN;
+        //
+        // // SHORT_INSN instructions
+        // b[Constants.SIPUSH] = SHORT_INSN;
+        //
+        // // (IMPL)VAR_INSN instructions
+        // b[Constants.RET] = VAR_INSN;
+        // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) {
+        // b[i] = VAR_INSN;
+        // }
+        // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) {
+        // b[i] = VAR_INSN;
+        // }
+        // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3
+        // b[i] = IMPLVAR_INSN;
+        // }
+        // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3
+        // b[i] = IMPLVAR_INSN;
+        // }
+        //
+        // // TYPE_INSN instructions
+        // b[Constants.NEW] = TYPE_INSN;
+        // b[Constants.ANEWARRAY] = TYPE_INSN;
+        // b[Constants.CHECKCAST] = TYPE_INSN;
+        // b[Constants.INSTANCEOF] = TYPE_INSN;
+        //
+        // // (Set)FIELDORMETH_INSN instructions
+        // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) {
+        // b[i] = FIELDORMETH_INSN;
+        // }
+        // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN;
+        // b[Constants.INVOKEDYNAMIC] = INDYMETH_INSN;
+        //
+        // // LABEL(W)_INSN instructions
+        // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) {
+        // b[i] = LABEL_INSN;
+        // }
+        // b[Constants.IFNULL] = LABEL_INSN;
+        // b[Constants.IFNONNULL] = LABEL_INSN;
+        // b[200] = LABELW_INSN; // GOTO_W
+        // b[201] = LABELW_INSN; // JSR_W
+        // // temporary opcodes used internally by ASM - see Label and
+        // MethodWriter
+        // for (i = 202; i < 220; ++i) {
+        // b[i] = LABEL_INSN;
+        // }
+        //
+        // // LDC(_W) instructions
+        // b[Constants.LDC] = LDC_INSN;
+        // b[19] = LDCW_INSN; // LDC_W
+        // b[20] = LDCW_INSN; // LDC2_W
+        //
+        // // special instructions
+        // b[Constants.IINC] = IINC_INSN;
+        // b[Constants.TABLESWITCH] = TABL_INSN;
+        // b[Constants.LOOKUPSWITCH] = LOOK_INSN;
+        // b[Constants.MULTIANEWARRAY] = MANA_INSN;
+        // b[196] = WIDE_INSN; // WIDE
+        //
+        // for (i = 0; i < b.length; ++i) {
+        // System.err.print((char)('A' + b[i]));
+        // }
+        // System.err.println();
+    }
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a new {@link ClassWriter} object.
+     *
+     * @param flags
+     *            option flags that can be used to modify the default behavior
+     *            of this class. See {@link #COMPUTE_MAXS},
+     *            {@link #COMPUTE_FRAMES}.
+     */
+    public ClassWriter(final int flags) {
+        super(Opcodes.ASM4);
+        index = 1;
+        pool = new ByteVector();
+        items = new Item[256];
+        threshold = (int) (0.75d * items.length);
+        key = new Item();
+        key2 = new Item();
+        key3 = new Item();
+        key4 = new Item();
+        this.computeMaxs = (flags & COMPUTE_MAXS) != 0;
+        this.computeFrames = (flags & COMPUTE_FRAMES) != 0;
+    }
+
+    /**
+     * Constructs a new {@link ClassWriter} object and enables optimizations for
+     * "mostly add" bytecode transformations. These optimizations are the
+     * following:
+     *
+     * <ul>
+     * <li>The constant pool from the original class is copied as is in the new
+     * class, which saves time. New constant pool entries will be added at the
+     * end if necessary, but unused constant pool entries <i>won't be
+     * removed</i>.</li>
+     * <li>Methods that are not transformed are copied as is in the new class,
+     * directly from the original class bytecode (i.e. without emitting visit
+     * events for all the method instructions), which saves a <i>lot</i> of
+     * time. Untransformed methods are detected by the fact that the
+     * {@link ClassReader} receives {@link MethodVisitor} objects that come from
+     * a {@link ClassWriter} (and not from any other {@link ClassVisitor}
+     * instance).</li>
+     * </ul>
+     *
+     * @param classReader
+     *            the {@link ClassReader} used to read the original class. It
+     *            will be used to copy the entire constant pool from the
+     *            original class and also to copy other fragments of original
+     *            bytecode where applicable.
+     * @param flags
+     *            option flags that can be used to modify the default behavior
+     *            of this class. <i>These option flags do not affect methods
+     *            that are copied as is in the new class. This means that the
+     *            maximum stack size nor the stack frames will be computed for
+     *            these methods</i>. See {@link #COMPUTE_MAXS},
+     *            {@link #COMPUTE_FRAMES}.
+     */
+    public ClassWriter(final ClassReader classReader, final int flags) {
+        this(flags);
+        classReader.copyPool(this);
+        this.cr = classReader;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the ClassVisitor abstract class
+    // ------------------------------------------------------------------------
+
+    @Override
+    public final void visit(final int version, final int access,
+            final String name, final String signature, final String superName,
+            final String[] interfaces) {
+        this.version = version;
+        this.access = access;
+        this.name = newClass(name);
+        thisName = name;
+        if (ClassReader.SIGNATURES && signature != null) {
+            this.signature = newUTF8(signature);
+        }
+        this.superName = superName == null ? 0 : newClass(superName);
+        if (interfaces != null && interfaces.length > 0) {
+            interfaceCount = interfaces.length;
+            this.interfaces = new int[interfaceCount];
+            for (int i = 0; i < interfaceCount; ++i) {
+                this.interfaces[i] = newClass(interfaces[i]);
+            }
+        }
+    }
+
+    @Override
+    public final void visitSource(final String file, final String debug) {
+        if (file != null) {
+            sourceFile = newUTF8(file);
+        }
+        if (debug != null) {
+            sourceDebug = new ByteVector().putUTF8(debug);
+        }
+    }
+
+    @Override
+    public final void visitOuterClass(final String owner, final String name,
+            final String desc) {
+        enclosingMethodOwner = newClass(owner);
+        if (name != null && desc != null) {
+            enclosingMethod = newNameType(name, desc);
+        }
+    }
+
+    @Override
+    public final AnnotationVisitor visitAnnotation(final String desc,
+            final boolean visible) {
+        if (!ClassReader.ANNOTATIONS) {
+            return null;
+        }
+        ByteVector bv = new ByteVector();
+        // write type, and reserve space for values count
+        bv.putShort(newUTF8(desc)).putShort(0);
+        AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2);
+        if (visible) {
+            aw.next = anns;
+            anns = aw;
+        } else {
+            aw.next = ianns;
+            ianns = aw;
+        }
+        return aw;
+    }
+
+    @Override
+    public final void visitAttribute(final Attribute attr) {
+        attr.next = attrs;
+        attrs = attr;
+    }
+
+    @Override
+    public final void visitInnerClass(final String name,
+            final String outerName, final String innerName, final int access) {
+        if (innerClasses == null) {
+            innerClasses = new ByteVector();
+        }
+        ++innerClassesCount;
+        innerClasses.putShort(name == null ? 0 : newClass(name));
+        innerClasses.putShort(outerName == null ? 0 : newClass(outerName));
+        innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName));
+        innerClasses.putShort(access);
+    }
+
+    @Override
+    public final FieldVisitor visitField(final int access, final String name,
+            final String desc, final String signature, final Object value) {
+        return new FieldWriter(this, access, name, desc, signature, value);
+    }
+
+    @Override
+    public final MethodVisitor visitMethod(final int access, final String name,
+            final String desc, final String signature, final String[] exceptions) {
+        return new MethodWriter(this, access, name, desc, signature,
+                exceptions, computeMaxs, computeFrames);
+    }
+
+    @Override
+    public final void visitEnd() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Other public methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the bytecode of the class that was build with this class writer.
+     *
+     * @return the bytecode of the class that was build with this class writer.
+     */
+    public byte[] toByteArray() {
+        if (index > 0xFFFF) {
+            throw new RuntimeException("Class file too large!");
+        }
+        // computes the real size of the bytecode of this class
+        int size = 24 + 2 * interfaceCount;
+        int nbFields = 0;
+        FieldWriter fb = firstField;
+        while (fb != null) {
+            ++nbFields;
+            size += fb.getSize();
+            fb = (FieldWriter) fb.fv;
+        }
+        int nbMethods = 0;
+        MethodWriter mb = firstMethod;
+        while (mb != null) {
+            ++nbMethods;
+            size += mb.getSize();
+            mb = (MethodWriter) mb.mv;
+        }
+        int attributeCount = 0;
+        if (bootstrapMethods != null) {
+            // we put it as first attribute in order to improve a bit
+            // ClassReader.copyBootstrapMethods
+            ++attributeCount;
+            size += 8 + bootstrapMethods.length;
+            newUTF8("BootstrapMethods");
+        }
+        if (ClassReader.SIGNATURES && signature != 0) {
+            ++attributeCount;
+            size += 8;
+            newUTF8("Signature");
+        }
+        if (sourceFile != 0) {
+            ++attributeCount;
+            size += 8;
+            newUTF8("SourceFile");
+        }
+        if (sourceDebug != null) {
+            ++attributeCount;
+            size += sourceDebug.length + 4;
+            newUTF8("SourceDebugExtension");
+        }
+        if (enclosingMethodOwner != 0) {
+            ++attributeCount;
+            size += 10;
+            newUTF8("EnclosingMethod");
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            ++attributeCount;
+            size += 6;
+            newUTF8("Deprecated");
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                ++attributeCount;
+                size += 6;
+                newUTF8("Synthetic");
+            }
+        }
+        if (innerClasses != null) {
+            ++attributeCount;
+            size += 8 + innerClasses.length;
+            newUTF8("InnerClasses");
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            ++attributeCount;
+            size += 8 + anns.getSize();
+            newUTF8("RuntimeVisibleAnnotations");
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            ++attributeCount;
+            size += 8 + ianns.getSize();
+            newUTF8("RuntimeInvisibleAnnotations");
+        }
+        if (attrs != null) {
+            attributeCount += attrs.getCount();
+            size += attrs.getSize(this, null, 0, -1, -1);
+        }
+        size += pool.length;
+        // allocates a byte vector of this size, in order to avoid unnecessary
+        // arraycopy operations in the ByteVector.enlarge() method
+        ByteVector out = new ByteVector(size);
+        out.putInt(0xCAFEBABE).putInt(version);
+        out.putShort(index).putByteArray(pool.data, 0, pool.length);
+        int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE
+                | ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC);
+        out.putShort(access & ~mask).putShort(name).putShort(superName);
+        out.putShort(interfaceCount);
+        for (int i = 0; i < interfaceCount; ++i) {
+            out.putShort(interfaces[i]);
+        }
+        out.putShort(nbFields);
+        fb = firstField;
+        while (fb != null) {
+            fb.put(out);
+            fb = (FieldWriter) fb.fv;
+        }
+        out.putShort(nbMethods);
+        mb = firstMethod;
+        while (mb != null) {
+            mb.put(out);
+            mb = (MethodWriter) mb.mv;
+        }
+        out.putShort(attributeCount);
+        if (bootstrapMethods != null) {
+            out.putShort(newUTF8("BootstrapMethods"));
+            out.putInt(bootstrapMethods.length + 2).putShort(
+                    bootstrapMethodsCount);
+            out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length);
+        }
+        if (ClassReader.SIGNATURES && signature != 0) {
+            out.putShort(newUTF8("Signature")).putInt(2).putShort(signature);
+        }
+        if (sourceFile != 0) {
+            out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile);
+        }
+        if (sourceDebug != null) {
+            int len = sourceDebug.length - 2;
+            out.putShort(newUTF8("SourceDebugExtension")).putInt(len);
+            out.putByteArray(sourceDebug.data, 2, len);
+        }
+        if (enclosingMethodOwner != 0) {
+            out.putShort(newUTF8("EnclosingMethod")).putInt(4);
+            out.putShort(enclosingMethodOwner).putShort(enclosingMethod);
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            out.putShort(newUTF8("Deprecated")).putInt(0);
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                out.putShort(newUTF8("Synthetic")).putInt(0);
+            }
+        }
+        if (innerClasses != null) {
+            out.putShort(newUTF8("InnerClasses"));
+            out.putInt(innerClasses.length + 2).putShort(innerClassesCount);
+            out.putByteArray(innerClasses.data, 0, innerClasses.length);
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            out.putShort(newUTF8("RuntimeVisibleAnnotations"));
+            anns.put(out);
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
+            ianns.put(out);
+        }
+        if (attrs != null) {
+            attrs.put(this, null, 0, -1, -1, out);
+        }
+        if (invalidFrames) {
+            ClassWriter cw = new ClassWriter(COMPUTE_FRAMES);
+            new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES);
+            return cw.toByteArray();
+        }
+        return out.data;
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods: constant pool management
+    // ------------------------------------------------------------------------
+
+    /**
+     * Adds a number or string constant to the constant pool of the class being
+     * build. Does nothing if the constant pool already contains a similar item.
+     *
+     * @param cst
+     *            the value of the constant to be added to the constant pool.
+     *            This parameter must be an {@link Integer}, a {@link Float}, a
+     *            {@link Long}, a {@link Double}, a {@link String} or a
+     *            {@link Type}.
+     * @return a new or already existing constant item with the given value.
+     */
+    Item newConstItem(final Object cst) {
+        if (cst instanceof Integer) {
+            int val = ((Integer) cst).intValue();
+            return newInteger(val);
+        } else if (cst instanceof Byte) {
+            int val = ((Byte) cst).intValue();
+            return newInteger(val);
+        } else if (cst instanceof Character) {
+            int val = ((Character) cst).charValue();
+            return newInteger(val);
+        } else if (cst instanceof Short) {
+            int val = ((Short) cst).intValue();
+            return newInteger(val);
+        } else if (cst instanceof Boolean) {
+            int val = ((Boolean) cst).booleanValue() ? 1 : 0;
+            return newInteger(val);
+        } else if (cst instanceof Float) {
+            float val = ((Float) cst).floatValue();
+            return newFloat(val);
+        } else if (cst instanceof Long) {
+            long val = ((Long) cst).longValue();
+            return newLong(val);
+        } else if (cst instanceof Double) {
+            double val = ((Double) cst).doubleValue();
+            return newDouble(val);
+        } else if (cst instanceof String) {
+            return newString((String) cst);
+        } else if (cst instanceof Type) {
+            Type t = (Type) cst;
+            int s = t.getSort();
+            if (s == Type.OBJECT) {
+                return newClassItem(t.getInternalName());
+            } else if (s == Type.METHOD) {
+                return newMethodTypeItem(t.getDescriptor());
+            } else { // s == primitive type or array
+                return newClassItem(t.getDescriptor());
+            }
+        } else if (cst instanceof Handle) {
+            Handle h = (Handle) cst;
+            return newHandleItem(h.tag, h.owner, h.name, h.desc);
+        } else {
+            throw new IllegalArgumentException("value " + cst);
+        }
+    }
+
+    /**
+     * Adds a number or string constant to the constant pool of the class being
+     * build. Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param cst
+     *            the value of the constant to be added to the constant pool.
+     *            This parameter must be an {@link Integer}, a {@link Float}, a
+     *            {@link Long}, a {@link Double} or a {@link String}.
+     * @return the index of a new or already existing constant item with the
+     *         given value.
+     */
+    public int newConst(final Object cst) {
+        return newConstItem(cst).index;
+    }
+
+    /**
+     * Adds an UTF8 string to the constant pool of the class being build. Does
+     * nothing if the constant pool already contains a similar item. <i>This
+     * method is intended for {@link Attribute} sub classes, and is normally not
+     * needed by class generators or adapters.</i>
+     *
+     * @param value
+     *            the String value.
+     * @return the index of a new or already existing UTF8 item.
+     */
+    public int newUTF8(final String value) {
+        key.set(UTF8, value, null, null);
+        Item result = get(key);
+        if (result == null) {
+            pool.putByte(UTF8).putUTF8(value);
+            result = new Item(index++, key);
+            put(result);
+        }
+        return result.index;
+    }
+
+    /**
+     * Adds a class reference to the constant pool of the class being build.
+     * Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param value
+     *            the internal name of the class.
+     * @return a new or already existing class reference item.
+     */
+    Item newClassItem(final String value) {
+        key2.set(CLASS, value, null, null);
+        Item result = get(key2);
+        if (result == null) {
+            pool.put12(CLASS, newUTF8(value));
+            result = new Item(index++, key2);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a class reference to the constant pool of the class being build.
+     * Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param value
+     *            the internal name of the class.
+     * @return the index of a new or already existing class reference item.
+     */
+    public int newClass(final String value) {
+        return newClassItem(value).index;
+    }
+
+    /**
+     * Adds a method type reference to the constant pool of the class being
+     * build. Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param methodDesc
+     *            method descriptor of the method type.
+     * @return a new or already existing method type reference item.
+     */
+    Item newMethodTypeItem(final String methodDesc) {
+        key2.set(MTYPE, methodDesc, null, null);
+        Item result = get(key2);
+        if (result == null) {
+            pool.put12(MTYPE, newUTF8(methodDesc));
+            result = new Item(index++, key2);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a method type reference to the constant pool of the class being
+     * build. Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param methodDesc
+     *            method descriptor of the method type.
+     * @return the index of a new or already existing method type reference
+     *         item.
+     */
+    public int newMethodType(final String methodDesc) {
+        return newMethodTypeItem(methodDesc).index;
+    }
+
+    /**
+     * Adds a handle to the constant pool of the class being build. Does nothing
+     * if the constant pool already contains a similar item. <i>This method is
+     * intended for {@link Attribute} sub classes, and is normally not needed by
+     * class generators or adapters.</i>
+     *
+     * @param tag
+     *            the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
+     *            {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
+     *            {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
+     *            {@link Opcodes#H_INVOKESTATIC},
+     *            {@link Opcodes#H_INVOKESPECIAL},
+     *            {@link Opcodes#H_NEWINVOKESPECIAL} or
+     *            {@link Opcodes#H_INVOKEINTERFACE}.
+     * @param owner
+     *            the internal name of the field or method owner class.
+     * @param name
+     *            the name of the field or method.
+     * @param desc
+     *            the descriptor of the field or method.
+     * @return a new or an already existing method type reference item.
+     */
+    Item newHandleItem(final int tag, final String owner, final String name,
+            final String desc) {
+        key4.set(HANDLE_BASE + tag, owner, name, desc);
+        Item result = get(key4);
+        if (result == null) {
+            if (tag <= Opcodes.H_PUTSTATIC) {
+                put112(HANDLE, tag, newField(owner, name, desc));
+            } else {
+                put112(HANDLE,
+                        tag,
+                        newMethod(owner, name, desc,
+                                tag == Opcodes.H_INVOKEINTERFACE));
+            }
+            result = new Item(index++, key4);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a handle to the constant pool of the class being build. Does nothing
+     * if the constant pool already contains a similar item. <i>This method is
+     * intended for {@link Attribute} sub classes, and is normally not needed by
+     * class generators or adapters.</i>
+     *
+     * @param tag
+     *            the kind of this handle. Must be {@link Opcodes#H_GETFIELD},
+     *            {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD},
+     *            {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL},
+     *            {@link Opcodes#H_INVOKESTATIC},
+     *            {@link Opcodes#H_INVOKESPECIAL},
+     *            {@link Opcodes#H_NEWINVOKESPECIAL} or
+     *            {@link Opcodes#H_INVOKEINTERFACE}.
+     * @param owner
+     *            the internal name of the field or method owner class.
+     * @param name
+     *            the name of the field or method.
+     * @param desc
+     *            the descriptor of the field or method.
+     * @return the index of a new or already existing method type reference
+     *         item.
+     */
+    public int newHandle(final int tag, final String owner, final String name,
+            final String desc) {
+        return newHandleItem(tag, owner, name, desc).index;
+    }
+
+    /**
+     * Adds an invokedynamic reference to the constant pool of the class being
+     * build. Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param name
+     *            name of the invoked method.
+     * @param desc
+     *            descriptor of the invoke method.
+     * @param bsm
+     *            the bootstrap method.
+     * @param bsmArgs
+     *            the bootstrap method constant arguments.
+     *
+     * @return a new or an already existing invokedynamic type reference item.
+     */
+    Item newInvokeDynamicItem(final String name, final String desc,
+            final Handle bsm, final Object... bsmArgs) {
+        // cache for performance
+        ByteVector bootstrapMethods = this.bootstrapMethods;
+        if (bootstrapMethods == null) {
+            bootstrapMethods = this.bootstrapMethods = new ByteVector();
+        }
+
+        int position = bootstrapMethods.length; // record current position
+
+        int hashCode = bsm.hashCode();
+        bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name,
+                bsm.desc));
+
+        int argsLength = bsmArgs.length;
+        bootstrapMethods.putShort(argsLength);
+
+        for (int i = 0; i < argsLength; i++) {
+            Object bsmArg = bsmArgs[i];
+            hashCode ^= bsmArg.hashCode();
+            bootstrapMethods.putShort(newConst(bsmArg));
+        }
+
+        byte[] data = bootstrapMethods.data;
+        int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments)
+        hashCode &= 0x7FFFFFFF;
+        Item result = items[hashCode % items.length];
+        loop: while (result != null) {
+            if (result.type != BSM || result.hashCode != hashCode) {
+                result = result.next;
+                continue;
+            }
+
+            // because the data encode the size of the argument
+            // we don't need to test if these size are equals
+            int resultPosition = result.intVal;
+            for (int p = 0; p < length; p++) {
+                if (data[position + p] != data[resultPosition + p]) {
+                    result = result.next;
+                    continue loop;
+                }
+            }
+            break;
+        }
+
+        int bootstrapMethodIndex;
+        if (result != null) {
+            bootstrapMethodIndex = result.index;
+            bootstrapMethods.length = position; // revert to old position
+        } else {
+            bootstrapMethodIndex = bootstrapMethodsCount++;
+            result = new Item(bootstrapMethodIndex);
+            result.set(position, hashCode);
+            put(result);
+        }
+
+        // now, create the InvokeDynamic constant
+        key3.set(name, desc, bootstrapMethodIndex);
+        result = get(key3);
+        if (result == null) {
+            put122(INDY, bootstrapMethodIndex, newNameType(name, desc));
+            result = new Item(index++, key3);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds an invokedynamic reference to the constant pool of the class being
+     * build. Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param name
+     *            name of the invoked method.
+     * @param desc
+     *            descriptor of the invoke method.
+     * @param bsm
+     *            the bootstrap method.
+     * @param bsmArgs
+     *            the bootstrap method constant arguments.
+     *
+     * @return the index of a new or already existing invokedynamic reference
+     *         item.
+     */
+    public int newInvokeDynamic(final String name, final String desc,
+            final Handle bsm, final Object... bsmArgs) {
+        return newInvokeDynamicItem(name, desc, bsm, bsmArgs).index;
+    }
+
+    /**
+     * Adds a field reference to the constant pool of the class being build.
+     * Does nothing if the constant pool already contains a similar item.
+     *
+     * @param owner
+     *            the internal name of the field's owner class.
+     * @param name
+     *            the field's name.
+     * @param desc
+     *            the field's descriptor.
+     * @return a new or already existing field reference item.
+     */
+    Item newFieldItem(final String owner, final String name, final String desc) {
+        key3.set(FIELD, owner, name, desc);
+        Item result = get(key3);
+        if (result == null) {
+            put122(FIELD, newClass(owner), newNameType(name, desc));
+            result = new Item(index++, key3);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a field reference to the constant pool of the class being build.
+     * Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param owner
+     *            the internal name of the field's owner class.
+     * @param name
+     *            the field's name.
+     * @param desc
+     *            the field's descriptor.
+     * @return the index of a new or already existing field reference item.
+     */
+    public int newField(final String owner, final String name, final String desc) {
+        return newFieldItem(owner, name, desc).index;
+    }
+
+    /**
+     * Adds a method reference to the constant pool of the class being build.
+     * Does nothing if the constant pool already contains a similar item.
+     *
+     * @param owner
+     *            the internal name of the method's owner class.
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor.
+     * @param itf
+     *            <tt>true</tt> if <tt>owner</tt> is an interface.
+     * @return a new or already existing method reference item.
+     */
+    Item newMethodItem(final String owner, final String name,
+            final String desc, final boolean itf) {
+        int type = itf ? IMETH : METH;
+        key3.set(type, owner, name, desc);
+        Item result = get(key3);
+        if (result == null) {
+            put122(type, newClass(owner), newNameType(name, desc));
+            result = new Item(index++, key3);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a method reference to the constant pool of the class being build.
+     * Does nothing if the constant pool already contains a similar item.
+     * <i>This method is intended for {@link Attribute} sub classes, and is
+     * normally not needed by class generators or adapters.</i>
+     *
+     * @param owner
+     *            the internal name of the method's owner class.
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor.
+     * @param itf
+     *            <tt>true</tt> if <tt>owner</tt> is an interface.
+     * @return the index of a new or already existing method reference item.
+     */
+    public int newMethod(final String owner, final String name,
+            final String desc, final boolean itf) {
+        return newMethodItem(owner, name, desc, itf).index;
+    }
+
+    /**
+     * Adds an integer to the constant pool of the class being build. Does
+     * nothing if the constant pool already contains a similar item.
+     *
+     * @param value
+     *            the int value.
+     * @return a new or already existing int item.
+     */
+    Item newInteger(final int value) {
+        key.set(value);
+        Item result = get(key);
+        if (result == null) {
+            pool.putByte(INT).putInt(value);
+            result = new Item(index++, key);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a float to the constant pool of the class being build. Does nothing
+     * if the constant pool already contains a similar item.
+     *
+     * @param value
+     *            the float value.
+     * @return a new or already existing float item.
+     */
+    Item newFloat(final float value) {
+        key.set(value);
+        Item result = get(key);
+        if (result == null) {
+            pool.putByte(FLOAT).putInt(key.intVal);
+            result = new Item(index++, key);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a long to the constant pool of the class being build. Does nothing
+     * if the constant pool already contains a similar item.
+     *
+     * @param value
+     *            the long value.
+     * @return a new or already existing long item.
+     */
+    Item newLong(final long value) {
+        key.set(value);
+        Item result = get(key);
+        if (result == null) {
+            pool.putByte(LONG).putLong(value);
+            result = new Item(index, key);
+            index += 2;
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a double to the constant pool of the class being build. Does nothing
+     * if the constant pool already contains a similar item.
+     *
+     * @param value
+     *            the double value.
+     * @return a new or already existing double item.
+     */
+    Item newDouble(final double value) {
+        key.set(value);
+        Item result = get(key);
+        if (result == null) {
+            pool.putByte(DOUBLE).putLong(key.longVal);
+            result = new Item(index, key);
+            index += 2;
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a string to the constant pool of the class being build. Does nothing
+     * if the constant pool already contains a similar item.
+     *
+     * @param value
+     *            the String value.
+     * @return a new or already existing string item.
+     */
+    private Item newString(final String value) {
+        key2.set(STR, value, null, null);
+        Item result = get(key2);
+        if (result == null) {
+            pool.put12(STR, newUTF8(value));
+            result = new Item(index++, key2);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds a name and type to the constant pool of the class being build. Does
+     * nothing if the constant pool already contains a similar item. <i>This
+     * method is intended for {@link Attribute} sub classes, and is normally not
+     * needed by class generators or adapters.</i>
+     *
+     * @param name
+     *            a name.
+     * @param desc
+     *            a type descriptor.
+     * @return the index of a new or already existing name and type item.
+     */
+    public int newNameType(final String name, final String desc) {
+        return newNameTypeItem(name, desc).index;
+    }
+
+    /**
+     * Adds a name and type to the constant pool of the class being build. Does
+     * nothing if the constant pool already contains a similar item.
+     *
+     * @param name
+     *            a name.
+     * @param desc
+     *            a type descriptor.
+     * @return a new or already existing name and type item.
+     */
+    Item newNameTypeItem(final String name, final String desc) {
+        key2.set(NAME_TYPE, name, desc, null);
+        Item result = get(key2);
+        if (result == null) {
+            put122(NAME_TYPE, newUTF8(name), newUTF8(desc));
+            result = new Item(index++, key2);
+            put(result);
+        }
+        return result;
+    }
+
+    /**
+     * Adds the given internal name to {@link #typeTable} and returns its index.
+     * Does nothing if the type table already contains this internal name.
+     *
+     * @param type
+     *            the internal name to be added to the type table.
+     * @return the index of this internal name in the type table.
+     */
+    int addType(final String type) {
+        key.set(TYPE_NORMAL, type, null, null);
+        Item result = get(key);
+        if (result == null) {
+            result = addType(key);
+        }
+        return result.index;
+    }
+
+    /**
+     * Adds the given "uninitialized" type to {@link #typeTable} and returns its
+     * index. This method is used for UNINITIALIZED types, made of an internal
+     * name and a bytecode offset.
+     *
+     * @param type
+     *            the internal name to be added to the type table.
+     * @param offset
+     *            the bytecode offset of the NEW instruction that created this
+     *            UNINITIALIZED type value.
+     * @return the index of this internal name in the type table.
+     */
+    int addUninitializedType(final String type, final int offset) {
+        key.type = TYPE_UNINIT;
+        key.intVal = offset;
+        key.strVal1 = type;
+        key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset);
+        Item result = get(key);
+        if (result == null) {
+            result = addType(key);
+        }
+        return result.index;
+    }
+
+    /**
+     * Adds the given Item to {@link #typeTable}.
+     *
+     * @param item
+     *            the value to be added to the type table.
+     * @return the added Item, which a new Item instance with the same value as
+     *         the given Item.
+     */
+    private Item addType(final Item item) {
+        ++typeCount;
+        Item result = new Item(typeCount, key);
+        put(result);
+        if (typeTable == null) {
+            typeTable = new Item[16];
+        }
+        if (typeCount == typeTable.length) {
+            Item[] newTable = new Item[2 * typeTable.length];
+            System.arraycopy(typeTable, 0, newTable, 0, typeTable.length);
+            typeTable = newTable;
+        }
+        typeTable[typeCount] = result;
+        return result;
+    }
+
+    /**
+     * Returns the index of the common super type of the two given types. This
+     * method calls {@link #getCommonSuperClass} and caches the result in the
+     * {@link #items} hash table to speedup future calls with the same
+     * parameters.
+     *
+     * @param type1
+     *            index of an internal name in {@link #typeTable}.
+     * @param type2
+     *            index of an internal name in {@link #typeTable}.
+     * @return the index of the common super type of the two given types.
+     */
+    int getMergedType(final int type1, final int type2) {
+        key2.type = TYPE_MERGED;
+        key2.longVal = type1 | (((long) type2) << 32);
+        key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2);
+        Item result = get(key2);
+        if (result == null) {
+            String t = typeTable[type1].strVal1;
+            String u = typeTable[type2].strVal1;
+            key2.intVal = addType(getCommonSuperClass(t, u));
+            result = new Item((short) 0, key2);
+            put(result);
+        }
+        return result.intVal;
+    }
+
+    /**
+     * Returns the common super type of the two given types. The default
+     * implementation of this method <i>loads<i> the two given classes and uses
+     * the java.lang.Class methods to find the common super class. It can be
+     * overridden to compute this common super type in other ways, in particular
+     * without actually loading any class, or to take into account the class
+     * that is currently being generated by this ClassWriter, which can of
+     * course not be loaded since it is under construction.
+     *
+     * @param type1
+     *            the internal name of a class.
+     * @param type2
+     *            the internal name of another class.
+     * @return the internal name of the common super class of the two given
+     *         classes.
+     */
+    protected String getCommonSuperClass(final String type1, final String type2) {
+        Class<?> c, d;
+        ClassLoader classLoader = getClass().getClassLoader();
+        try {
+            c = Class.forName(type1.replace('/', '.'), false, classLoader);
+            d = Class.forName(type2.replace('/', '.'), false, classLoader);
+        } catch (Exception e) {
+            throw new RuntimeException(e.toString());
+        }
+        if (c.isAssignableFrom(d)) {
+            return type1;
+        }
+        if (d.isAssignableFrom(c)) {
+            return type2;
+        }
+        if (c.isInterface() || d.isInterface()) {
+            return "java/lang/Object";
+        } else {
+            do {
+                c = c.getSuperclass();
+            } while (!c.isAssignableFrom(d));
+            return c.getName().replace('.', '/');
+        }
+    }
+
+    /**
+     * Returns the constant pool's hash table item which is equal to the given
+     * item.
+     *
+     * @param key
+     *            a constant pool item.
+     * @return the constant pool's hash table item which is equal to the given
+     *         item, or <tt>null</tt> if there is no such item.
+     */
+    private Item get(final Item key) {
+        Item i = items[key.hashCode % items.length];
+        while (i != null && (i.type != key.type || !key.isEqualTo(i))) {
+            i = i.next;
+        }
+        return i;
+    }
+
+    /**
+     * Puts the given item in the constant pool's hash table. The hash table
+     * <i>must</i> not already contains this item.
+     *
+     * @param i
+     *            the item to be added to the constant pool's hash table.
+     */
+    private void put(final Item i) {
+        if (index + typeCount > threshold) {
+            int ll = items.length;
+            int nl = ll * 2 + 1;
+            Item[] newItems = new Item[nl];
+            for (int l = ll - 1; l >= 0; --l) {
+                Item j = items[l];
+                while (j != null) {
+                    int index = j.hashCode % newItems.length;
+                    Item k = j.next;
+                    j.next = newItems[index];
+                    newItems[index] = j;
+                    j = k;
+                }
+            }
+            items = newItems;
+            threshold = (int) (nl * 0.75);
+        }
+        int index = i.hashCode % items.length;
+        i.next = items[index];
+        items[index] = i;
+    }
+
+    /**
+     * Puts one byte and two shorts into the constant pool.
+     *
+     * @param b
+     *            a byte.
+     * @param s1
+     *            a short.
+     * @param s2
+     *            another short.
+     */
+    private void put122(final int b, final int s1, final int s2) {
+        pool.put12(b, s1).putShort(s2);
+    }
+
+    /**
+     * Puts two bytes and one short into the constant pool.
+     *
+     * @param b1
+     *            a byte.
+     * @param b2
+     *            another byte.
+     * @param s
+     *            a short.
+     */
+    private void put112(final int b1, final int b2, final int s) {
+        pool.put11(b1, b2).putShort(s);
+    }
+}
diff --git a/src/jvm/clojure/asm/Context.java b/src/jvm/clojure/asm/Context.java
new file mode 100644
index 0000000..d113ed9
--- /dev/null
+++ b/src/jvm/clojure/asm/Context.java
@@ -0,0 +1,110 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package clojure.asm;
+
+/**
+ * Information about a class being parsed in a {@link ClassReader}.
+ *
+ * @author Eric Bruneton
+ */
+class Context {
+
+    /**
+     * Prototypes of the attributes that must be parsed for this class.
+     */
+    Attribute[] attrs;
+
+    /**
+     * The {@link ClassReader} option flags for the parsing of this class.
+     */
+    int flags;
+
+    /**
+     * The buffer used to read strings.
+     */
+    char[] buffer;
+
+    /**
+     * The start index of each bootstrap method.
+     */
+    int[] bootstrapMethods;
+
+    /**
+     * The access flags of the method currently being parsed.
+     */
+    int access;
+
+    /**
+     * The name of the method currently being parsed.
+     */
+    String name;
+
+    /**
+     * The descriptor of the method currently being parsed.
+     */
+    String desc;
+
+    /**
+     * The offset of the latest stack map frame that has been parsed.
+     */
+    int offset;
+
+    /**
+     * The encoding of the latest stack map frame that has been parsed.
+     */
+    int mode;
+
+    /**
+     * The number of locals in the latest stack map frame that has been parsed.
+     */
+    int localCount;
+
+    /**
+     * The number locals in the latest stack map frame that has been parsed,
+     * minus the number of locals in the previous frame.
+     */
+    int localDiff;
+
+    /**
+     * The local values of the latest stack map frame that has been parsed.
+     */
+    Object[] local;
+
+    /**
+     * The stack size of the latest stack map frame that has been parsed.
+     */
+    int stackCount;
+
+    /**
+     * The stack values of the latest stack map frame that has been parsed.
+     */
+    Object[] stack;
+}
\ No newline at end of file
diff --git a/src/jvm/clojure/asm/Edge.java b/src/jvm/clojure/asm/Edge.java
new file mode 100644
index 0000000..045cd46
--- /dev/null
+++ b/src/jvm/clojure/asm/Edge.java
@@ -0,0 +1,75 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * An edge in the control flow graph of a method body. See {@link Label Label}.
+ *
+ * @author Eric Bruneton
+ */
+class Edge {
+
+    /**
+     * Denotes a normal control flow graph edge.
+     */
+    static final int NORMAL = 0;
+
+    /**
+     * Denotes a control flow graph edge corresponding to an exception handler.
+     * More precisely any {@link Edge} whose {@link #info} is strictly positive
+     * corresponds to an exception handler. The actual value of {@link #info} is
+     * the index, in the {@link ClassWriter} type table, of the exception that
+     * is catched.
+     */
+    static final int EXCEPTION = 0x7FFFFFFF;
+
+    /**
+     * Information about this control flow graph edge. If
+     * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative)
+     * stack size in the basic block from which this edge originates. This size
+     * is equal to the stack size at the "jump" instruction to which this edge
+     * corresponds, relatively to the stack size at the beginning of the
+     * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used,
+     * this field is the kind of this control flow graph edge (i.e. NORMAL or
+     * EXCEPTION).
+     */
+    int info;
+
+    /**
+     * The successor block of the basic block from which this edge originates.
+     */
+    Label successor;
+
+    /**
+     * The next edge in the list of successors of the originating basic block.
+     * See {@link Label#successors successors}.
+     */
+    Edge next;
+}
diff --git a/src/jvm/clojure/asm/FieldVisitor.java b/src/jvm/clojure/asm/FieldVisitor.java
new file mode 100644
index 0000000..b9df738
--- /dev/null
+++ b/src/jvm/clojure/asm/FieldVisitor.java
@@ -0,0 +1,121 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A visitor to visit a Java field. The methods of this class must be called in
+ * the following order: ( <tt>visitAnnotation</tt> | <tt>visitAttribute</tt> )*
+ * <tt>visitEnd</tt>.
+ *
+ * @author Eric Bruneton
+ */
+public abstract class FieldVisitor {
+
+    /**
+     * The ASM API version implemented by this visitor. The value of this field
+     * must be one of {@link Opcodes#ASM4}.
+     */
+    protected final int api;
+
+    /**
+     * The field visitor to which this visitor must delegate method calls. May
+     * be null.
+     */
+    protected FieldVisitor fv;
+
+    /**
+     * Constructs a new {@link FieldVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     */
+    public FieldVisitor(final int api) {
+        this(api, null);
+    }
+
+    /**
+     * Constructs a new {@link FieldVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param fv
+     *            the field visitor to which this visitor must delegate method
+     *            calls. May be null.
+     */
+    public FieldVisitor(final int api, final FieldVisitor fv) {
+        if (api != Opcodes.ASM4) {
+            throw new IllegalArgumentException();
+        }
+        this.api = api;
+        this.fv = fv;
+    }
+
+    /**
+     * Visits an annotation of the field.
+     *
+     * @param desc
+     *            the class descriptor of the annotation class.
+     * @param visible
+     *            <tt>true</tt> if the annotation is visible at runtime.
+     * @return a visitor to visit the annotation values, or <tt>null</tt> if
+     *         this visitor is not interested in visiting this annotation.
+     */
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        if (fv != null) {
+            return fv.visitAnnotation(desc, visible);
+        }
+        return null;
+    }
+
+    /**
+     * Visits a non standard attribute of the field.
+     *
+     * @param attr
+     *            an attribute.
+     */
+    public void visitAttribute(Attribute attr) {
+        if (fv != null) {
+            fv.visitAttribute(attr);
+        }
+    }
+
+    /**
+     * Visits the end of the field. This method, which is the last one to be
+     * called, is used to inform the visitor that all the annotations and
+     * attributes of the field have been visited.
+     */
+    public void visitEnd() {
+        if (fv != null) {
+            fv.visitEnd();
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/FieldWriter.java b/src/jvm/clojure/asm/FieldWriter.java
new file mode 100644
index 0000000..ccd32ec
--- /dev/null
+++ b/src/jvm/clojure/asm/FieldWriter.java
@@ -0,0 +1,273 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * An {@link FieldVisitor} that generates Java fields in bytecode form.
+ *
+ * @author Eric Bruneton
+ */
+final class FieldWriter extends FieldVisitor {
+
+    /**
+     * The class writer to which this field must be added.
+     */
+    private final ClassWriter cw;
+
+    /**
+     * Access flags of this field.
+     */
+    private final int access;
+
+    /**
+     * The index of the constant pool item that contains the name of this
+     * method.
+     */
+    private final int name;
+
+    /**
+     * The index of the constant pool item that contains the descriptor of this
+     * field.
+     */
+    private final int desc;
+
+    /**
+     * The index of the constant pool item that contains the signature of this
+     * field.
+     */
+    private int signature;
+
+    /**
+     * The index of the constant pool item that contains the constant value of
+     * this field.
+     */
+    private int value;
+
+    /**
+     * The runtime visible annotations of this field. May be <tt>null</tt>.
+     */
+    private AnnotationWriter anns;
+
+    /**
+     * The runtime invisible annotations of this field. May be <tt>null</tt>.
+     */
+    private AnnotationWriter ianns;
+
+    /**
+     * The non standard attributes of this field. May be <tt>null</tt>.
+     */
+    private Attribute attrs;
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a new {@link FieldWriter}.
+     *
+     * @param cw
+     *            the class writer to which this field must be added.
+     * @param access
+     *            the field's access flags (see {@link Opcodes}).
+     * @param name
+     *            the field's name.
+     * @param desc
+     *            the field's descriptor (see {@link Type}).
+     * @param signature
+     *            the field's signature. May be <tt>null</tt>.
+     * @param value
+     *            the field's constant value. May be <tt>null</tt>.
+     */
+    FieldWriter(final ClassWriter cw, final int access, final String name,
+            final String desc, final String signature, final Object value) {
+        super(Opcodes.ASM4);
+        if (cw.firstField == null) {
+            cw.firstField = this;
+        } else {
+            cw.lastField.fv = this;
+        }
+        cw.lastField = this;
+        this.cw = cw;
+        this.access = access;
+        this.name = cw.newUTF8(name);
+        this.desc = cw.newUTF8(desc);
+        if (ClassReader.SIGNATURES && signature != null) {
+            this.signature = cw.newUTF8(signature);
+        }
+        if (value != null) {
+            this.value = cw.newConstItem(value).index;
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the FieldVisitor abstract class
+    // ------------------------------------------------------------------------
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String desc,
+            final boolean visible) {
+        if (!ClassReader.ANNOTATIONS) {
+            return null;
+        }
+        ByteVector bv = new ByteVector();
+        // write type, and reserve space for values count
+        bv.putShort(cw.newUTF8(desc)).putShort(0);
+        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+        if (visible) {
+            aw.next = anns;
+            anns = aw;
+        } else {
+            aw.next = ianns;
+            ianns = aw;
+        }
+        return aw;
+    }
+
+    @Override
+    public void visitAttribute(final Attribute attr) {
+        attr.next = attrs;
+        attrs = attr;
+    }
+
+    @Override
+    public void visitEnd() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the size of this field.
+     *
+     * @return the size of this field.
+     */
+    int getSize() {
+        int size = 8;
+        if (value != 0) {
+            cw.newUTF8("ConstantValue");
+            size += 8;
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((cw.version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                cw.newUTF8("Synthetic");
+                size += 6;
+            }
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            cw.newUTF8("Deprecated");
+            size += 6;
+        }
+        if (ClassReader.SIGNATURES && signature != 0) {
+            cw.newUTF8("Signature");
+            size += 8;
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            cw.newUTF8("RuntimeVisibleAnnotations");
+            size += 8 + anns.getSize();
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            cw.newUTF8("RuntimeInvisibleAnnotations");
+            size += 8 + ianns.getSize();
+        }
+        if (attrs != null) {
+            size += attrs.getSize(cw, null, 0, -1, -1);
+        }
+        return size;
+    }
+
+    /**
+     * Puts the content of this field into the given byte vector.
+     *
+     * @param out
+     *            where the content of this field must be put.
+     */
+    void put(final ByteVector out) {
+        final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
+        int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
+                | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
+        out.putShort(access & ~mask).putShort(name).putShort(desc);
+        int attributeCount = 0;
+        if (value != 0) {
+            ++attributeCount;
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((cw.version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                ++attributeCount;
+            }
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            ++attributeCount;
+        }
+        if (ClassReader.SIGNATURES && signature != 0) {
+            ++attributeCount;
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            ++attributeCount;
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            ++attributeCount;
+        }
+        if (attrs != null) {
+            attributeCount += attrs.getCount();
+        }
+        out.putShort(attributeCount);
+        if (value != 0) {
+            out.putShort(cw.newUTF8("ConstantValue"));
+            out.putInt(2).putShort(value);
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((cw.version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+            }
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+        }
+        if (ClassReader.SIGNATURES && signature != 0) {
+            out.putShort(cw.newUTF8("Signature"));
+            out.putInt(2).putShort(signature);
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+            anns.put(out);
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+            ianns.put(out);
+        }
+        if (attrs != null) {
+            attrs.put(cw, null, 0, -1, -1, out);
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/Frame.java b/src/jvm/clojure/asm/Frame.java
new file mode 100644
index 0000000..9c7e61d
--- /dev/null
+++ b/src/jvm/clojure/asm/Frame.java
@@ -0,0 +1,1453 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * Information about the input and output stack map frames of a basic block.
+ *
+ * @author Eric Bruneton
+ */
+final class Frame {
+
+    /*
+     * Frames are computed in a two steps process: during the visit of each
+     * instruction, the state of the frame at the end of current basic block is
+     * updated by simulating the action of the instruction on the previous state
+     * of this so called "output frame". In visitMaxs, a fix point algorithm is
+     * used to compute the "input frame" of each basic block, i.e. the stack map
+     * frame at the beginning of the basic block, starting from the input frame
+     * of the first basic block (which is computed from the method descriptor),
+     * and by using the previously computed output frames to compute the input
+     * state of the other blocks.
+     *
+     * All output and input frames are stored as arrays of integers. Reference
+     * and array types are represented by an index into a type table (which is
+     * not the same as the constant pool of the class, in order to avoid adding
+     * unnecessary constants in the pool - not all computed frames will end up
+     * being stored in the stack map table). This allows very fast type
+     * comparisons.
+     *
+     * Output stack map frames are computed relatively to the input frame of the
+     * basic block, which is not yet known when output frames are computed. It
+     * is therefore necessary to be able to represent abstract types such as
+     * "the type at position x in the input frame locals" or "the type at
+     * position x from the top of the input frame stack" or even "the type at
+     * position x in the input frame, with y more (or less) array dimensions".
+     * This explains the rather complicated type format used in output frames.
+     *
+     * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a
+     * signed number of array dimensions (from -8 to 7). KIND is either BASE,
+     * LOCAL or STACK. BASE is used for types that are not relative to the input
+     * frame. LOCAL is used for types that are relative to the input local
+     * variable types. STACK is used for types that are relative to the input
+     * stack types. VALUE depends on KIND. For LOCAL types, it is an index in
+     * the input local variable types. For STACK types, it is a position
+     * relatively to the top of input frame stack. For BASE types, it is either
+     * one of the constants defined in FrameVisitor, or for OBJECT and
+     * UNINITIALIZED types, a tag and an index in the type table.
+     *
+     * Output frames can contain types of any kind and with a positive or
+     * negative dimension (and even unassigned types, represented by 0 - which
+     * does not correspond to any valid type value). Input frames can only
+     * contain BASE types of positive or null dimension. In all cases the type
+     * table contains only internal type names (array type descriptors are
+     * forbidden - dimensions must be represented through the DIM field).
+     *
+     * The LONG and DOUBLE types are always represented by using two slots (LONG
+     * + TOP or DOUBLE + TOP), for local variable types as well as in the
+     * operand stack. This is necessary to be able to simulate DUPx_y
+     * instructions, whose effect would be dependent on the actual type values
+     * if types were always represented by a single slot in the stack (and this
+     * is not possible, since actual type values are not always known - cf LOCAL
+     * and STACK type kinds).
+     */
+
+    /**
+     * Mask to get the dimension of a frame type. This dimension is a signed
+     * integer between -8 and 7.
+     */
+    static final int DIM = 0xF0000000;
+
+    /**
+     * Constant to be added to a type to get a type with one more dimension.
+     */
+    static final int ARRAY_OF = 0x10000000;
+
+    /**
+     * Constant to be added to a type to get a type with one less dimension.
+     */
+    static final int ELEMENT_OF = 0xF0000000;
+
+    /**
+     * Mask to get the kind of a frame type.
+     *
+     * @see #BASE
+     * @see #LOCAL
+     * @see #STACK
+     */
+    static final int KIND = 0xF000000;
+
+    /**
+     * Flag used for LOCAL and STACK types. Indicates that if this type happens
+     * to be a long or double type (during the computations of input frames),
+     * then it must be set to TOP because the second word of this value has been
+     * reused to store other data in the basic block. Hence the first word no
+     * longer stores a valid long or double value.
+     */
+    static final int TOP_IF_LONG_OR_DOUBLE = 0x800000;
+
+    /**
+     * Mask to get the value of a frame type.
+     */
+    static final int VALUE = 0x7FFFFF;
+
+    /**
+     * Mask to get the kind of base types.
+     */
+    static final int BASE_KIND = 0xFF00000;
+
+    /**
+     * Mask to get the value of base types.
+     */
+    static final int BASE_VALUE = 0xFFFFF;
+
+    /**
+     * Kind of the types that are not relative to an input stack map frame.
+     */
+    static final int BASE = 0x1000000;
+
+    /**
+     * Base kind of the base reference types. The BASE_VALUE of such types is an
+     * index into the type table.
+     */
+    static final int OBJECT = BASE | 0x700000;
+
+    /**
+     * Base kind of the uninitialized base types. The BASE_VALUE of such types
+     * in an index into the type table (the Item at that index contains both an
+     * instruction offset and an internal class name).
+     */
+    static final int UNINITIALIZED = BASE | 0x800000;
+
+    /**
+     * Kind of the types that are relative to the local variable types of an
+     * input stack map frame. The value of such types is a local variable index.
+     */
+    private static final int LOCAL = 0x2000000;
+
+    /**
+     * Kind of the the types that are relative to the stack of an input stack
+     * map frame. The value of such types is a position relatively to the top of
+     * this stack.
+     */
+    private static final int STACK = 0x3000000;
+
+    /**
+     * The TOP type. This is a BASE type.
+     */
+    static final int TOP = BASE | 0;
+
+    /**
+     * The BOOLEAN type. This is a BASE type mainly used for array types.
+     */
+    static final int BOOLEAN = BASE | 9;
+
+    /**
+     * The BYTE type. This is a BASE type mainly used for array types.
+     */
+    static final int BYTE = BASE | 10;
+
+    /**
+     * The CHAR type. This is a BASE type mainly used for array types.
+     */
+    static final int CHAR = BASE | 11;
+
+    /**
+     * The SHORT type. This is a BASE type mainly used for array types.
+     */
+    static final int SHORT = BASE | 12;
+
+    /**
+     * The INTEGER type. This is a BASE type.
+     */
+    static final int INTEGER = BASE | 1;
+
+    /**
+     * The FLOAT type. This is a BASE type.
+     */
+    static final int FLOAT = BASE | 2;
+
+    /**
+     * The DOUBLE type. This is a BASE type.
+     */
+    static final int DOUBLE = BASE | 3;
+
+    /**
+     * The LONG type. This is a BASE type.
+     */
+    static final int LONG = BASE | 4;
+
+    /**
+     * The NULL type. This is a BASE type.
+     */
+    static final int NULL = BASE | 5;
+
+    /**
+     * The UNINITIALIZED_THIS type. This is a BASE type.
+     */
+    static final int UNINITIALIZED_THIS = BASE | 6;
+
+    /**
+     * The stack size variation corresponding to each JVM instruction. This
+     * stack variation is equal to the size of the values produced by an
+     * instruction, minus the size of the values consumed by this instruction.
+     */
+    static final int[] SIZE;
+
+    /**
+     * Computes the stack size variation corresponding to each JVM instruction.
+     */
+    static {
+        int i;
+        int[] b = new int[202];
+        String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD"
+                + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD"
+                + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED"
+                + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE";
+        for (i = 0; i < b.length; ++i) {
+            b[i] = s.charAt(i) - 'E';
+        }
+        SIZE = b;
+
+        // code to generate the above string
+        //
+        // int NA = 0; // not applicable (unused opcode or variable size opcode)
+        //
+        // b = new int[] {
+        // 0, //NOP, // visitInsn
+        // 1, //ACONST_NULL, // -
+        // 1, //ICONST_M1, // -
+        // 1, //ICONST_0, // -
+        // 1, //ICONST_1, // -
+        // 1, //ICONST_2, // -
+        // 1, //ICONST_3, // -
+        // 1, //ICONST_4, // -
+        // 1, //ICONST_5, // -
+        // 2, //LCONST_0, // -
+        // 2, //LCONST_1, // -
+        // 1, //FCONST_0, // -
+        // 1, //FCONST_1, // -
+        // 1, //FCONST_2, // -
+        // 2, //DCONST_0, // -
+        // 2, //DCONST_1, // -
+        // 1, //BIPUSH, // visitIntInsn
+        // 1, //SIPUSH, // -
+        // 1, //LDC, // visitLdcInsn
+        // NA, //LDC_W, // -
+        // NA, //LDC2_W, // -
+        // 1, //ILOAD, // visitVarInsn
+        // 2, //LLOAD, // -
+        // 1, //FLOAD, // -
+        // 2, //DLOAD, // -
+        // 1, //ALOAD, // -
+        // NA, //ILOAD_0, // -
+        // NA, //ILOAD_1, // -
+        // NA, //ILOAD_2, // -
+        // NA, //ILOAD_3, // -
+        // NA, //LLOAD_0, // -
+        // NA, //LLOAD_1, // -
+        // NA, //LLOAD_2, // -
+        // NA, //LLOAD_3, // -
+        // NA, //FLOAD_0, // -
+        // NA, //FLOAD_1, // -
+        // NA, //FLOAD_2, // -
+        // NA, //FLOAD_3, // -
+        // NA, //DLOAD_0, // -
+        // NA, //DLOAD_1, // -
+        // NA, //DLOAD_2, // -
+        // NA, //DLOAD_3, // -
+        // NA, //ALOAD_0, // -
+        // NA, //ALOAD_1, // -
+        // NA, //ALOAD_2, // -
+        // NA, //ALOAD_3, // -
+        // -1, //IALOAD, // visitInsn
+        // 0, //LALOAD, // -
+        // -1, //FALOAD, // -
+        // 0, //DALOAD, // -
+        // -1, //AALOAD, // -
+        // -1, //BALOAD, // -
+        // -1, //CALOAD, // -
+        // -1, //SALOAD, // -
+        // -1, //ISTORE, // visitVarInsn
+        // -2, //LSTORE, // -
+        // -1, //FSTORE, // -
+        // -2, //DSTORE, // -
+        // -1, //ASTORE, // -
+        // NA, //ISTORE_0, // -
+        // NA, //ISTORE_1, // -
+        // NA, //ISTORE_2, // -
+        // NA, //ISTORE_3, // -
+        // NA, //LSTORE_0, // -
+        // NA, //LSTORE_1, // -
+        // NA, //LSTORE_2, // -
+        // NA, //LSTORE_3, // -
+        // NA, //FSTORE_0, // -
+        // NA, //FSTORE_1, // -
+        // NA, //FSTORE_2, // -
+        // NA, //FSTORE_3, // -
+        // NA, //DSTORE_0, // -
+        // NA, //DSTORE_1, // -
+        // NA, //DSTORE_2, // -
+        // NA, //DSTORE_3, // -
+        // NA, //ASTORE_0, // -
+        // NA, //ASTORE_1, // -
+        // NA, //ASTORE_2, // -
+        // NA, //ASTORE_3, // -
+        // -3, //IASTORE, // visitInsn
+        // -4, //LASTORE, // -
+        // -3, //FASTORE, // -
+        // -4, //DASTORE, // -
+        // -3, //AASTORE, // -
+        // -3, //BASTORE, // -
+        // -3, //CASTORE, // -
+        // -3, //SASTORE, // -
+        // -1, //POP, // -
+        // -2, //POP2, // -
+        // 1, //DUP, // -
+        // 1, //DUP_X1, // -
+        // 1, //DUP_X2, // -
+        // 2, //DUP2, // -
+        // 2, //DUP2_X1, // -
+        // 2, //DUP2_X2, // -
+        // 0, //SWAP, // -
+        // -1, //IADD, // -
+        // -2, //LADD, // -
+        // -1, //FADD, // -
+        // -2, //DADD, // -
+        // -1, //ISUB, // -
+        // -2, //LSUB, // -
+        // -1, //FSUB, // -
+        // -2, //DSUB, // -
+        // -1, //IMUL, // -
+        // -2, //LMUL, // -
+        // -1, //FMUL, // -
+        // -2, //DMUL, // -
+        // -1, //IDIV, // -
+        // -2, //LDIV, // -
+        // -1, //FDIV, // -
+        // -2, //DDIV, // -
+        // -1, //IREM, // -
+        // -2, //LREM, // -
+        // -1, //FREM, // -
+        // -2, //DREM, // -
+        // 0, //INEG, // -
+        // 0, //LNEG, // -
+        // 0, //FNEG, // -
+        // 0, //DNEG, // -
+        // -1, //ISHL, // -
+        // -1, //LSHL, // -
+        // -1, //ISHR, // -
+        // -1, //LSHR, // -
+        // -1, //IUSHR, // -
+        // -1, //LUSHR, // -
+        // -1, //IAND, // -
+        // -2, //LAND, // -
+        // -1, //IOR, // -
+        // -2, //LOR, // -
+        // -1, //IXOR, // -
+        // -2, //LXOR, // -
+        // 0, //IINC, // visitIincInsn
+        // 1, //I2L, // visitInsn
+        // 0, //I2F, // -
+        // 1, //I2D, // -
+        // -1, //L2I, // -
+        // -1, //L2F, // -
+        // 0, //L2D, // -
+        // 0, //F2I, // -
+        // 1, //F2L, // -
+        // 1, //F2D, // -
+        // -1, //D2I, // -
+        // 0, //D2L, // -
+        // -1, //D2F, // -
+        // 0, //I2B, // -
+        // 0, //I2C, // -
+        // 0, //I2S, // -
+        // -3, //LCMP, // -
+        // -1, //FCMPL, // -
+        // -1, //FCMPG, // -
+        // -3, //DCMPL, // -
+        // -3, //DCMPG, // -
+        // -1, //IFEQ, // visitJumpInsn
+        // -1, //IFNE, // -
+        // -1, //IFLT, // -
+        // -1, //IFGE, // -
+        // -1, //IFGT, // -
+        // -1, //IFLE, // -
+        // -2, //IF_ICMPEQ, // -
+        // -2, //IF_ICMPNE, // -
+        // -2, //IF_ICMPLT, // -
+        // -2, //IF_ICMPGE, // -
+        // -2, //IF_ICMPGT, // -
+        // -2, //IF_ICMPLE, // -
+        // -2, //IF_ACMPEQ, // -
+        // -2, //IF_ACMPNE, // -
+        // 0, //GOTO, // -
+        // 1, //JSR, // -
+        // 0, //RET, // visitVarInsn
+        // -1, //TABLESWITCH, // visiTableSwitchInsn
+        // -1, //LOOKUPSWITCH, // visitLookupSwitch
+        // -1, //IRETURN, // visitInsn
+        // -2, //LRETURN, // -
+        // -1, //FRETURN, // -
+        // -2, //DRETURN, // -
+        // -1, //ARETURN, // -
+        // 0, //RETURN, // -
+        // NA, //GETSTATIC, // visitFieldInsn
+        // NA, //PUTSTATIC, // -
+        // NA, //GETFIELD, // -
+        // NA, //PUTFIELD, // -
+        // NA, //INVOKEVIRTUAL, // visitMethodInsn
+        // NA, //INVOKESPECIAL, // -
+        // NA, //INVOKESTATIC, // -
+        // NA, //INVOKEINTERFACE, // -
+        // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn
+        // 1, //NEW, // visitTypeInsn
+        // 0, //NEWARRAY, // visitIntInsn
+        // 0, //ANEWARRAY, // visitTypeInsn
+        // 0, //ARRAYLENGTH, // visitInsn
+        // NA, //ATHROW, // -
+        // 0, //CHECKCAST, // visitTypeInsn
+        // 0, //INSTANCEOF, // -
+        // -1, //MONITORENTER, // visitInsn
+        // -1, //MONITOREXIT, // -
+        // NA, //WIDE, // NOT VISITED
+        // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn
+        // -1, //IFNULL, // visitJumpInsn
+        // -1, //IFNONNULL, // -
+        // NA, //GOTO_W, // -
+        // NA, //JSR_W, // -
+        // };
+        // for (i = 0; i < b.length; ++i) {
+        // System.err.print((char)('E' + b[i]));
+        // }
+        // System.err.println();
+    }
+
+    /**
+     * The label (i.e. basic block) to which these input and output stack map
+     * frames correspond.
+     */
+    Label owner;
+
+    /**
+     * The input stack map frame locals.
+     */
+    int[] inputLocals;
+
+    /**
+     * The input stack map frame stack.
+     */
+    int[] inputStack;
+
+    /**
+     * The output stack map frame locals.
+     */
+    private int[] outputLocals;
+
+    /**
+     * The output stack map frame stack.
+     */
+    private int[] outputStack;
+
+    /**
+     * Relative size of the output stack. The exact semantics of this field
+     * depends on the algorithm that is used.
+     *
+     * When only the maximum stack size is computed, this field is the size of
+     * the output stack relatively to the top of the input stack.
+     *
+     * When the stack map frames are completely computed, this field is the
+     * actual number of types in {@link #outputStack}.
+     */
+    private int outputStackTop;
+
+    /**
+     * Number of types that are initialized in the basic block.
+     *
+     * @see #initializations
+     */
+    private int initializationCount;
+
+    /**
+     * The types that are initialized in the basic block. A constructor
+     * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace
+     * <i>every occurence</i> of this type in the local variables and in the
+     * operand stack. This cannot be done during the first phase of the
+     * algorithm since, during this phase, the local variables and the operand
+     * stack are not completely computed. It is therefore necessary to store the
+     * types on which constructors are invoked in the basic block, in order to
+     * do this replacement during the second phase of the algorithm, where the
+     * frames are fully computed. Note that this array can contain types that
+     * are relative to input locals or to the input stack (see below for the
+     * description of the algorithm).
+     */
+    private int[] initializations;
+
+    /**
+     * Returns the output frame local variable type at the given index.
+     *
+     * @param local
+     *            the index of the local that must be returned.
+     * @return the output frame local variable type at the given index.
+     */
+    private int get(final int local) {
+        if (outputLocals == null || local >= outputLocals.length) {
+            // this local has never been assigned in this basic block,
+            // so it is still equal to its value in the input frame
+            return LOCAL | local;
+        } else {
+            int type = outputLocals[local];
+            if (type == 0) {
+                // this local has never been assigned in this basic block,
+                // so it is still equal to its value in the input frame
+                type = outputLocals[local] = LOCAL | local;
+            }
+            return type;
+        }
+    }
+
+    /**
+     * Sets the output frame local variable type at the given index.
+     *
+     * @param local
+     *            the index of the local that must be set.
+     * @param type
+     *            the value of the local that must be set.
+     */
+    private void set(final int local, final int type) {
+        // creates and/or resizes the output local variables array if necessary
+        if (outputLocals == null) {
+            outputLocals = new int[10];
+        }
+        int n = outputLocals.length;
+        if (local >= n) {
+            int[] t = new int[Math.max(local + 1, 2 * n)];
+            System.arraycopy(outputLocals, 0, t, 0, n);
+            outputLocals = t;
+        }
+        // sets the local variable
+        outputLocals[local] = type;
+    }
+
+    /**
+     * Pushes a new type onto the output frame stack.
+     *
+     * @param type
+     *            the type that must be pushed.
+     */
+    private void push(final int type) {
+        // creates and/or resizes the output stack array if necessary
+        if (outputStack == null) {
+            outputStack = new int[10];
+        }
+        int n = outputStack.length;
+        if (outputStackTop >= n) {
+            int[] t = new int[Math.max(outputStackTop + 1, 2 * n)];
+            System.arraycopy(outputStack, 0, t, 0, n);
+            outputStack = t;
+        }
+        // pushes the type on the output stack
+        outputStack[outputStackTop++] = type;
+        // updates the maximun height reached by the output stack, if needed
+        int top = owner.inputStackTop + outputStackTop;
+        if (top > owner.outputStackMax) {
+            owner.outputStackMax = top;
+        }
+    }
+
+    /**
+     * Pushes a new type onto the output frame stack.
+     *
+     * @param cw
+     *            the ClassWriter to which this label belongs.
+     * @param desc
+     *            the descriptor of the type to be pushed. Can also be a method
+     *            descriptor (in this case this method pushes its return type
+     *            onto the output frame stack).
+     */
+    private void push(final ClassWriter cw, final String desc) {
+        int type = type(cw, desc);
+        if (type != 0) {
+            push(type);
+            if (type == LONG || type == DOUBLE) {
+                push(TOP);
+            }
+        }
+    }
+
+    /**
+     * Returns the int encoding of the given type.
+     *
+     * @param cw
+     *            the ClassWriter to which this label belongs.
+     * @param desc
+     *            a type descriptor.
+     * @return the int encoding of the given type.
+     */
+    private static int type(final ClassWriter cw, final String desc) {
+        String t;
+        int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
+        switch (desc.charAt(index)) {
+        case 'V':
+            return 0;
+        case 'Z':
+        case 'C':
+        case 'B':
+        case 'S':
+        case 'I':
+            return INTEGER;
+        case 'F':
+            return FLOAT;
+        case 'J':
+            return LONG;
+        case 'D':
+            return DOUBLE;
+        case 'L':
+            // stores the internal name, not the descriptor!
+            t = desc.substring(index + 1, desc.length() - 1);
+            return OBJECT | cw.addType(t);
+            // case '[':
+        default:
+            // extracts the dimensions and the element type
+            int data;
+            int dims = index + 1;
+            while (desc.charAt(dims) == '[') {
+                ++dims;
+            }
+            switch (desc.charAt(dims)) {
+            case 'Z':
+                data = BOOLEAN;
+                break;
+            case 'C':
+                data = CHAR;
+                break;
+            case 'B':
+                data = BYTE;
+                break;
+            case 'S':
+                data = SHORT;
+                break;
+            case 'I':
+                data = INTEGER;
+                break;
+            case 'F':
+                data = FLOAT;
+                break;
+            case 'J':
+                data = LONG;
+                break;
+            case 'D':
+                data = DOUBLE;
+                break;
+            // case 'L':
+            default:
+                // stores the internal name, not the descriptor
+                t = desc.substring(dims + 1, desc.length() - 1);
+                data = OBJECT | cw.addType(t);
+            }
+            return (dims - index) << 28 | data;
+        }
+    }
+
+    /**
+     * Pops a type from the output frame stack and returns its value.
+     *
+     * @return the type that has been popped from the output frame stack.
+     */
+    private int pop() {
+        if (outputStackTop > 0) {
+            return outputStack[--outputStackTop];
+        } else {
+            // if the output frame stack is empty, pops from the input stack
+            return STACK | -(--owner.inputStackTop);
+        }
+    }
+
+    /**
+     * Pops the given number of types from the output frame stack.
+     *
+     * @param elements
+     *            the number of types that must be popped.
+     */
+    private void pop(final int elements) {
+        if (outputStackTop >= elements) {
+            outputStackTop -= elements;
+        } else {
+            // if the number of elements to be popped is greater than the number
+            // of elements in the output stack, clear it, and pops the remaining
+            // elements from the input stack.
+            owner.inputStackTop -= elements - outputStackTop;
+            outputStackTop = 0;
+        }
+    }
+
+    /**
+     * Pops a type from the output frame stack.
+     *
+     * @param desc
+     *            the descriptor of the type to be popped. Can also be a method
+     *            descriptor (in this case this method pops the types
+     *            corresponding to the method arguments).
+     */
+    private void pop(final String desc) {
+        char c = desc.charAt(0);
+        if (c == '(') {
+            pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1);
+        } else if (c == 'J' || c == 'D') {
+            pop(2);
+        } else {
+            pop(1);
+        }
+    }
+
+    /**
+     * Adds a new type to the list of types on which a constructor is invoked in
+     * the basic block.
+     *
+     * @param var
+     *            a type on a which a constructor is invoked.
+     */
+    private void init(final int var) {
+        // creates and/or resizes the initializations array if necessary
+        if (initializations == null) {
+            initializations = new int[2];
+        }
+        int n = initializations.length;
+        if (initializationCount >= n) {
+            int[] t = new int[Math.max(initializationCount + 1, 2 * n)];
+            System.arraycopy(initializations, 0, t, 0, n);
+            initializations = t;
+        }
+        // stores the type to be initialized
+        initializations[initializationCount++] = var;
+    }
+
+    /**
+     * Replaces the given type with the appropriate type if it is one of the
+     * types on which a constructor is invoked in the basic block.
+     *
+     * @param cw
+     *            the ClassWriter to which this label belongs.
+     * @param t
+     *            a type
+     * @return t or, if t is one of the types on which a constructor is invoked
+     *         in the basic block, the type corresponding to this constructor.
+     */
+    private int init(final ClassWriter cw, final int t) {
+        int s;
+        if (t == UNINITIALIZED_THIS) {
+            s = OBJECT | cw.addType(cw.thisName);
+        } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) {
+            String type = cw.typeTable[t & BASE_VALUE].strVal1;
+            s = OBJECT | cw.addType(type);
+        } else {
+            return t;
+        }
+        for (int j = 0; j < initializationCount; ++j) {
+            int u = initializations[j];
+            int dim = u & DIM;
+            int kind = u & KIND;
+            if (kind == LOCAL) {
+                u = dim + inputLocals[u & VALUE];
+            } else if (kind == STACK) {
+                u = dim + inputStack[inputStack.length - (u & VALUE)];
+            }
+            if (t == u) {
+                return s;
+            }
+        }
+        return t;
+    }
+
+    /**
+     * Initializes the input frame of the first basic block from the method
+     * descriptor.
+     *
+     * @param cw
+     *            the ClassWriter to which this label belongs.
+     * @param access
+     *            the access flags of the method to which this label belongs.
+     * @param args
+     *            the formal parameter types of this method.
+     * @param maxLocals
+     *            the maximum number of local variables of this method.
+     */
+    void initInputFrame(final ClassWriter cw, final int access,
+            final Type[] args, final int maxLocals) {
+        inputLocals = new int[maxLocals];
+        inputStack = new int[0];
+        int i = 0;
+        if ((access & Opcodes.ACC_STATIC) == 0) {
+            if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) {
+                inputLocals[i++] = OBJECT | cw.addType(cw.thisName);
+            } else {
+                inputLocals[i++] = UNINITIALIZED_THIS;
+            }
+        }
+        for (int j = 0; j < args.length; ++j) {
+            int t = type(cw, args[j].getDescriptor());
+            inputLocals[i++] = t;
+            if (t == LONG || t == DOUBLE) {
+                inputLocals[i++] = TOP;
+            }
+        }
+        while (i < maxLocals) {
+            inputLocals[i++] = TOP;
+        }
+    }
+
+    /**
+     * Simulates the action of the given instruction on the output stack frame.
+     *
+     * @param opcode
+     *            the opcode of the instruction.
+     * @param arg
+     *            the operand of the instruction, if any.
+     * @param cw
+     *            the class writer to which this label belongs.
+     * @param item
+     *            the operand of the instructions, if any.
+     */
+    void execute(final int opcode, final int arg, final ClassWriter cw,
+            final Item item) {
+        int t1, t2, t3, t4;
+        switch (opcode) {
+        case Opcodes.NOP:
+        case Opcodes.INEG:
+        case Opcodes.LNEG:
+        case Opcodes.FNEG:
+        case Opcodes.DNEG:
+        case Opcodes.I2B:
+        case Opcodes.I2C:
+        case Opcodes.I2S:
+        case Opcodes.GOTO:
+        case Opcodes.RETURN:
+            break;
+        case Opcodes.ACONST_NULL:
+            push(NULL);
+            break;
+        case Opcodes.ICONST_M1:
+        case Opcodes.ICONST_0:
+        case Opcodes.ICONST_1:
+        case Opcodes.ICONST_2:
+        case Opcodes.ICONST_3:
+        case Opcodes.ICONST_4:
+        case Opcodes.ICONST_5:
+        case Opcodes.BIPUSH:
+        case Opcodes.SIPUSH:
+        case Opcodes.ILOAD:
+            push(INTEGER);
+            break;
+        case Opcodes.LCONST_0:
+        case Opcodes.LCONST_1:
+        case Opcodes.LLOAD:
+            push(LONG);
+            push(TOP);
+            break;
+        case Opcodes.FCONST_0:
+        case Opcodes.FCONST_1:
+        case Opcodes.FCONST_2:
+        case Opcodes.FLOAD:
+            push(FLOAT);
+            break;
+        case Opcodes.DCONST_0:
+        case Opcodes.DCONST_1:
+        case Opcodes.DLOAD:
+            push(DOUBLE);
+            push(TOP);
+            break;
+        case Opcodes.LDC:
+            switch (item.type) {
+            case ClassWriter.INT:
+                push(INTEGER);
+                break;
+            case ClassWriter.LONG:
+                push(LONG);
+                push(TOP);
+                break;
+            case ClassWriter.FLOAT:
+                push(FLOAT);
+                break;
+            case ClassWriter.DOUBLE:
+                push(DOUBLE);
+                push(TOP);
+                break;
+            case ClassWriter.CLASS:
+                push(OBJECT | cw.addType("java/lang/Class"));
+                break;
+            case ClassWriter.STR:
+                push(OBJECT | cw.addType("java/lang/String"));
+                break;
+            case ClassWriter.MTYPE:
+                push(OBJECT | cw.addType("java/lang/invoke/MethodType"));
+                break;
+            // case ClassWriter.HANDLE_BASE + [1..9]:
+            default:
+                push(OBJECT | cw.addType("java/lang/invoke/MethodHandle"));
+            }
+            break;
+        case Opcodes.ALOAD:
+            push(get(arg));
+            break;
+        case Opcodes.IALOAD:
+        case Opcodes.BALOAD:
+        case Opcodes.CALOAD:
+        case Opcodes.SALOAD:
+            pop(2);
+            push(INTEGER);
+            break;
+        case Opcodes.LALOAD:
+        case Opcodes.D2L:
+            pop(2);
+            push(LONG);
+            push(TOP);
+            break;
+        case Opcodes.FALOAD:
+            pop(2);
+            push(FLOAT);
+            break;
+        case Opcodes.DALOAD:
+        case Opcodes.L2D:
+            pop(2);
+            push(DOUBLE);
+            push(TOP);
+            break;
+        case Opcodes.AALOAD:
+            pop(1);
+            t1 = pop();
+            push(ELEMENT_OF + t1);
+            break;
+        case Opcodes.ISTORE:
+        case Opcodes.FSTORE:
+        case Opcodes.ASTORE:
+            t1 = pop();
+            set(arg, t1);
+            if (arg > 0) {
+                t2 = get(arg - 1);
+                // if t2 is of kind STACK or LOCAL we cannot know its size!
+                if (t2 == LONG || t2 == DOUBLE) {
+                    set(arg - 1, TOP);
+                } else if ((t2 & KIND) != BASE) {
+                    set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE);
+                }
+            }
+            break;
+        case Opcodes.LSTORE:
+        case Opcodes.DSTORE:
+            pop(1);
+            t1 = pop();
+            set(arg, t1);
+            set(arg + 1, TOP);
+            if (arg > 0) {
+                t2 = get(arg - 1);
+                // if t2 is of kind STACK or LOCAL we cannot know its size!
+                if (t2 == LONG || t2 == DOUBLE) {
+                    set(arg - 1, TOP);
+                } else if ((t2 & KIND) != BASE) {
+                    set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE);
+                }
+            }
+            break;
+        case Opcodes.IASTORE:
+        case Opcodes.BASTORE:
+        case Opcodes.CASTORE:
+        case Opcodes.SASTORE:
+        case Opcodes.FASTORE:
+        case Opcodes.AASTORE:
+            pop(3);
+            break;
+        case Opcodes.LASTORE:
+        case Opcodes.DASTORE:
+            pop(4);
+            break;
+        case Opcodes.POP:
+        case Opcodes.IFEQ:
+        case Opcodes.IFNE:
+        case Opcodes.IFLT:
+        case Opcodes.IFGE:
+        case Opcodes.IFGT:
+        case Opcodes.IFLE:
+        case Opcodes.IRETURN:
+        case Opcodes.FRETURN:
+        case Opcodes.ARETURN:
+        case Opcodes.TABLESWITCH:
+        case Opcodes.LOOKUPSWITCH:
+        case Opcodes.ATHROW:
+        case Opcodes.MONITORENTER:
+        case Opcodes.MONITOREXIT:
+        case Opcodes.IFNULL:
+        case Opcodes.IFNONNULL:
+            pop(1);
+            break;
+        case Opcodes.POP2:
+        case Opcodes.IF_ICMPEQ:
+        case Opcodes.IF_ICMPNE:
+        case Opcodes.IF_ICMPLT:
+        case Opcodes.IF_ICMPGE:
+        case Opcodes.IF_ICMPGT:
+        case Opcodes.IF_ICMPLE:
+        case Opcodes.IF_ACMPEQ:
+        case Opcodes.IF_ACMPNE:
+        case Opcodes.LRETURN:
+        case Opcodes.DRETURN:
+            pop(2);
+            break;
+        case Opcodes.DUP:
+            t1 = pop();
+            push(t1);
+            push(t1);
+            break;
+        case Opcodes.DUP_X1:
+            t1 = pop();
+            t2 = pop();
+            push(t1);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP_X2:
+            t1 = pop();
+            t2 = pop();
+            t3 = pop();
+            push(t1);
+            push(t3);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP2:
+            t1 = pop();
+            t2 = pop();
+            push(t2);
+            push(t1);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP2_X1:
+            t1 = pop();
+            t2 = pop();
+            t3 = pop();
+            push(t2);
+            push(t1);
+            push(t3);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP2_X2:
+            t1 = pop();
+            t2 = pop();
+            t3 = pop();
+            t4 = pop();
+            push(t2);
+            push(t1);
+            push(t4);
+            push(t3);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.SWAP:
+            t1 = pop();
+            t2 = pop();
+            push(t1);
+            push(t2);
+            break;
+        case Opcodes.IADD:
+        case Opcodes.ISUB:
+        case Opcodes.IMUL:
+        case Opcodes.IDIV:
+        case Opcodes.IREM:
+        case Opcodes.IAND:
+        case Opcodes.IOR:
+        case Opcodes.IXOR:
+        case Opcodes.ISHL:
+        case Opcodes.ISHR:
+        case Opcodes.IUSHR:
+        case Opcodes.L2I:
+        case Opcodes.D2I:
+        case Opcodes.FCMPL:
+        case Opcodes.FCMPG:
+            pop(2);
+            push(INTEGER);
+            break;
+        case Opcodes.LADD:
+        case Opcodes.LSUB:
+        case Opcodes.LMUL:
+        case Opcodes.LDIV:
+        case Opcodes.LREM:
+        case Opcodes.LAND:
+        case Opcodes.LOR:
+        case Opcodes.LXOR:
+            pop(4);
+            push(LONG);
+            push(TOP);
+            break;
+        case Opcodes.FADD:
+        case Opcodes.FSUB:
+        case Opcodes.FMUL:
+        case Opcodes.FDIV:
+        case Opcodes.FREM:
+        case Opcodes.L2F:
+        case Opcodes.D2F:
+            pop(2);
+            push(FLOAT);
+            break;
+        case Opcodes.DADD:
+        case Opcodes.DSUB:
+        case Opcodes.DMUL:
+        case Opcodes.DDIV:
+        case Opcodes.DREM:
+            pop(4);
+            push(DOUBLE);
+            push(TOP);
+            break;
+        case Opcodes.LSHL:
+        case Opcodes.LSHR:
+        case Opcodes.LUSHR:
+            pop(3);
+            push(LONG);
+            push(TOP);
+            break;
+        case Opcodes.IINC:
+            set(arg, INTEGER);
+            break;
+        case Opcodes.I2L:
+        case Opcodes.F2L:
+            pop(1);
+            push(LONG);
+            push(TOP);
+            break;
+        case Opcodes.I2F:
+            pop(1);
+            push(FLOAT);
+            break;
+        case Opcodes.I2D:
+        case Opcodes.F2D:
+            pop(1);
+            push(DOUBLE);
+            push(TOP);
+            break;
+        case Opcodes.F2I:
+        case Opcodes.ARRAYLENGTH:
+        case Opcodes.INSTANCEOF:
+            pop(1);
+            push(INTEGER);
+            break;
+        case Opcodes.LCMP:
+        case Opcodes.DCMPL:
+        case Opcodes.DCMPG:
+            pop(4);
+            push(INTEGER);
+            break;
+        case Opcodes.JSR:
+        case Opcodes.RET:
+            throw new RuntimeException(
+                    "JSR/RET are not supported with computeFrames option");
+        case Opcodes.GETSTATIC:
+            push(cw, item.strVal3);
+            break;
+        case Opcodes.PUTSTATIC:
+            pop(item.strVal3);
+            break;
+        case Opcodes.GETFIELD:
+            pop(1);
+            push(cw, item.strVal3);
+            break;
+        case Opcodes.PUTFIELD:
+            pop(item.strVal3);
+            pop();
+            break;
+        case Opcodes.INVOKEVIRTUAL:
+        case Opcodes.INVOKESPECIAL:
+        case Opcodes.INVOKESTATIC:
+        case Opcodes.INVOKEINTERFACE:
+            pop(item.strVal3);
+            if (opcode != Opcodes.INVOKESTATIC) {
+                t1 = pop();
+                if (opcode == Opcodes.INVOKESPECIAL
+                        && item.strVal2.charAt(0) == '<') {
+                    init(t1);
+                }
+            }
+            push(cw, item.strVal3);
+            break;
+        case Opcodes.INVOKEDYNAMIC:
+            pop(item.strVal2);
+            push(cw, item.strVal2);
+            break;
+        case Opcodes.NEW:
+            push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg));
+            break;
+        case Opcodes.NEWARRAY:
+            pop();
+            switch (arg) {
+            case Opcodes.T_BOOLEAN:
+                push(ARRAY_OF | BOOLEAN);
+                break;
+            case Opcodes.T_CHAR:
+                push(ARRAY_OF | CHAR);
+                break;
+            case Opcodes.T_BYTE:
+                push(ARRAY_OF | BYTE);
+                break;
+            case Opcodes.T_SHORT:
+                push(ARRAY_OF | SHORT);
+                break;
+            case Opcodes.T_INT:
+                push(ARRAY_OF | INTEGER);
+                break;
+            case Opcodes.T_FLOAT:
+                push(ARRAY_OF | FLOAT);
+                break;
+            case Opcodes.T_DOUBLE:
+                push(ARRAY_OF | DOUBLE);
+                break;
+            // case Opcodes.T_LONG:
+            default:
+                push(ARRAY_OF | LONG);
+                break;
+            }
+            break;
+        case Opcodes.ANEWARRAY:
+            String s = item.strVal1;
+            pop();
+            if (s.charAt(0) == '[') {
+                push(cw, '[' + s);
+            } else {
+                push(ARRAY_OF | OBJECT | cw.addType(s));
+            }
+            break;
+        case Opcodes.CHECKCAST:
+            s = item.strVal1;
+            pop();
+            if (s.charAt(0) == '[') {
+                push(cw, s);
+            } else {
+                push(OBJECT | cw.addType(s));
+            }
+            break;
+        // case Opcodes.MULTIANEWARRAY:
+        default:
+            pop(arg);
+            push(cw, item.strVal1);
+            break;
+        }
+    }
+
+    /**
+     * Merges the input frame of the given basic block with the input and output
+     * frames of this basic block. Returns <tt>true</tt> if the input frame of
+     * the given label has been changed by this operation.
+     *
+     * @param cw
+     *            the ClassWriter to which this label belongs.
+     * @param frame
+     *            the basic block whose input frame must be updated.
+     * @param edge
+     *            the kind of the {@link Edge} between this label and 'label'.
+     *            See {@link Edge#info}.
+     * @return <tt>true</tt> if the input frame of the given label has been
+     *         changed by this operation.
+     */
+    boolean merge(final ClassWriter cw, final Frame frame, final int edge) {
+        boolean changed = false;
+        int i, s, dim, kind, t;
+
+        int nLocal = inputLocals.length;
+        int nStack = inputStack.length;
+        if (frame.inputLocals == null) {
+            frame.inputLocals = new int[nLocal];
+            changed = true;
+        }
+
+        for (i = 0; i < nLocal; ++i) {
+            if (outputLocals != null && i < outputLocals.length) {
+                s = outputLocals[i];
+                if (s == 0) {
+                    t = inputLocals[i];
+                } else {
+                    dim = s & DIM;
+                    kind = s & KIND;
+                    if (kind == BASE) {
+                        t = s;
+                    } else {
+                        if (kind == LOCAL) {
+                            t = dim + inputLocals[s & VALUE];
+                        } else {
+                            t = dim + inputStack[nStack - (s & VALUE)];
+                        }
+                        if ((s & TOP_IF_LONG_OR_DOUBLE) != 0
+                                && (t == LONG || t == DOUBLE)) {
+                            t = TOP;
+                        }
+                    }
+                }
+            } else {
+                t = inputLocals[i];
+            }
+            if (initializations != null) {
+                t = init(cw, t);
+            }
+            changed |= merge(cw, t, frame.inputLocals, i);
+        }
+
+        if (edge > 0) {
+            for (i = 0; i < nLocal; ++i) {
+                t = inputLocals[i];
+                changed |= merge(cw, t, frame.inputLocals, i);
+            }
+            if (frame.inputStack == null) {
+                frame.inputStack = new int[1];
+                changed = true;
+            }
+            changed |= merge(cw, edge, frame.inputStack, 0);
+            return changed;
+        }
+
+        int nInputStack = inputStack.length + owner.inputStackTop;
+        if (frame.inputStack == null) {
+            frame.inputStack = new int[nInputStack + outputStackTop];
+            changed = true;
+        }
+
+        for (i = 0; i < nInputStack; ++i) {
+            t = inputStack[i];
+            if (initializations != null) {
+                t = init(cw, t);
+            }
+            changed |= merge(cw, t, frame.inputStack, i);
+        }
+        for (i = 0; i < outputStackTop; ++i) {
+            s = outputStack[i];
+            dim = s & DIM;
+            kind = s & KIND;
+            if (kind == BASE) {
+                t = s;
+            } else {
+                if (kind == LOCAL) {
+                    t = dim + inputLocals[s & VALUE];
+                } else {
+                    t = dim + inputStack[nStack - (s & VALUE)];
+                }
+                if ((s & TOP_IF_LONG_OR_DOUBLE) != 0
+                        && (t == LONG || t == DOUBLE)) {
+                    t = TOP;
+                }
+            }
+            if (initializations != null) {
+                t = init(cw, t);
+            }
+            changed |= merge(cw, t, frame.inputStack, nInputStack + i);
+        }
+        return changed;
+    }
+
+    /**
+     * Merges the type at the given index in the given type array with the given
+     * type. Returns <tt>true</tt> if the type array has been modified by this
+     * operation.
+     *
+     * @param cw
+     *            the ClassWriter to which this label belongs.
+     * @param t
+     *            the type with which the type array element must be merged.
+     * @param types
+     *            an array of types.
+     * @param index
+     *            the index of the type that must be merged in 'types'.
+     * @return <tt>true</tt> if the type array has been modified by this
+     *         operation.
+     */
+    private static boolean merge(final ClassWriter cw, int t,
+            final int[] types, final int index) {
+        int u = types[index];
+        if (u == t) {
+            // if the types are equal, merge(u,t)=u, so there is no change
+            return false;
+        }
+        if ((t & ~DIM) == NULL) {
+            if (u == NULL) {
+                return false;
+            }
+            t = NULL;
+        }
+        if (u == 0) {
+            // if types[index] has never been assigned, merge(u,t)=t
+            types[index] = t;
+            return true;
+        }
+        int v;
+        if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) {
+            // if u is a reference type of any dimension
+            if (t == NULL) {
+                // if t is the NULL type, merge(u,t)=u, so there is no change
+                return false;
+            } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) {
+                if ((u & BASE_KIND) == OBJECT) {
+                    // if t is also a reference type, and if u and t have the
+                    // same dimension merge(u,t) = dim(t) | common parent of the
+                    // element types of u and t
+                    v = (t & DIM) | OBJECT
+                            | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE);
+                } else {
+                    // if u and t are array types, but not with the same element
+                    // type, merge(u,t)=java/lang/Object
+                    v = OBJECT | cw.addType("java/lang/Object");
+                }
+            } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) {
+                // if t is any other reference or array type,
+                // merge(u,t)=java/lang/Object
+                v = OBJECT | cw.addType("java/lang/Object");
+            } else {
+                // if t is any other type, merge(u,t)=TOP
+                v = TOP;
+            }
+        } else if (u == NULL) {
+            // if u is the NULL type, merge(u,t)=t,
+            // or TOP if t is not a reference type
+            v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP;
+        } else {
+            // if u is any other type, merge(u,t)=TOP whatever t
+            v = TOP;
+        }
+        if (u != v) {
+            types[index] = v;
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/src/jvm/clojure/asm/Handle.java b/src/jvm/clojure/asm/Handle.java
new file mode 100644
index 0000000..a147cf1
--- /dev/null
+++ b/src/jvm/clojure/asm/Handle.java
@@ -0,0 +1,167 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package clojure.asm;
+
+/**
+ * A reference to a field or a method.
+ *
+ * @author Remi Forax
+ * @author Eric Bruneton
+ */
+public final class Handle {
+
+    /**
+     * The kind of field or method designated by this Handle. Should be
+     * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
+     * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
+     * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
+     * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or
+     * {@link Opcodes#H_INVOKEINTERFACE}.
+     */
+    final int tag;
+
+    /**
+     * The internal name of the field or method designed by this handle.
+     */
+    final String owner;
+
+    /**
+     * The name of the field or method designated by this handle.
+     */
+    final String name;
+
+    /**
+     * The descriptor of the field or method designated by this handle.
+     */
+    final String desc;
+
+    /**
+     * Constructs a new field or method handle.
+     *
+     * @param tag
+     *            the kind of field or method designated by this Handle. Must be
+     *            {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
+     *            {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
+     *            {@link Opcodes#H_INVOKEVIRTUAL},
+     *            {@link Opcodes#H_INVOKESTATIC},
+     *            {@link Opcodes#H_INVOKESPECIAL},
+     *            {@link Opcodes#H_NEWINVOKESPECIAL} or
+     *            {@link Opcodes#H_INVOKEINTERFACE}.
+     * @param owner
+     *            the internal name of the field or method designed by this
+     *            handle.
+     * @param name
+     *            the name of the field or method designated by this handle.
+     * @param desc
+     *            the descriptor of the field or method designated by this
+     *            handle.
+     */
+    public Handle(int tag, String owner, String name, String desc) {
+        this.tag = tag;
+        this.owner = owner;
+        this.name = name;
+        this.desc = desc;
+    }
+
+    /**
+     * Returns the kind of field or method designated by this handle.
+     *
+     * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC},
+     *         {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC},
+     *         {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC},
+     *         {@link Opcodes#H_INVOKESPECIAL},
+     *         {@link Opcodes#H_NEWINVOKESPECIAL} or
+     *         {@link Opcodes#H_INVOKEINTERFACE}.
+     */
+    public int getTag() {
+        return tag;
+    }
+
+    /**
+     * Returns the internal name of the field or method designed by this handle.
+     *
+     * @return the internal name of the field or method designed by this handle.
+     */
+    public String getOwner() {
+        return owner;
+    }
+
+    /**
+     * Returns the name of the field or method designated by this handle.
+     *
+     * @return the name of the field or method designated by this handle.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the descriptor of the field or method designated by this handle.
+     *
+     * @return the descriptor of the field or method designated by this handle.
+     */
+    public String getDesc() {
+        return desc;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (!(obj instanceof Handle)) {
+            return false;
+        }
+        Handle h = (Handle) obj;
+        return tag == h.tag && owner.equals(h.owner) && name.equals(h.name)
+                && desc.equals(h.desc);
+    }
+
+    @Override
+    public int hashCode() {
+        return tag + owner.hashCode() * name.hashCode() * desc.hashCode();
+    }
+
+    /**
+     * Returns the textual representation of this handle. The textual
+     * representation is:
+     *
+     * <pre>
+     * owner '.' name desc ' ' '(' tag ')'
+     * </pre>
+     *
+     * . As this format is unambiguous, it can be parsed if necessary.
+     */
+    @Override
+    public String toString() {
+        return owner + '.' + name + desc + " (" + tag + ')';
+    }
+}
diff --git a/src/jvm/clojure/asm/Handler.java b/src/jvm/clojure/asm/Handler.java
new file mode 100644
index 0000000..bc0579e
--- /dev/null
+++ b/src/jvm/clojure/asm/Handler.java
@@ -0,0 +1,121 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * Information about an exception handler block.
+ *
+ * @author Eric Bruneton
+ */
+class Handler {
+
+    /**
+     * Beginning of the exception handler's scope (inclusive).
+     */
+    Label start;
+
+    /**
+     * End of the exception handler's scope (exclusive).
+     */
+    Label end;
+
+    /**
+     * Beginning of the exception handler's code.
+     */
+    Label handler;
+
+    /**
+     * Internal name of the type of exceptions handled by this handler, or
+     * <tt>null</tt> to catch any exceptions.
+     */
+    String desc;
+
+    /**
+     * Constant pool index of the internal name of the type of exceptions
+     * handled by this handler, or 0 to catch any exceptions.
+     */
+    int type;
+
+    /**
+     * Next exception handler block info.
+     */
+    Handler next;
+
+    /**
+     * Removes the range between start and end from the given exception
+     * handlers.
+     *
+     * @param h
+     *            an exception handler list.
+     * @param start
+     *            the start of the range to be removed.
+     * @param end
+     *            the end of the range to be removed. Maybe null.
+     * @return the exception handler list with the start-end range removed.
+     */
+    static Handler remove(Handler h, Label start, Label end) {
+        if (h == null) {
+            return null;
+        } else {
+            h.next = remove(h.next, start, end);
+        }
+        int hstart = h.start.position;
+        int hend = h.end.position;
+        int s = start.position;
+        int e = end == null ? Integer.MAX_VALUE : end.position;
+        // if [hstart,hend[ and [s,e[ intervals intersect...
+        if (s < hend && e > hstart) {
+            if (s <= hstart) {
+                if (e >= hend) {
+                    // [hstart,hend[ fully included in [s,e[, h removed
+                    h = h.next;
+                } else {
+                    // [hstart,hend[ minus [s,e[ = [e,hend[
+                    h.start = end;
+                }
+            } else if (e >= hend) {
+                // [hstart,hend[ minus [s,e[ = [hstart,s[
+                h.end = start;
+            } else {
+                // [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[
+                Handler g = new Handler();
+                g.start = end;
+                g.end = h.end;
+                g.handler = h.handler;
+                g.desc = h.desc;
+                g.type = h.type;
+                g.next = h.next;
+                h.end = start;
+                h.next = g;
+            }
+        }
+        return h;
+    }
+}
diff --git a/src/jvm/clojure/asm/Item.java b/src/jvm/clojure/asm/Item.java
new file mode 100644
index 0000000..274a548
--- /dev/null
+++ b/src/jvm/clojure/asm/Item.java
@@ -0,0 +1,311 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A constant pool item. Constant pool items can be created with the 'newXXX'
+ * methods in the {@link ClassWriter} class.
+ *
+ * @author Eric Bruneton
+ */
+final class Item {
+
+    /**
+     * Index of this item in the constant pool.
+     */
+    int index;
+
+    /**
+     * Type of this constant pool item. A single class is used to represent all
+     * constant pool item types, in order to minimize the bytecode size of this
+     * package. The value of this field is one of {@link ClassWriter#INT},
+     * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT},
+     * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8},
+     * {@link ClassWriter#STR}, {@link ClassWriter#CLASS},
+     * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD},
+     * {@link ClassWriter#METH}, {@link ClassWriter#IMETH},
+     * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}.
+     *
+     * MethodHandle constant 9 variations are stored using a range of 9 values
+     * from {@link ClassWriter#HANDLE_BASE} + 1 to
+     * {@link ClassWriter#HANDLE_BASE} + 9.
+     *
+     * Special Item types are used for Items that are stored in the ClassWriter
+     * {@link ClassWriter#typeTable}, instead of the constant pool, in order to
+     * avoid clashes with normal constant pool items in the ClassWriter constant
+     * pool's hash table. These special item types are
+     * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and
+     * {@link ClassWriter#TYPE_MERGED}.
+     */
+    int type;
+
+    /**
+     * Value of this item, for an integer item.
+     */
+    int intVal;
+
+    /**
+     * Value of this item, for a long item.
+     */
+    long longVal;
+
+    /**
+     * First part of the value of this item, for items that do not hold a
+     * primitive value.
+     */
+    String strVal1;
+
+    /**
+     * Second part of the value of this item, for items that do not hold a
+     * primitive value.
+     */
+    String strVal2;
+
+    /**
+     * Third part of the value of this item, for items that do not hold a
+     * primitive value.
+     */
+    String strVal3;
+
+    /**
+     * The hash code value of this constant pool item.
+     */
+    int hashCode;
+
+    /**
+     * Link to another constant pool item, used for collision lists in the
+     * constant pool's hash table.
+     */
+    Item next;
+
+    /**
+     * Constructs an uninitialized {@link Item}.
+     */
+    Item() {
+    }
+
+    /**
+     * Constructs an uninitialized {@link Item} for constant pool element at
+     * given position.
+     *
+     * @param index
+     *            index of the item to be constructed.
+     */
+    Item(final int index) {
+        this.index = index;
+    }
+
+    /**
+     * Constructs a copy of the given item.
+     *
+     * @param index
+     *            index of the item to be constructed.
+     * @param i
+     *            the item that must be copied into the item to be constructed.
+     */
+    Item(final int index, final Item i) {
+        this.index = index;
+        type = i.type;
+        intVal = i.intVal;
+        longVal = i.longVal;
+        strVal1 = i.strVal1;
+        strVal2 = i.strVal2;
+        strVal3 = i.strVal3;
+        hashCode = i.hashCode;
+    }
+
+    /**
+     * Sets this item to an integer item.
+     *
+     * @param intVal
+     *            the value of this item.
+     */
+    void set(final int intVal) {
+        this.type = ClassWriter.INT;
+        this.intVal = intVal;
+        this.hashCode = 0x7FFFFFFF & (type + intVal);
+    }
+
+    /**
+     * Sets this item to a long item.
+     *
+     * @param longVal
+     *            the value of this item.
+     */
+    void set(final long longVal) {
+        this.type = ClassWriter.LONG;
+        this.longVal = longVal;
+        this.hashCode = 0x7FFFFFFF & (type + (int) longVal);
+    }
+
+    /**
+     * Sets this item to a float item.
+     *
+     * @param floatVal
+     *            the value of this item.
+     */
+    void set(final float floatVal) {
+        this.type = ClassWriter.FLOAT;
+        this.intVal = Float.floatToRawIntBits(floatVal);
+        this.hashCode = 0x7FFFFFFF & (type + (int) floatVal);
+    }
+
+    /**
+     * Sets this item to a double item.
+     *
+     * @param doubleVal
+     *            the value of this item.
+     */
+    void set(final double doubleVal) {
+        this.type = ClassWriter.DOUBLE;
+        this.longVal = Double.doubleToRawLongBits(doubleVal);
+        this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal);
+    }
+
+    /**
+     * Sets this item to an item that do not hold a primitive value.
+     *
+     * @param type
+     *            the type of this item.
+     * @param strVal1
+     *            first part of the value of this item.
+     * @param strVal2
+     *            second part of the value of this item.
+     * @param strVal3
+     *            third part of the value of this item.
+     */
+    void set(final int type, final String strVal1, final String strVal2,
+            final String strVal3) {
+        this.type = type;
+        this.strVal1 = strVal1;
+        this.strVal2 = strVal2;
+        this.strVal3 = strVal3;
+        switch (type) {
+        case ClassWriter.UTF8:
+        case ClassWriter.STR:
+        case ClassWriter.CLASS:
+        case ClassWriter.MTYPE:
+        case ClassWriter.TYPE_NORMAL:
+            hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
+            return;
+        case ClassWriter.NAME_TYPE: {
+            hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+                    * strVal2.hashCode());
+            return;
+        }
+        // ClassWriter.FIELD:
+        // ClassWriter.METH:
+        // ClassWriter.IMETH:
+        // ClassWriter.HANDLE_BASE + 1..9
+        default:
+            hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+                    * strVal2.hashCode() * strVal3.hashCode());
+        }
+    }
+
+    /**
+     * Sets the item to an InvokeDynamic item.
+     *
+     * @param name
+     *            invokedynamic's name.
+     * @param desc
+     *            invokedynamic's desc.
+     * @param bsmIndex
+     *            zero based index into the class attribute BootrapMethods.
+     */
+    void set(String name, String desc, int bsmIndex) {
+        this.type = ClassWriter.INDY;
+        this.longVal = bsmIndex;
+        this.strVal1 = name;
+        this.strVal2 = desc;
+        this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex
+                * strVal1.hashCode() * strVal2.hashCode());
+    }
+
+    /**
+     * Sets the item to a BootstrapMethod item.
+     *
+     * @param position
+     *            position in byte in the class attribute BootrapMethods.
+     * @param hashCode
+     *            hashcode of the item. This hashcode is processed from the
+     *            hashcode of the bootstrap method and the hashcode of all
+     *            bootstrap arguments.
+     */
+    void set(int position, int hashCode) {
+        this.type = ClassWriter.BSM;
+        this.intVal = position;
+        this.hashCode = hashCode;
+    }
+
+    /**
+     * Indicates if the given item is equal to this one. <i>This method assumes
+     * that the two items have the same {@link #type}</i>.
+     *
+     * @param i
+     *            the item to be compared to this one. Both items must have the
+     *            same {@link #type}.
+     * @return <tt>true</tt> if the given item if equal to this one,
+     *         <tt>false</tt> otherwise.
+     */
+    boolean isEqualTo(final Item i) {
+        switch (type) {
+        case ClassWriter.UTF8:
+        case ClassWriter.STR:
+        case ClassWriter.CLASS:
+        case ClassWriter.MTYPE:
+        case ClassWriter.TYPE_NORMAL:
+            return i.strVal1.equals(strVal1);
+        case ClassWriter.TYPE_MERGED:
+        case ClassWriter.LONG:
+        case ClassWriter.DOUBLE:
+            return i.longVal == longVal;
+        case ClassWriter.INT:
+        case ClassWriter.FLOAT:
+            return i.intVal == intVal;
+        case ClassWriter.TYPE_UNINIT:
+            return i.intVal == intVal && i.strVal1.equals(strVal1);
+        case ClassWriter.NAME_TYPE:
+            return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2);
+        case ClassWriter.INDY: {
+            return i.longVal == longVal && i.strVal1.equals(strVal1)
+                    && i.strVal2.equals(strVal2);
+        }
+        // case ClassWriter.FIELD:
+        // case ClassWriter.METH:
+        // case ClassWriter.IMETH:
+        // case ClassWriter.HANDLE_BASE + 1..9
+        default:
+            return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2)
+                    && i.strVal3.equals(strVal3);
+        }
+    }
+
+}
diff --git a/src/jvm/clojure/asm/Label.java b/src/jvm/clojure/asm/Label.java
new file mode 100644
index 0000000..735e1b9
--- /dev/null
+++ b/src/jvm/clojure/asm/Label.java
@@ -0,0 +1,560 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A label represents a position in the bytecode of a method. Labels are used
+ * for jump, goto, and switch instructions, and for try catch blocks. A label
+ * designates the <i>instruction</i> that is just after. Note however that there
+ * can be other elements between a label and the instruction it designates (such
+ * as other labels, stack map frames, line numbers, etc.).
+ *
+ * @author Eric Bruneton
+ */
+public class Label {
+
+    /**
+     * Indicates if this label is only used for debug attributes. Such a label
+     * is not the start of a basic block, the target of a jump instruction, or
+     * an exception handler. It can be safely ignored in control flow graph
+     * analysis algorithms (for optimization purposes).
+     */
+    static final int DEBUG = 1;
+
+    /**
+     * Indicates if the position of this label is known.
+     */
+    static final int RESOLVED = 2;
+
+    /**
+     * Indicates if this label has been updated, after instruction resizing.
+     */
+    static final int RESIZED = 4;
+
+    /**
+     * Indicates if this basic block has been pushed in the basic block stack.
+     * See {@link MethodWriter#visitMaxs visitMaxs}.
+     */
+    static final int PUSHED = 8;
+
+    /**
+     * Indicates if this label is the target of a jump instruction, or the start
+     * of an exception handler.
+     */
+    static final int TARGET = 16;
+
+    /**
+     * Indicates if a stack map frame must be stored for this label.
+     */
+    static final int STORE = 32;
+
+    /**
+     * Indicates if this label corresponds to a reachable basic block.
+     */
+    static final int REACHABLE = 64;
+
+    /**
+     * Indicates if this basic block ends with a JSR instruction.
+     */
+    static final int JSR = 128;
+
+    /**
+     * Indicates if this basic block ends with a RET instruction.
+     */
+    static final int RET = 256;
+
+    /**
+     * Indicates if this basic block is the start of a subroutine.
+     */
+    static final int SUBROUTINE = 512;
+
+    /**
+     * Indicates if this subroutine basic block has been visited by a
+     * visitSubroutine(null, ...) call.
+     */
+    static final int VISITED = 1024;
+
+    /**
+     * Indicates if this subroutine basic block has been visited by a
+     * visitSubroutine(!null, ...) call.
+     */
+    static final int VISITED2 = 2048;
+
+    /**
+     * Field used to associate user information to a label. Warning: this field
+     * is used by the ASM tree package. In order to use it with the ASM tree
+     * package you must override the
+     * {@link clojure.asm.tree.MethodNode#getLabelNode} method.
+     */
+    public Object info;
+
+    /**
+     * Flags that indicate the status of this label.
+     *
+     * @see #DEBUG
+     * @see #RESOLVED
+     * @see #RESIZED
+     * @see #PUSHED
+     * @see #TARGET
+     * @see #STORE
+     * @see #REACHABLE
+     * @see #JSR
+     * @see #RET
+     */
+    int status;
+
+    /**
+     * The line number corresponding to this label, if known.
+     */
+    int line;
+
+    /**
+     * The position of this label in the code, if known.
+     */
+    int position;
+
+    /**
+     * Number of forward references to this label, times two.
+     */
+    private int referenceCount;
+
+    /**
+     * Informations about forward references. Each forward reference is
+     * described by two consecutive integers in this array: the first one is the
+     * position of the first byte of the bytecode instruction that contains the
+     * forward reference, while the second is the position of the first byte of
+     * the forward reference itself. In fact the sign of the first integer
+     * indicates if this reference uses 2 or 4 bytes, and its absolute value
+     * gives the position of the bytecode instruction. This array is also used
+     * as a bitset to store the subroutines to which a basic block belongs. This
+     * information is needed in {@linked MethodWriter#visitMaxs}, after all
+     * forward references have been resolved. Hence the same array can be used
+     * for both purposes without problems.
+     */
+    private int[] srcAndRefPositions;
+
+    // ------------------------------------------------------------------------
+
+    /*
+     * Fields for the control flow and data flow graph analysis algorithms (used
+     * to compute the maximum stack size or the stack map frames). A control
+     * flow graph contains one node per "basic block", and one edge per "jump"
+     * from one basic block to another. Each node (i.e., each basic block) is
+     * represented by the Label object that corresponds to the first instruction
+     * of this basic block. Each node also stores the list of its successors in
+     * the graph, as a linked list of Edge objects.
+     *
+     * The control flow analysis algorithms used to compute the maximum stack
+     * size or the stack map frames are similar and use two steps. The first
+     * step, during the visit of each instruction, builds information about the
+     * state of the local variables and the operand stack at the end of each
+     * basic block, called the "output frame", <i>relatively</i> to the frame
+     * state at the beginning of the basic block, which is called the "input
+     * frame", and which is <i>unknown</i> during this step. The second step, in
+     * {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes
+     * information about the input frame of each basic block, from the input
+     * state of the first basic block (known from the method signature), and by
+     * the using the previously computed relative output frames.
+     *
+     * The algorithm used to compute the maximum stack size only computes the
+     * relative output and absolute input stack heights, while the algorithm
+     * used to compute stack map frames computes relative output frames and
+     * absolute input frames.
+     */
+
+    /**
+     * Start of the output stack relatively to the input stack. The exact
+     * semantics of this field depends on the algorithm that is used.
+     *
+     * When only the maximum stack size is computed, this field is the number of
+     * elements in the input stack.
+     *
+     * When the stack map frames are completely computed, this field is the
+     * offset of the first output stack element relatively to the top of the
+     * input stack. This offset is always negative or null. A null offset means
+     * that the output stack must be appended to the input stack. A -n offset
+     * means that the first n output stack elements must replace the top n input
+     * stack elements, and that the other elements must be appended to the input
+     * stack.
+     */
+    int inputStackTop;
+
+    /**
+     * Maximum height reached by the output stack, relatively to the top of the
+     * input stack. This maximum is always positive or null.
+     */
+    int outputStackMax;
+
+    /**
+     * Information about the input and output stack map frames of this basic
+     * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES}
+     * option is used.
+     */
+    Frame frame;
+
+    /**
+     * The successor of this label, in the order they are visited. This linked
+     * list does not include labels used for debug info only. If
+     * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it
+     * does not contain successive labels that denote the same bytecode position
+     * (in this case only the first label appears in this list).
+     */
+    Label successor;
+
+    /**
+     * The successors of this node in the control flow graph. These successors
+     * are stored in a linked list of {@link Edge Edge} objects, linked to each
+     * other by their {@link Edge#next} field.
+     */
+    Edge successors;
+
+    /**
+     * The next basic block in the basic block stack. This stack is used in the
+     * main loop of the fix point algorithm used in the second step of the
+     * control flow analysis algorithms. It is also used in
+     * {@link #visitSubroutine} to avoid using a recursive method.
+     *
+     * @see MethodWriter#visitMaxs
+     */
+    Label next;
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a new label.
+     */
+    public Label() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Methods to compute offsets and to manage forward references
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the offset corresponding to this label. This offset is computed
+     * from the start of the method's bytecode. <i>This method is intended for
+     * {@link Attribute} sub classes, and is normally not needed by class
+     * generators or adapters.</i>
+     *
+     * @return the offset corresponding to this label.
+     * @throws IllegalStateException
+     *             if this label is not resolved yet.
+     */
+    public int getOffset() {
+        if ((status & RESOLVED) == 0) {
+            throw new IllegalStateException(
+                    "Label offset position has not been resolved yet");
+        }
+        return position;
+    }
+
+    /**
+     * Puts a reference to this label in the bytecode of a method. If the
+     * position of the label is known, the offset is computed and written
+     * directly. Otherwise, a null offset is written and a new forward reference
+     * is declared for this label.
+     *
+     * @param owner
+     *            the code writer that calls this method.
+     * @param out
+     *            the bytecode of the method.
+     * @param source
+     *            the position of first byte of the bytecode instruction that
+     *            contains this label.
+     * @param wideOffset
+     *            <tt>true</tt> if the reference must be stored in 4 bytes, or
+     *            <tt>false</tt> if it must be stored with 2 bytes.
+     * @throws IllegalArgumentException
+     *             if this label has not been created by the given code writer.
+     */
+    void put(final MethodWriter owner, final ByteVector out, final int source,
+            final boolean wideOffset) {
+        if ((status & RESOLVED) == 0) {
+            if (wideOffset) {
+                addReference(-1 - source, out.length);
+                out.putInt(-1);
+            } else {
+                addReference(source, out.length);
+                out.putShort(-1);
+            }
+        } else {
+            if (wideOffset) {
+                out.putInt(position - source);
+            } else {
+                out.putShort(position - source);
+            }
+        }
+    }
+
+    /**
+     * Adds a forward reference to this label. This method must be called only
+     * for a true forward reference, i.e. only if this label is not resolved
+     * yet. For backward references, the offset of the reference can be, and
+     * must be, computed and stored directly.
+     *
+     * @param sourcePosition
+     *            the position of the referencing instruction. This position
+     *            will be used to compute the offset of this forward reference.
+     * @param referencePosition
+     *            the position where the offset for this forward reference must
+     *            be stored.
+     */
+    private void addReference(final int sourcePosition,
+            final int referencePosition) {
+        if (srcAndRefPositions == null) {
+            srcAndRefPositions = new int[6];
+        }
+        if (referenceCount >= srcAndRefPositions.length) {
+            int[] a = new int[srcAndRefPositions.length + 6];
+            System.arraycopy(srcAndRefPositions, 0, a, 0,
+                    srcAndRefPositions.length);
+            srcAndRefPositions = a;
+        }
+        srcAndRefPositions[referenceCount++] = sourcePosition;
+        srcAndRefPositions[referenceCount++] = referencePosition;
+    }
+
+    /**
+     * Resolves all forward references to this label. This method must be called
+     * when this label is added to the bytecode of the method, i.e. when its
+     * position becomes known. This method fills in the blanks that where left
+     * in the bytecode by each forward reference previously added to this label.
+     *
+     * @param owner
+     *            the code writer that calls this method.
+     * @param position
+     *            the position of this label in the bytecode.
+     * @param data
+     *            the bytecode of the method.
+     * @return <tt>true</tt> if a blank that was left for this label was to
+     *         small to store the offset. In such a case the corresponding jump
+     *         instruction is replaced with a pseudo instruction (using unused
+     *         opcodes) using an unsigned two bytes offset. These pseudo
+     *         instructions will need to be replaced with true instructions with
+     *         wider offsets (4 bytes instead of 2). This is done in
+     *         {@link MethodWriter#resizeInstructions}.
+     * @throws IllegalArgumentException
+     *             if this label has already been resolved, or if it has not
+     *             been created by the given code writer.
+     */
+    boolean resolve(final MethodWriter owner, final int position,
+            final byte[] data) {
+        boolean needUpdate = false;
+        this.status |= RESOLVED;
+        this.position = position;
+        int i = 0;
+        while (i < referenceCount) {
+            int source = srcAndRefPositions[i++];
+            int reference = srcAndRefPositions[i++];
+            int offset;
+            if (source >= 0) {
+                offset = position - source;
+                if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) {
+                    /*
+                     * changes the opcode of the jump instruction, in order to
+                     * be able to find it later (see resizeInstructions in
+                     * MethodWriter). These temporary opcodes are similar to
+                     * jump instruction opcodes, except that the 2 bytes offset
+                     * is unsigned (and can therefore represent values from 0 to
+                     * 65535, which is sufficient since the size of a method is
+                     * limited to 65535 bytes).
+                     */
+                    int opcode = data[reference - 1] & 0xFF;
+                    if (opcode <= Opcodes.JSR) {
+                        // changes IFEQ ... JSR to opcodes 202 to 217
+                        data[reference - 1] = (byte) (opcode + 49);
+                    } else {
+                        // changes IFNULL and IFNONNULL to opcodes 218 and 219
+                        data[reference - 1] = (byte) (opcode + 20);
+                    }
+                    needUpdate = true;
+                }
+                data[reference++] = (byte) (offset >>> 8);
+                data[reference] = (byte) offset;
+            } else {
+                offset = position + source + 1;
+                data[reference++] = (byte) (offset >>> 24);
+                data[reference++] = (byte) (offset >>> 16);
+                data[reference++] = (byte) (offset >>> 8);
+                data[reference] = (byte) offset;
+            }
+        }
+        return needUpdate;
+    }
+
+    /**
+     * Returns the first label of the series to which this label belongs. For an
+     * isolated label or for the first label in a series of successive labels,
+     * this method returns the label itself. For other labels it returns the
+     * first label of the series.
+     *
+     * @return the first label of the series to which this label belongs.
+     */
+    Label getFirst() {
+        return !ClassReader.FRAMES || frame == null ? this : frame.owner;
+    }
+
+    // ------------------------------------------------------------------------
+    // Methods related to subroutines
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns true is this basic block belongs to the given subroutine.
+     *
+     * @param id
+     *            a subroutine id.
+     * @return true is this basic block belongs to the given subroutine.
+     */
+    boolean inSubroutine(final long id) {
+        if ((status & Label.VISITED) != 0) {
+            return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this basic block and the given one belong to a common
+     * subroutine.
+     *
+     * @param block
+     *            another basic block.
+     * @return true if this basic block and the given one belong to a common
+     *         subroutine.
+     */
+    boolean inSameSubroutine(final Label block) {
+        if ((status & VISITED) == 0 || (block.status & VISITED) == 0) {
+            return false;
+        }
+        for (int i = 0; i < srcAndRefPositions.length; ++i) {
+            if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Marks this basic block as belonging to the given subroutine.
+     *
+     * @param id
+     *            a subroutine id.
+     * @param nbSubroutines
+     *            the total number of subroutines in the method.
+     */
+    void addToSubroutine(final long id, final int nbSubroutines) {
+        if ((status & VISITED) == 0) {
+            status |= VISITED;
+            srcAndRefPositions = new int[(nbSubroutines - 1) / 32 + 1];
+        }
+        srcAndRefPositions[(int) (id >>> 32)] |= (int) id;
+    }
+
+    /**
+     * Finds the basic blocks that belong to a given subroutine, and marks these
+     * blocks as belonging to this subroutine. This method follows the control
+     * flow graph to find all the blocks that are reachable from the current
+     * block WITHOUT following any JSR target.
+     *
+     * @param JSR
+     *            a JSR block that jumps to this subroutine. If this JSR is not
+     *            null it is added to the successor of the RET blocks found in
+     *            the subroutine.
+     * @param id
+     *            the id of this subroutine.
+     * @param nbSubroutines
+     *            the total number of subroutines in the method.
+     */
+    void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) {
+        // user managed stack of labels, to avoid using a recursive method
+        // (recursivity can lead to stack overflow with very large methods)
+        Label stack = this;
+        while (stack != null) {
+            // removes a label l from the stack
+            Label l = stack;
+            stack = l.next;
+            l.next = null;
+
+            if (JSR != null) {
+                if ((l.status & VISITED2) != 0) {
+                    continue;
+                }
+                l.status |= VISITED2;
+                // adds JSR to the successors of l, if it is a RET block
+                if ((l.status & RET) != 0) {
+                    if (!l.inSameSubroutine(JSR)) {
+                        Edge e = new Edge();
+                        e.info = l.inputStackTop;
+                        e.successor = JSR.successors.successor;
+                        e.next = l.successors;
+                        l.successors = e;
+                    }
+                }
+            } else {
+                // if the l block already belongs to subroutine 'id', continue
+                if (l.inSubroutine(id)) {
+                    continue;
+                }
+                // marks the l block as belonging to subroutine 'id'
+                l.addToSubroutine(id, nbSubroutines);
+            }
+            // pushes each successor of l on the stack, except JSR targets
+            Edge e = l.successors;
+            while (e != null) {
+                // if the l block is a JSR block, then 'l.successors.next' leads
+                // to the JSR target (see {@link #visitJumpInsn}) and must
+                // therefore not be followed
+                if ((l.status & Label.JSR) == 0 || e != l.successors.next) {
+                    // pushes e.successor on the stack if it not already added
+                    if (e.successor.next == null) {
+                        e.successor.next = stack;
+                        stack = e.successor;
+                    }
+                }
+                e = e.next;
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Overriden Object methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns a string representation of this label.
+     *
+     * @return a string representation of this label.
+     */
+    @Override
+    public String toString() {
+        return "L" + System.identityHashCode(this);
+    }
+}
diff --git a/src/jvm/clojure/asm/MethodVisitor.java b/src/jvm/clojure/asm/MethodVisitor.java
new file mode 100644
index 0000000..347455e
--- /dev/null
+++ b/src/jvm/clojure/asm/MethodVisitor.java
@@ -0,0 +1,662 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A visitor to visit a Java method. The methods of this class must be called in
+ * the following order: [ <tt>visitAnnotationDefault</tt> ] (
+ * <tt>visitAnnotation</tt> | <tt>visitParameterAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> |
+ * <tt>visit</tt><i>X</i>Insn</tt> | <tt>visitLabel</tt> |
+ * <tt>visitTryCatchBlock</tt> | <tt>visitLocalVariable</tt> |
+ * <tt>visitLineNumber</tt> )* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In
+ * addition, the <tt>visit</tt><i>X</i>Insn</tt> and <tt>visitLabel</tt> methods
+ * must be called in the sequential order of the bytecode instructions of the
+ * visited code, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the
+ * labels passed as arguments have been visited, and the
+ * <tt>visitLocalVariable</tt> and <tt>visitLineNumber</tt> methods must be
+ * called <i>after</i> the labels passed as arguments have been visited.
+ *
+ * @author Eric Bruneton
+ */
+public abstract class MethodVisitor {
+
+    /**
+     * The ASM API version implemented by this visitor. The value of this field
+     * must be one of {@link Opcodes#ASM4}.
+     */
+    protected final int api;
+
+    /**
+     * The method visitor to which this visitor must delegate method calls. May
+     * be null.
+     */
+    protected MethodVisitor mv;
+
+    /**
+     * Constructs a new {@link MethodVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     */
+    public MethodVisitor(final int api) {
+        this(api, null);
+    }
+
+    /**
+     * Constructs a new {@link MethodVisitor}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param mv
+     *            the method visitor to which this visitor must delegate method
+     *            calls. May be null.
+     */
+    public MethodVisitor(final int api, final MethodVisitor mv) {
+        if (api != Opcodes.ASM4) {
+            throw new IllegalArgumentException();
+        }
+        this.api = api;
+        this.mv = mv;
+    }
+
+    // -------------------------------------------------------------------------
+    // Annotations and non standard attributes
+    // -------------------------------------------------------------------------
+
+    /**
+     * Visits the default value of this annotation interface method.
+     *
+     * @return a visitor to the visit the actual default value of this
+     *         annotation interface method, or <tt>null</tt> if this visitor is
+     *         not interested in visiting this default value. The 'name'
+     *         parameters passed to the methods of this annotation visitor are
+     *         ignored. Moreover, exacly one visit method must be called on this
+     *         annotation visitor, followed by visitEnd.
+     */
+    public AnnotationVisitor visitAnnotationDefault() {
+        if (mv != null) {
+            return mv.visitAnnotationDefault();
+        }
+        return null;
+    }
+
+    /**
+     * Visits an annotation of this method.
+     *
+     * @param desc
+     *            the class descriptor of the annotation class.
+     * @param visible
+     *            <tt>true</tt> if the annotation is visible at runtime.
+     * @return a visitor to visit the annotation values, or <tt>null</tt> if
+     *         this visitor is not interested in visiting this annotation.
+     */
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        if (mv != null) {
+            return mv.visitAnnotation(desc, visible);
+        }
+        return null;
+    }
+
+    /**
+     * Visits an annotation of a parameter this method.
+     *
+     * @param parameter
+     *            the parameter index.
+     * @param desc
+     *            the class descriptor of the annotation class.
+     * @param visible
+     *            <tt>true</tt> if the annotation is visible at runtime.
+     * @return a visitor to visit the annotation values, or <tt>null</tt> if
+     *         this visitor is not interested in visiting this annotation.
+     */
+    public AnnotationVisitor visitParameterAnnotation(int parameter,
+            String desc, boolean visible) {
+        if (mv != null) {
+            return mv.visitParameterAnnotation(parameter, desc, visible);
+        }
+        return null;
+    }
+
+    /**
+     * Visits a non standard attribute of this method.
+     *
+     * @param attr
+     *            an attribute.
+     */
+    public void visitAttribute(Attribute attr) {
+        if (mv != null) {
+            mv.visitAttribute(attr);
+        }
+    }
+
+    /**
+     * Starts the visit of the method's code, if any (i.e. non abstract method).
+     */
+    public void visitCode() {
+        if (mv != null) {
+            mv.visitCode();
+        }
+    }
+
+    /**
+     * Visits the current state of the local variables and operand stack
+     * elements. This method must(*) be called <i>just before</i> any
+     * instruction <b>i</b> that follows an unconditional branch instruction
+     * such as GOTO or THROW, that is the target of a jump instruction, or that
+     * starts an exception handler block. The visited types must describe the
+     * values of the local variables and of the operand stack elements <i>just
+     * before</i> <b>i</b> is executed.<br>
+     * <br>
+     * (*) this is mandatory only for classes whose version is greater than or
+     * equal to {@link Opcodes#V1_6 V1_6}. <br>
+     * <br>
+     * The frames of a method must be given either in expanded form, or in
+     * compressed form (all frames must use the same format, i.e. you must not
+     * mix expanded and compressed frames within a single method):
+     * <ul>
+     * <li>In expanded form, all frames must have the F_NEW type.</li>
+     * <li>In compressed form, frames are basically "deltas" from the state of
+     * the previous frame:
+     * <ul>
+     * <li>{@link Opcodes#F_SAME} representing frame with exactly the same
+     * locals as the previous frame and with the empty stack.</li>
+     * <li>{@link Opcodes#F_SAME1} representing frame with exactly the same
+     * locals as the previous frame and with single value on the stack (
+     * <code>nStack</code> is 1 and <code>stack[0]</code> contains value for the
+     * type of the stack item).</li>
+     * <li>{@link Opcodes#F_APPEND} representing frame with current locals are
+     * the same as the locals in the previous frame, except that additional
+     * locals are defined (<code>nLocal</code> is 1, 2 or 3 and
+     * <code>local</code> elements contains values representing added types).</li>
+     * <li>{@link Opcodes#F_CHOP} representing frame with current locals are the
+     * same as the locals in the previous frame, except that the last 1-3 locals
+     * are absent and with the empty stack (<code>nLocals</code> is 1, 2 or 3).</li>
+     * <li>{@link Opcodes#F_FULL} representing complete frame data.</li></li>
+     * </ul>
+     * </ul> <br>
+     * In both cases the first frame, corresponding to the method's parameters
+     * and access flags, is implicit and must not be visited. Also, it is
+     * illegal to visit two or more frames for the same code location (i.e., at
+     * least one instruction must be visited between two calls to visitFrame).
+     *
+     * @param type
+     *            the type of this stack map frame. Must be
+     *            {@link Opcodes#F_NEW} for expanded frames, or
+     *            {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND},
+     *            {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or
+     *            {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for
+     *            compressed frames.
+     * @param nLocal
+     *            the number of local variables in the visited frame.
+     * @param local
+     *            the local variable types in this frame. This array must not be
+     *            modified. Primitive types are represented by
+     *            {@link Opcodes#TOP}, {@link Opcodes#INTEGER},
+     *            {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+     *            {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+     *            {@link Opcodes#UNINITIALIZED_THIS} (long and double are
+     *            represented by a single element). Reference types are
+     *            represented by String objects (representing internal names),
+     *            and uninitialized types by Label objects (this label
+     *            designates the NEW instruction that created this uninitialized
+     *            value).
+     * @param nStack
+     *            the number of operand stack elements in the visited frame.
+     * @param stack
+     *            the operand stack types in this frame. This array must not be
+     *            modified. Its content has the same format as the "local"
+     *            array.
+     * @throws IllegalStateException
+     *             if a frame is visited just after another one, without any
+     *             instruction between the two (unless this frame is a
+     *             Opcodes#F_SAME frame, in which case it is silently ignored).
+     */
+    public void visitFrame(int type, int nLocal, Object[] local, int nStack,
+            Object[] stack) {
+        if (mv != null) {
+            mv.visitFrame(type, nLocal, local, nStack, stack);
+        }
+    }
+
+    // -------------------------------------------------------------------------
+    // Normal instructions
+    // -------------------------------------------------------------------------
+
+    /**
+     * Visits a zero operand instruction.
+     *
+     * @param opcode
+     *            the opcode of the instruction to be visited. This opcode is
+     *            either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1,
+     *            ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1,
+     *            FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD,
+     *            LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD,
+     *            IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE,
+     *            SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1,
+     *            DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB,
+     *            IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM,
+     *            FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR,
+     *            IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D,
+     *            L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S,
+     *            LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN,
+     *            DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER,
+     *            or MONITOREXIT.
+     */
+    public void visitInsn(int opcode) {
+        if (mv != null) {
+            mv.visitInsn(opcode);
+        }
+    }
+
+    /**
+     * Visits an instruction with a single int operand.
+     *
+     * @param opcode
+     *            the opcode of the instruction to be visited. This opcode is
+     *            either BIPUSH, SIPUSH or NEWARRAY.
+     * @param operand
+     *            the operand of the instruction to be visited.<br>
+     *            When opcode is BIPUSH, operand value should be between
+     *            Byte.MIN_VALUE and Byte.MAX_VALUE.<br>
+     *            When opcode is SIPUSH, operand value should be between
+     *            Short.MIN_VALUE and Short.MAX_VALUE.<br>
+     *            When opcode is NEWARRAY, operand value should be one of
+     *            {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR},
+     *            {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE},
+     *            {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT},
+     *            {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}.
+     */
+    public void visitIntInsn(int opcode, int operand) {
+        if (mv != null) {
+            mv.visitIntInsn(opcode, operand);
+        }
+    }
+
+    /**
+     * Visits a local variable instruction. A local variable instruction is an
+     * instruction that loads or stores the value of a local variable.
+     *
+     * @param opcode
+     *            the opcode of the local variable instruction to be visited.
+     *            This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD,
+     *            ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
+     * @param var
+     *            the operand of the instruction to be visited. This operand is
+     *            the index of a local variable.
+     */
+    public void visitVarInsn(int opcode, int var) {
+        if (mv != null) {
+            mv.visitVarInsn(opcode, var);
+        }
+    }
+
+    /**
+     * Visits a type instruction. A type instruction is an instruction that
+     * takes the internal name of a class as parameter.
+     *
+     * @param opcode
+     *            the opcode of the type instruction to be visited. This opcode
+     *            is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
+     * @param type
+     *            the operand of the instruction to be visited. This operand
+     *            must be the internal name of an object or array class (see
+     *            {@link Type#getInternalName() getInternalName}).
+     */
+    public void visitTypeInsn(int opcode, String type) {
+        if (mv != null) {
+            mv.visitTypeInsn(opcode, type);
+        }
+    }
+
+    /**
+     * Visits a field instruction. A field instruction is an instruction that
+     * loads or stores the value of a field of an object.
+     *
+     * @param opcode
+     *            the opcode of the type instruction to be visited. This opcode
+     *            is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
+     * @param owner
+     *            the internal name of the field's owner class (see
+     *            {@link Type#getInternalName() getInternalName}).
+     * @param name
+     *            the field's name.
+     * @param desc
+     *            the field's descriptor (see {@link Type Type}).
+     */
+    public void visitFieldInsn(int opcode, String owner, String name,
+            String desc) {
+        if (mv != null) {
+            mv.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    /**
+     * Visits a method instruction. A method instruction is an instruction that
+     * invokes a method.
+     *
+     * @param opcode
+     *            the opcode of the type instruction to be visited. This opcode
+     *            is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
+     *            INVOKEINTERFACE.
+     * @param owner
+     *            the internal name of the method's owner class (see
+     *            {@link Type#getInternalName() getInternalName}).
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     */
+    public void visitMethodInsn(int opcode, String owner, String name,
+            String desc) {
+        if (mv != null) {
+            mv.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    /**
+     * Visits an invokedynamic instruction.
+     *
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     * @param bsm
+     *            the bootstrap method.
+     * @param bsmArgs
+     *            the bootstrap method constant arguments. Each argument must be
+     *            an {@link Integer}, {@link Float}, {@link Long},
+     *            {@link Double}, {@link String}, {@link Type} or {@link Handle}
+     *            value. This method is allowed to modify the content of the
+     *            array so a caller should expect that this array may change.
+     */
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
+        if (mv != null) {
+            mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+        }
+    }
+
+    /**
+     * Visits a jump instruction. A jump instruction is an instruction that may
+     * jump to another instruction.
+     *
+     * @param opcode
+     *            the opcode of the type instruction to be visited. This opcode
+     *            is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ,
+     *            IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE,
+     *            IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL.
+     * @param label
+     *            the operand of the instruction to be visited. This operand is
+     *            a label that designates the instruction to which the jump
+     *            instruction may jump.
+     */
+    public void visitJumpInsn(int opcode, Label label) {
+        if (mv != null) {
+            mv.visitJumpInsn(opcode, label);
+        }
+    }
+
+    /**
+     * Visits a label. A label designates the instruction that will be visited
+     * just after it.
+     *
+     * @param label
+     *            a {@link Label Label} object.
+     */
+    public void visitLabel(Label label) {
+        if (mv != null) {
+            mv.visitLabel(label);
+        }
+    }
+
+    // -------------------------------------------------------------------------
+    // Special instructions
+    // -------------------------------------------------------------------------
+
+    /**
+     * Visits a LDC instruction. Note that new constant types may be added in
+     * future versions of the Java Virtual Machine. To easily detect new
+     * constant types, implementations of this method should check for
+     * unexpected constant types, like this:
+     *
+     * <pre>
+     * if (cst instanceof Integer) {
+     *     // ...
+     * } else if (cst instanceof Float) {
+     *     // ...
+     * } else if (cst instanceof Long) {
+     *     // ...
+     * } else if (cst instanceof Double) {
+     *     // ...
+     * } else if (cst instanceof String) {
+     *     // ...
+     * } else if (cst instanceof Type) {
+     *     int sort = ((Type) cst).getSort();
+     *     if (sort == Type.OBJECT) {
+     *         // ...
+     *     } else if (sort == Type.ARRAY) {
+     *         // ...
+     *     } else if (sort == Type.METHOD) {
+     *         // ...
+     *     } else {
+     *         // throw an exception
+     *     }
+     * } else if (cst instanceof Handle) {
+     *     // ...
+     * } else {
+     *     // throw an exception
+     * }
+     * </pre>
+     *
+     * @param cst
+     *            the constant to be loaded on the stack. This parameter must be
+     *            a non null {@link Integer}, a {@link Float}, a {@link Long}, a
+     *            {@link Double}, a {@link String}, a {@link Type} of OBJECT or
+     *            ARRAY sort for <tt>.class</tt> constants, for classes whose
+     *            version is 49.0, a {@link Type} of METHOD sort or a
+     *            {@link Handle} for MethodType and MethodHandle constants, for
+     *            classes whose version is 51.0.
+     */
+    public void visitLdcInsn(Object cst) {
+        if (mv != null) {
+            mv.visitLdcInsn(cst);
+        }
+    }
+
+    /**
+     * Visits an IINC instruction.
+     *
+     * @param var
+     *            index of the local variable to be incremented.
+     * @param increment
+     *            amount to increment the local variable by.
+     */
+    public void visitIincInsn(int var, int increment) {
+        if (mv != null) {
+            mv.visitIincInsn(var, increment);
+        }
+    }
+
+    /**
+     * Visits a TABLESWITCH instruction.
+     *
+     * @param min
+     *            the minimum key value.
+     * @param max
+     *            the maximum key value.
+     * @param dflt
+     *            beginning of the default handler block.
+     * @param labels
+     *            beginnings of the handler blocks. <tt>labels[i]</tt> is the
+     *            beginning of the handler block for the <tt>min + i</tt> key.
+     */
+    public void visitTableSwitchInsn(int min, int max, Label dflt,
+            Label... labels) {
+        if (mv != null) {
+            mv.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    /**
+     * Visits a LOOKUPSWITCH instruction.
+     *
+     * @param dflt
+     *            beginning of the default handler block.
+     * @param keys
+     *            the values of the keys.
+     * @param labels
+     *            beginnings of the handler blocks. <tt>labels[i]</tt> is the
+     *            beginning of the handler block for the <tt>keys[i]</tt> key.
+     */
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        if (mv != null) {
+            mv.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    /**
+     * Visits a MULTIANEWARRAY instruction.
+     *
+     * @param desc
+     *            an array type descriptor (see {@link Type Type}).
+     * @param dims
+     *            number of dimensions of the array to allocate.
+     */
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        if (mv != null) {
+            mv.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+
+    // -------------------------------------------------------------------------
+    // Exceptions table entries, debug information, max stack and max locals
+    // -------------------------------------------------------------------------
+
+    /**
+     * Visits a try catch block.
+     *
+     * @param start
+     *            beginning of the exception handler's scope (inclusive).
+     * @param end
+     *            end of the exception handler's scope (exclusive).
+     * @param handler
+     *            beginning of the exception handler's code.
+     * @param type
+     *            internal name of the type of exceptions handled by the
+     *            handler, or <tt>null</tt> to catch any exceptions (for
+     *            "finally" blocks).
+     * @throws IllegalArgumentException
+     *             if one of the labels has already been visited by this visitor
+     *             (by the {@link #visitLabel visitLabel} method).
+     */
+    public void visitTryCatchBlock(Label start, Label end, Label handler,
+            String type) {
+        if (mv != null) {
+            mv.visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    /**
+     * Visits a local variable declaration.
+     *
+     * @param name
+     *            the name of a local variable.
+     * @param desc
+     *            the type descriptor of this local variable.
+     * @param signature
+     *            the type signature of this local variable. May be
+     *            <tt>null</tt> if the local variable type does not use generic
+     *            types.
+     * @param start
+     *            the first instruction corresponding to the scope of this local
+     *            variable (inclusive).
+     * @param end
+     *            the last instruction corresponding to the scope of this local
+     *            variable (exclusive).
+     * @param index
+     *            the local variable's index.
+     * @throws IllegalArgumentException
+     *             if one of the labels has not already been visited by this
+     *             visitor (by the {@link #visitLabel visitLabel} method).
+     */
+    public void visitLocalVariable(String name, String desc, String signature,
+            Label start, Label end, int index) {
+        if (mv != null) {
+            mv.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+    }
+
+    /**
+     * Visits a line number declaration.
+     *
+     * @param line
+     *            a line number. This number refers to the source file from
+     *            which the class was compiled.
+     * @param start
+     *            the first instruction corresponding to this line number.
+     * @throws IllegalArgumentException
+     *             if <tt>start</tt> has not already been visited by this
+     *             visitor (by the {@link #visitLabel visitLabel} method).
+     */
+    public void visitLineNumber(int line, Label start) {
+        if (mv != null) {
+            mv.visitLineNumber(line, start);
+        }
+    }
+
+    /**
+     * Visits the maximum stack size and the maximum number of local variables
+     * of the method.
+     *
+     * @param maxStack
+     *            maximum stack size of the method.
+     * @param maxLocals
+     *            maximum number of local variables for the method.
+     */
+    public void visitMaxs(int maxStack, int maxLocals) {
+        if (mv != null) {
+            mv.visitMaxs(maxStack, maxLocals);
+        }
+    }
+
+    /**
+     * Visits the end of the method. This method, which is the last one to be
+     * called, is used to inform the visitor that all the annotations and
+     * attributes of the method have been visited.
+     */
+    public void visitEnd() {
+        if (mv != null) {
+            mv.visitEnd();
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/MethodWriter.java b/src/jvm/clojure/asm/MethodWriter.java
new file mode 100644
index 0000000..9197318
--- /dev/null
+++ b/src/jvm/clojure/asm/MethodWriter.java
@@ -0,0 +1,2685 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * A {@link MethodVisitor} that generates methods in bytecode form. Each visit
+ * method of this class appends the bytecode corresponding to the visited
+ * instruction to a byte vector, in the order these methods are called.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+class MethodWriter extends MethodVisitor {
+
+    /**
+     * Pseudo access flag used to denote constructors.
+     */
+    static final int ACC_CONSTRUCTOR = 0x80000;
+
+    /**
+     * Frame has exactly the same locals as the previous stack map frame and
+     * number of stack items is zero.
+     */
+    static final int SAME_FRAME = 0; // to 63 (0-3f)
+
+    /**
+     * Frame has exactly the same locals as the previous stack map frame and
+     * number of stack items is 1
+     */
+    static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
+
+    /**
+     * Reserved for future use
+     */
+    static final int RESERVED = 128;
+
+    /**
+     * Frame has exactly the same locals as the previous stack map frame and
+     * number of stack items is 1. Offset is bigger then 63;
+     */
+    static final int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
+
+    /**
+     * Frame where current locals are the same as the locals in the previous
+     * frame, except that the k last locals are absent. The value of k is given
+     * by the formula 251-frame_type.
+     */
+    static final int CHOP_FRAME = 248; // to 250 (f8-fA)
+
+    /**
+     * Frame has exactly the same locals as the previous stack map frame and
+     * number of stack items is zero. Offset is bigger then 63;
+     */
+    static final int SAME_FRAME_EXTENDED = 251; // fb
+
+    /**
+     * Frame where current locals are the same as the locals in the previous
+     * frame, except that k additional locals are defined. The value of k is
+     * given by the formula frame_type-251.
+     */
+    static final int APPEND_FRAME = 252; // to 254 // fc-fe
+
+    /**
+     * Full frame
+     */
+    static final int FULL_FRAME = 255; // ff
+
+    /**
+     * Indicates that the stack map frames must be recomputed from scratch. In
+     * this case the maximum stack size and number of local variables is also
+     * recomputed from scratch.
+     *
+     * @see #compute
+     */
+    private static final int FRAMES = 0;
+
+    /**
+     * Indicates that the maximum stack size and number of local variables must
+     * be automatically computed.
+     *
+     * @see #compute
+     */
+    private static final int MAXS = 1;
+
+    /**
+     * Indicates that nothing must be automatically computed.
+     *
+     * @see #compute
+     */
+    private static final int NOTHING = 2;
+
+    /**
+     * The class writer to which this method must be added.
+     */
+    final ClassWriter cw;
+
+    /**
+     * Access flags of this method.
+     */
+    private int access;
+
+    /**
+     * The index of the constant pool item that contains the name of this
+     * method.
+     */
+    private final int name;
+
+    /**
+     * The index of the constant pool item that contains the descriptor of this
+     * method.
+     */
+    private final int desc;
+
+    /**
+     * The descriptor of this method.
+     */
+    private final String descriptor;
+
+    /**
+     * The signature of this method.
+     */
+    String signature;
+
+    /**
+     * If not zero, indicates that the code of this method must be copied from
+     * the ClassReader associated to this writer in <code>cw.cr</code>. More
+     * precisely, this field gives the index of the first byte to copied from
+     * <code>cw.cr.b</code>.
+     */
+    int classReaderOffset;
+
+    /**
+     * If not zero, indicates that the code of this method must be copied from
+     * the ClassReader associated to this writer in <code>cw.cr</code>. More
+     * precisely, this field gives the number of bytes to copied from
+     * <code>cw.cr.b</code>.
+     */
+    int classReaderLength;
+
+    /**
+     * Number of exceptions that can be thrown by this method.
+     */
+    int exceptionCount;
+
+    /**
+     * The exceptions that can be thrown by this method. More precisely, this
+     * array contains the indexes of the constant pool items that contain the
+     * internal names of these exception classes.
+     */
+    int[] exceptions;
+
+    /**
+     * The annotation default attribute of this method. May be <tt>null</tt>.
+     */
+    private ByteVector annd;
+
+    /**
+     * The runtime visible annotations of this method. May be <tt>null</tt>.
+     */
+    private AnnotationWriter anns;
+
+    /**
+     * The runtime invisible annotations of this method. May be <tt>null</tt>.
+     */
+    private AnnotationWriter ianns;
+
+    /**
+     * The runtime visible parameter annotations of this method. May be
+     * <tt>null</tt>.
+     */
+    private AnnotationWriter[] panns;
+
+    /**
+     * The runtime invisible parameter annotations of this method. May be
+     * <tt>null</tt>.
+     */
+    private AnnotationWriter[] ipanns;
+
+    /**
+     * The number of synthetic parameters of this method.
+     */
+    private int synthetics;
+
+    /**
+     * The non standard attributes of the method.
+     */
+    private Attribute attrs;
+
+    /**
+     * The bytecode of this method.
+     */
+    private ByteVector code = new ByteVector();
+
+    /**
+     * Maximum stack size of this method.
+     */
+    private int maxStack;
+
+    /**
+     * Maximum number of local variables for this method.
+     */
+    private int maxLocals;
+
+    /**
+     * Number of local variables in the current stack map frame.
+     */
+    private int currentLocals;
+
+    /**
+     * Number of stack map frames in the StackMapTable attribute.
+     */
+    private int frameCount;
+
+    /**
+     * The StackMapTable attribute.
+     */
+    private ByteVector stackMap;
+
+    /**
+     * The offset of the last frame that was written in the StackMapTable
+     * attribute.
+     */
+    private int previousFrameOffset;
+
+    /**
+     * The last frame that was written in the StackMapTable attribute.
+     *
+     * @see #frame
+     */
+    private int[] previousFrame;
+
+    /**
+     * The current stack map frame. The first element contains the offset of the
+     * instruction to which the frame corresponds, the second element is the
+     * number of locals and the third one is the number of stack elements. The
+     * local variables start at index 3 and are followed by the operand stack
+     * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
+     * nStack, frame[3] = nLocal. All types are encoded as integers, with the
+     * same format as the one used in {@link Label}, but limited to BASE types.
+     */
+    private int[] frame;
+
+    /**
+     * Number of elements in the exception handler list.
+     */
+    private int handlerCount;
+
+    /**
+     * The first element in the exception handler list.
+     */
+    private Handler firstHandler;
+
+    /**
+     * The last element in the exception handler list.
+     */
+    private Handler lastHandler;
+
+    /**
+     * Number of entries in the LocalVariableTable attribute.
+     */
+    private int localVarCount;
+
+    /**
+     * The LocalVariableTable attribute.
+     */
+    private ByteVector localVar;
+
+    /**
+     * Number of entries in the LocalVariableTypeTable attribute.
+     */
+    private int localVarTypeCount;
+
+    /**
+     * The LocalVariableTypeTable attribute.
+     */
+    private ByteVector localVarType;
+
+    /**
+     * Number of entries in the LineNumberTable attribute.
+     */
+    private int lineNumberCount;
+
+    /**
+     * The LineNumberTable attribute.
+     */
+    private ByteVector lineNumber;
+
+    /**
+     * The non standard attributes of the method's code.
+     */
+    private Attribute cattrs;
+
+    /**
+     * Indicates if some jump instructions are too small and need to be resized.
+     */
+    private boolean resize;
+
+    /**
+     * The number of subroutines in this method.
+     */
+    private int subroutines;
+
+    // ------------------------------------------------------------------------
+
+    /*
+     * Fields for the control flow graph analysis algorithm (used to compute the
+     * maximum stack size). A control flow graph contains one node per "basic
+     * block", and one edge per "jump" from one basic block to another. Each
+     * node (i.e., each basic block) is represented by the Label object that
+     * corresponds to the first instruction of this basic block. Each node also
+     * stores the list of its successors in the graph, as a linked list of Edge
+     * objects.
+     */
+
+    /**
+     * Indicates what must be automatically computed.
+     *
+     * @see #FRAMES
+     * @see #MAXS
+     * @see #NOTHING
+     */
+    private final int compute;
+
+    /**
+     * A list of labels. This list is the list of basic blocks in the method,
+     * i.e. a list of Label objects linked to each other by their
+     * {@link Label#successor} field, in the order they are visited by
+     * {@link MethodVisitor#visitLabel}, and starting with the first basic
+     * block.
+     */
+    private Label labels;
+
+    /**
+     * The previous basic block.
+     */
+    private Label previousBlock;
+
+    /**
+     * The current basic block.
+     */
+    private Label currentBlock;
+
+    /**
+     * The (relative) stack size after the last visited instruction. This size
+     * is relative to the beginning of the current basic block, i.e., the true
+     * stack size after the last visited instruction is equal to the
+     * {@link Label#inputStackTop beginStackSize} of the current basic block
+     * plus <tt>stackSize</tt>.
+     */
+    private int stackSize;
+
+    /**
+     * The (relative) maximum stack size after the last visited instruction.
+     * This size is relative to the beginning of the current basic block, i.e.,
+     * the true maximum stack size after the last visited instruction is equal
+     * to the {@link Label#inputStackTop beginStackSize} of the current basic
+     * block plus <tt>stackSize</tt>.
+     */
+    private int maxStackSize;
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a new {@link MethodWriter}.
+     *
+     * @param cw
+     *            the class writer in which the method must be added.
+     * @param access
+     *            the method's access flags (see {@link Opcodes}).
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type}).
+     * @param signature
+     *            the method's signature. May be <tt>null</tt>.
+     * @param exceptions
+     *            the internal names of the method's exceptions. May be
+     *            <tt>null</tt>.
+     * @param computeMaxs
+     *            <tt>true</tt> if the maximum stack size and number of local
+     *            variables must be automatically computed.
+     * @param computeFrames
+     *            <tt>true</tt> if the stack map tables must be recomputed from
+     *            scratch.
+     */
+    MethodWriter(final ClassWriter cw, final int access, final String name,
+            final String desc, final String signature,
+            final String[] exceptions, final boolean computeMaxs,
+            final boolean computeFrames) {
+        super(Opcodes.ASM4);
+        if (cw.firstMethod == null) {
+            cw.firstMethod = this;
+        } else {
+            cw.lastMethod.mv = this;
+        }
+        cw.lastMethod = this;
+        this.cw = cw;
+        this.access = access;
+        if ("<init>".equals(name)) {
+            this.access |= ACC_CONSTRUCTOR;
+        }
+        this.name = cw.newUTF8(name);
+        this.desc = cw.newUTF8(desc);
+        this.descriptor = desc;
+        if (ClassReader.SIGNATURES) {
+            this.signature = signature;
+        }
+        if (exceptions != null && exceptions.length > 0) {
+            exceptionCount = exceptions.length;
+            this.exceptions = new int[exceptionCount];
+            for (int i = 0; i < exceptionCount; ++i) {
+                this.exceptions[i] = cw.newClass(exceptions[i]);
+            }
+        }
+        this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
+        if (computeMaxs || computeFrames) {
+            // updates maxLocals
+            int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2;
+            if ((access & Opcodes.ACC_STATIC) != 0) {
+                --size;
+            }
+            maxLocals = size;
+            currentLocals = size;
+            // creates and visits the label for the first basic block
+            labels = new Label();
+            labels.status |= Label.PUSHED;
+            visitLabel(labels);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the MethodVisitor abstract class
+    // ------------------------------------------------------------------------
+
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+        if (!ClassReader.ANNOTATIONS) {
+            return null;
+        }
+        annd = new ByteVector();
+        return new AnnotationWriter(cw, false, annd, null, 0);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(final String desc,
+            final boolean visible) {
+        if (!ClassReader.ANNOTATIONS) {
+            return null;
+        }
+        ByteVector bv = new ByteVector();
+        // write type, and reserve space for values count
+        bv.putShort(cw.newUTF8(desc)).putShort(0);
+        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+        if (visible) {
+            aw.next = anns;
+            anns = aw;
+        } else {
+            aw.next = ianns;
+            ianns = aw;
+        }
+        return aw;
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(final int parameter,
+            final String desc, final boolean visible) {
+        if (!ClassReader.ANNOTATIONS) {
+            return null;
+        }
+        ByteVector bv = new ByteVector();
+        if ("Ljava/lang/Synthetic;".equals(desc)) {
+            // workaround for a bug in javac with synthetic parameters
+            // see ClassReader.readParameterAnnotations
+            synthetics = Math.max(synthetics, parameter + 1);
+            return new AnnotationWriter(cw, false, bv, null, 0);
+        }
+        // write type, and reserve space for values count
+        bv.putShort(cw.newUTF8(desc)).putShort(0);
+        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
+        if (visible) {
+            if (panns == null) {
+                panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
+            }
+            aw.next = panns[parameter];
+            panns[parameter] = aw;
+        } else {
+            if (ipanns == null) {
+                ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
+            }
+            aw.next = ipanns[parameter];
+            ipanns[parameter] = aw;
+        }
+        return aw;
+    }
+
+    @Override
+    public void visitAttribute(final Attribute attr) {
+        if (attr.isCodeAttribute()) {
+            attr.next = cattrs;
+            cattrs = attr;
+        } else {
+            attr.next = attrs;
+            attrs = attr;
+        }
+    }
+
+    @Override
+    public void visitCode() {
+    }
+
+    @Override
+    public void visitFrame(final int type, final int nLocal,
+            final Object[] local, final int nStack, final Object[] stack) {
+        if (!ClassReader.FRAMES || compute == FRAMES) {
+            return;
+        }
+
+        if (type == Opcodes.F_NEW) {
+            if (previousFrame == null) {
+                visitImplicitFirstFrame();
+            }
+            currentLocals = nLocal;
+            int frameIndex = startFrame(code.length, nLocal, nStack);
+            for (int i = 0; i < nLocal; ++i) {
+                if (local[i] instanceof String) {
+                    frame[frameIndex++] = Frame.OBJECT
+                            | cw.addType((String) local[i]);
+                } else if (local[i] instanceof Integer) {
+                    frame[frameIndex++] = ((Integer) local[i]).intValue();
+                } else {
+                    frame[frameIndex++] = Frame.UNINITIALIZED
+                            | cw.addUninitializedType("",
+                                    ((Label) local[i]).position);
+                }
+            }
+            for (int i = 0; i < nStack; ++i) {
+                if (stack[i] instanceof String) {
+                    frame[frameIndex++] = Frame.OBJECT
+                            | cw.addType((String) stack[i]);
+                } else if (stack[i] instanceof Integer) {
+                    frame[frameIndex++] = ((Integer) stack[i]).intValue();
+                } else {
+                    frame[frameIndex++] = Frame.UNINITIALIZED
+                            | cw.addUninitializedType("",
+                                    ((Label) stack[i]).position);
+                }
+            }
+            endFrame();
+        } else {
+            int delta;
+            if (stackMap == null) {
+                stackMap = new ByteVector();
+                delta = code.length;
+            } else {
+                delta = code.length - previousFrameOffset - 1;
+                if (delta < 0) {
+                    if (type == Opcodes.F_SAME) {
+                        return;
+                    } else {
+                        throw new IllegalStateException();
+                    }
+                }
+            }
+
+            switch (type) {
+            case Opcodes.F_FULL:
+                currentLocals = nLocal;
+                stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal);
+                for (int i = 0; i < nLocal; ++i) {
+                    writeFrameType(local[i]);
+                }
+                stackMap.putShort(nStack);
+                for (int i = 0; i < nStack; ++i) {
+                    writeFrameType(stack[i]);
+                }
+                break;
+            case Opcodes.F_APPEND:
+                currentLocals += nLocal;
+                stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta);
+                for (int i = 0; i < nLocal; ++i) {
+                    writeFrameType(local[i]);
+                }
+                break;
+            case Opcodes.F_CHOP:
+                currentLocals -= nLocal;
+                stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta);
+                break;
+            case Opcodes.F_SAME:
+                if (delta < 64) {
+                    stackMap.putByte(delta);
+                } else {
+                    stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
+                }
+                break;
+            case Opcodes.F_SAME1:
+                if (delta < 64) {
+                    stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
+                } else {
+                    stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
+                            .putShort(delta);
+                }
+                writeFrameType(stack[0]);
+                break;
+            }
+
+            previousFrameOffset = code.length;
+            ++frameCount;
+        }
+
+        maxStack = Math.max(maxStack, nStack);
+        maxLocals = Math.max(maxLocals, currentLocals);
+    }
+
+    @Override
+    public void visitInsn(final int opcode) {
+        // adds the instruction to the bytecode of the method
+        code.putByte(opcode);
+        // update currentBlock
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(opcode, 0, null, null);
+            } else {
+                // updates current and max stack sizes
+                int size = stackSize + Frame.SIZE[opcode];
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+            // if opcode == ATHROW or xRETURN, ends current block (no successor)
+            if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
+                    || opcode == Opcodes.ATHROW) {
+                noSuccessor();
+            }
+        }
+    }
+
+    @Override
+    public void visitIntInsn(final int opcode, final int operand) {
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(opcode, operand, null, null);
+            } else if (opcode != Opcodes.NEWARRAY) {
+                // updates current and max stack sizes only for NEWARRAY
+                // (stack size variation = 0 for BIPUSH or SIPUSH)
+                int size = stackSize + 1;
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        if (opcode == Opcodes.SIPUSH) {
+            code.put12(opcode, operand);
+        } else { // BIPUSH or NEWARRAY
+            code.put11(opcode, operand);
+        }
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(opcode, var, null, null);
+            } else {
+                // updates current and max stack sizes
+                if (opcode == Opcodes.RET) {
+                    // no stack change, but end of current block (no successor)
+                    currentBlock.status |= Label.RET;
+                    // save 'stackSize' here for future use
+                    // (see {@link #findSubroutineSuccessors})
+                    currentBlock.inputStackTop = stackSize;
+                    noSuccessor();
+                } else { // xLOAD or xSTORE
+                    int size = stackSize + Frame.SIZE[opcode];
+                    if (size > maxStackSize) {
+                        maxStackSize = size;
+                    }
+                    stackSize = size;
+                }
+            }
+        }
+        if (compute != NOTHING) {
+            // updates max locals
+            int n;
+            if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
+                    || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) {
+                n = var + 2;
+            } else {
+                n = var + 1;
+            }
+            if (n > maxLocals) {
+                maxLocals = n;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        if (var < 4 && opcode != Opcodes.RET) {
+            int opt;
+            if (opcode < Opcodes.ISTORE) {
+                /* ILOAD_0 */
+                opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
+            } else {
+                /* ISTORE_0 */
+                opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
+            }
+            code.putByte(opt);
+        } else if (var >= 256) {
+            code.putByte(196 /* WIDE */).put12(opcode, var);
+        } else {
+            code.put11(opcode, var);
+        }
+        if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) {
+            visitLabel(new Label());
+        }
+    }
+
+    @Override
+    public void visitTypeInsn(final int opcode, final String type) {
+        Item i = cw.newClassItem(type);
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(opcode, code.length, cw, i);
+            } else if (opcode == Opcodes.NEW) {
+                // updates current and max stack sizes only if opcode == NEW
+                // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF)
+                int size = stackSize + 1;
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        code.put12(opcode, i.index);
+    }
+
+    @Override
+    public void visitFieldInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        Item i = cw.newFieldItem(owner, name, desc);
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(opcode, 0, cw, i);
+            } else {
+                int size;
+                // computes the stack size variation
+                char c = desc.charAt(0);
+                switch (opcode) {
+                case Opcodes.GETSTATIC:
+                    size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
+                    break;
+                case Opcodes.PUTSTATIC:
+                    size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
+                    break;
+                case Opcodes.GETFIELD:
+                    size = stackSize + (c == 'D' || c == 'J' ? 1 : 0);
+                    break;
+                // case Constants.PUTFIELD:
+                default:
+                    size = stackSize + (c == 'D' || c == 'J' ? -3 : -2);
+                    break;
+                }
+                // updates current and max stack sizes
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        code.put12(opcode, i.index);
+    }
+
+    @Override
+    public void visitMethodInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        boolean itf = opcode == Opcodes.INVOKEINTERFACE;
+        Item i = cw.newMethodItem(owner, name, desc, itf);
+        int argSize = i.intVal;
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(opcode, 0, cw, i);
+            } else {
+                /*
+                 * computes the stack size variation. In order not to recompute
+                 * several times this variation for the same Item, we use the
+                 * intVal field of this item to store this variation, once it
+                 * has been computed. More precisely this intVal field stores
+                 * the sizes of the arguments and of the return value
+                 * corresponding to desc.
+                 */
+                if (argSize == 0) {
+                    // the above sizes have not been computed yet,
+                    // so we compute them...
+                    argSize = Type.getArgumentsAndReturnSizes(desc);
+                    // ... and we save them in order
+                    // not to recompute them in the future
+                    i.intVal = argSize;
+                }
+                int size;
+                if (opcode == Opcodes.INVOKESTATIC) {
+                    size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
+                } else {
+                    size = stackSize - (argSize >> 2) + (argSize & 0x03);
+                }
+                // updates current and max stack sizes
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        if (itf) {
+            if (argSize == 0) {
+                argSize = Type.getArgumentsAndReturnSizes(desc);
+                i.intVal = argSize;
+            }
+            code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
+        } else {
+            code.put12(opcode, i.index);
+        }
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(final String name, final String desc,
+            final Handle bsm, final Object... bsmArgs) {
+        Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs);
+        int argSize = i.intVal;
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i);
+            } else {
+                /*
+                 * computes the stack size variation. In order not to recompute
+                 * several times this variation for the same Item, we use the
+                 * intVal field of this item to store this variation, once it
+                 * has been computed. More precisely this intVal field stores
+                 * the sizes of the arguments and of the return value
+                 * corresponding to desc.
+                 */
+                if (argSize == 0) {
+                    // the above sizes have not been computed yet,
+                    // so we compute them...
+                    argSize = Type.getArgumentsAndReturnSizes(desc);
+                    // ... and we save them in order
+                    // not to recompute them in the future
+                    i.intVal = argSize;
+                }
+                int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
+
+                // updates current and max stack sizes
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        code.put12(Opcodes.INVOKEDYNAMIC, i.index);
+        code.putShort(0);
+    }
+
+    @Override
+    public void visitJumpInsn(final int opcode, final Label label) {
+        Label nextInsn = null;
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(opcode, 0, null, null);
+                // 'label' is the target of a jump instruction
+                label.getFirst().status |= Label.TARGET;
+                // adds 'label' as a successor of this basic block
+                addSuccessor(Edge.NORMAL, label);
+                if (opcode != Opcodes.GOTO) {
+                    // creates a Label for the next basic block
+                    nextInsn = new Label();
+                }
+            } else {
+                if (opcode == Opcodes.JSR) {
+                    if ((label.status & Label.SUBROUTINE) == 0) {
+                        label.status |= Label.SUBROUTINE;
+                        ++subroutines;
+                    }
+                    currentBlock.status |= Label.JSR;
+                    addSuccessor(stackSize + 1, label);
+                    // creates a Label for the next basic block
+                    nextInsn = new Label();
+                    /*
+                     * note that, by construction in this method, a JSR block
+                     * has at least two successors in the control flow graph:
+                     * the first one leads the next instruction after the JSR,
+                     * while the second one leads to the JSR target.
+                     */
+                } else {
+                    // updates current stack size (max stack size unchanged
+                    // because stack size variation always negative in this
+                    // case)
+                    stackSize += Frame.SIZE[opcode];
+                    addSuccessor(stackSize, label);
+                }
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        if ((label.status & Label.RESOLVED) != 0
+                && label.position - code.length < Short.MIN_VALUE) {
+            /*
+             * case of a backward jump with an offset < -32768. In this case we
+             * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx
+             * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
+             * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>
+             * designates the instruction just after the GOTO_W.
+             */
+            if (opcode == Opcodes.GOTO) {
+                code.putByte(200); // GOTO_W
+            } else if (opcode == Opcodes.JSR) {
+                code.putByte(201); // JSR_W
+            } else {
+                // if the IF instruction is transformed into IFNOT GOTO_W the
+                // next instruction becomes the target of the IFNOT instruction
+                if (nextInsn != null) {
+                    nextInsn.status |= Label.TARGET;
+                }
+                code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
+                        : opcode ^ 1);
+                code.putShort(8); // jump offset
+                code.putByte(200); // GOTO_W
+            }
+            label.put(this, code, code.length - 1, true);
+        } else {
+            /*
+             * case of a backward jump with an offset >= -32768, or of a forward
+             * jump with, of course, an unknown offset. In these cases we store
+             * the offset in 2 bytes (which will be increased in
+             * resizeInstructions, if needed).
+             */
+            code.putByte(opcode);
+            label.put(this, code, code.length - 1, false);
+        }
+        if (currentBlock != null) {
+            if (nextInsn != null) {
+                // if the jump instruction is not a GOTO, the next instruction
+                // is also a successor of this instruction. Calling visitLabel
+                // adds the label of this next instruction as a successor of the
+                // current block, and starts a new basic block
+                visitLabel(nextInsn);
+            }
+            if (opcode == Opcodes.GOTO) {
+                noSuccessor();
+            }
+        }
+    }
+
+    @Override
+    public void visitLabel(final Label label) {
+        // resolves previous forward references to label, if any
+        resize |= label.resolve(this, code.length, code.data);
+        // updates currentBlock
+        if ((label.status & Label.DEBUG) != 0) {
+            return;
+        }
+        if (compute == FRAMES) {
+            if (currentBlock != null) {
+                if (label.position == currentBlock.position) {
+                    // successive labels, do not start a new basic block
+                    currentBlock.status |= (label.status & Label.TARGET);
+                    label.frame = currentBlock.frame;
+                    return;
+                }
+                // ends current block (with one new successor)
+                addSuccessor(Edge.NORMAL, label);
+            }
+            // begins a new current block
+            currentBlock = label;
+            if (label.frame == null) {
+                label.frame = new Frame();
+                label.frame.owner = label;
+            }
+            // updates the basic block list
+            if (previousBlock != null) {
+                if (label.position == previousBlock.position) {
+                    previousBlock.status |= (label.status & Label.TARGET);
+                    label.frame = previousBlock.frame;
+                    currentBlock = previousBlock;
+                    return;
+                }
+                previousBlock.successor = label;
+            }
+            previousBlock = label;
+        } else if (compute == MAXS) {
+            if (currentBlock != null) {
+                // ends current block (with one new successor)
+                currentBlock.outputStackMax = maxStackSize;
+                addSuccessor(stackSize, label);
+            }
+            // begins a new current block
+            currentBlock = label;
+            // resets the relative current and max stack sizes
+            stackSize = 0;
+            maxStackSize = 0;
+            // updates the basic block list
+            if (previousBlock != null) {
+                previousBlock.successor = label;
+            }
+            previousBlock = label;
+        }
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        Item i = cw.newConstItem(cst);
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
+            } else {
+                int size;
+                // computes the stack size variation
+                if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
+                    size = stackSize + 2;
+                } else {
+                    size = stackSize + 1;
+                }
+                // updates current and max stack sizes
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        int index = i.index;
+        if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) {
+            code.put12(20 /* LDC2_W */, index);
+        } else if (index >= 256) {
+            code.put12(19 /* LDC_W */, index);
+        } else {
+            code.put11(Opcodes.LDC, index);
+        }
+    }
+
+    @Override
+    public void visitIincInsn(final int var, final int increment) {
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(Opcodes.IINC, var, null, null);
+            }
+        }
+        if (compute != NOTHING) {
+            // updates max locals
+            int n = var + 1;
+            if (n > maxLocals) {
+                maxLocals = n;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        if ((var > 255) || (increment > 127) || (increment < -128)) {
+            code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var)
+                    .putShort(increment);
+        } else {
+            code.putByte(Opcodes.IINC).put11(var, increment);
+        }
+    }
+
+    @Override
+    public void visitTableSwitchInsn(final int min, final int max,
+            final Label dflt, final Label... labels) {
+        // adds the instruction to the bytecode of the method
+        int source = code.length;
+        code.putByte(Opcodes.TABLESWITCH);
+        code.putByteArray(null, 0, (4 - code.length % 4) % 4);
+        dflt.put(this, code, source, true);
+        code.putInt(min).putInt(max);
+        for (int i = 0; i < labels.length; ++i) {
+            labels[i].put(this, code, source, true);
+        }
+        // updates currentBlock
+        visitSwitchInsn(dflt, labels);
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
+            final Label[] labels) {
+        // adds the instruction to the bytecode of the method
+        int source = code.length;
+        code.putByte(Opcodes.LOOKUPSWITCH);
+        code.putByteArray(null, 0, (4 - code.length % 4) % 4);
+        dflt.put(this, code, source, true);
+        code.putInt(labels.length);
+        for (int i = 0; i < labels.length; ++i) {
+            code.putInt(keys[i]);
+            labels[i].put(this, code, source, true);
+        }
+        // updates currentBlock
+        visitSwitchInsn(dflt, labels);
+    }
+
+    private void visitSwitchInsn(final Label dflt, final Label[] labels) {
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);
+                // adds current block successors
+                addSuccessor(Edge.NORMAL, dflt);
+                dflt.getFirst().status |= Label.TARGET;
+                for (int i = 0; i < labels.length; ++i) {
+                    addSuccessor(Edge.NORMAL, labels[i]);
+                    labels[i].getFirst().status |= Label.TARGET;
+                }
+            } else {
+                // updates current stack size (max stack size unchanged)
+                --stackSize;
+                // adds current block successors
+                addSuccessor(stackSize, dflt);
+                for (int i = 0; i < labels.length; ++i) {
+                    addSuccessor(stackSize, labels[i]);
+                }
+            }
+            // ends current block
+            noSuccessor();
+        }
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        Item i = cw.newClassItem(desc);
+        // Label currentBlock = this.currentBlock;
+        if (currentBlock != null) {
+            if (compute == FRAMES) {
+                currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
+            } else {
+                // updates current stack size (max stack size unchanged because
+                // stack size variation always negative or null)
+                stackSize += 1 - dims;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
+    }
+
+    @Override
+    public void visitTryCatchBlock(final Label start, final Label end,
+            final Label handler, final String type) {
+        ++handlerCount;
+        Handler h = new Handler();
+        h.start = start;
+        h.end = end;
+        h.handler = handler;
+        h.desc = type;
+        h.type = type != null ? cw.newClass(type) : 0;
+        if (lastHandler == null) {
+            firstHandler = h;
+        } else {
+            lastHandler.next = h;
+        }
+        lastHandler = h;
+    }
+
+    @Override
+    public void visitLocalVariable(final String name, final String desc,
+            final String signature, final Label start, final Label end,
+            final int index) {
+        if (signature != null) {
+            if (localVarType == null) {
+                localVarType = new ByteVector();
+            }
+            ++localVarTypeCount;
+            localVarType.putShort(start.position)
+                    .putShort(end.position - start.position)
+                    .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature))
+                    .putShort(index);
+        }
+        if (localVar == null) {
+            localVar = new ByteVector();
+        }
+        ++localVarCount;
+        localVar.putShort(start.position)
+                .putShort(end.position - start.position)
+                .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc))
+                .putShort(index);
+        if (compute != NOTHING) {
+            // updates max locals
+            char c = desc.charAt(0);
+            int n = index + (c == 'J' || c == 'D' ? 2 : 1);
+            if (n > maxLocals) {
+                maxLocals = n;
+            }
+        }
+    }
+
+    @Override
+    public void visitLineNumber(final int line, final Label start) {
+        if (lineNumber == null) {
+            lineNumber = new ByteVector();
+        }
+        ++lineNumberCount;
+        lineNumber.putShort(start.position);
+        lineNumber.putShort(line);
+    }
+
+    @Override
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        if (ClassReader.FRAMES && compute == FRAMES) {
+            // completes the control flow graph with exception handler blocks
+            Handler handler = firstHandler;
+            while (handler != null) {
+                Label l = handler.start.getFirst();
+                Label h = handler.handler.getFirst();
+                Label e = handler.end.getFirst();
+                // computes the kind of the edges to 'h'
+                String t = handler.desc == null ? "java/lang/Throwable"
+                        : handler.desc;
+                int kind = Frame.OBJECT | cw.addType(t);
+                // h is an exception handler
+                h.status |= Label.TARGET;
+                // adds 'h' as a successor of labels between 'start' and 'end'
+                while (l != e) {
+                    // creates an edge to 'h'
+                    Edge b = new Edge();
+                    b.info = kind;
+                    b.successor = h;
+                    // adds it to the successors of 'l'
+                    b.next = l.successors;
+                    l.successors = b;
+                    // goes to the next label
+                    l = l.successor;
+                }
+                handler = handler.next;
+            }
+
+            // creates and visits the first (implicit) frame
+            Frame f = labels.frame;
+            Type[] args = Type.getArgumentTypes(descriptor);
+            f.initInputFrame(cw, access, args, this.maxLocals);
+            visitFrame(f);
+
+            /*
+             * fix point algorithm: mark the first basic block as 'changed'
+             * (i.e. put it in the 'changed' list) and, while there are changed
+             * basic blocks, choose one, mark it as unchanged, and update its
+             * successors (which can be changed in the process).
+             */
+            int max = 0;
+            Label changed = labels;
+            while (changed != null) {
+                // removes a basic block from the list of changed basic blocks
+                Label l = changed;
+                changed = changed.next;
+                l.next = null;
+                f = l.frame;
+                // a reachable jump target must be stored in the stack map
+                if ((l.status & Label.TARGET) != 0) {
+                    l.status |= Label.STORE;
+                }
+                // all visited labels are reachable, by definition
+                l.status |= Label.REACHABLE;
+                // updates the (absolute) maximum stack size
+                int blockMax = f.inputStack.length + l.outputStackMax;
+                if (blockMax > max) {
+                    max = blockMax;
+                }
+                // updates the successors of the current basic block
+                Edge e = l.successors;
+                while (e != null) {
+                    Label n = e.successor.getFirst();
+                    boolean change = f.merge(cw, n.frame, e.info);
+                    if (change && n.next == null) {
+                        // if n has changed and is not already in the 'changed'
+                        // list, adds it to this list
+                        n.next = changed;
+                        changed = n;
+                    }
+                    e = e.next;
+                }
+            }
+
+            // visits all the frames that must be stored in the stack map
+            Label l = labels;
+            while (l != null) {
+                f = l.frame;
+                if ((l.status & Label.STORE) != 0) {
+                    visitFrame(f);
+                }
+                if ((l.status & Label.REACHABLE) == 0) {
+                    // finds start and end of dead basic block
+                    Label k = l.successor;
+                    int start = l.position;
+                    int end = (k == null ? code.length : k.position) - 1;
+                    // if non empty basic block
+                    if (end >= start) {
+                        max = Math.max(max, 1);
+                        // replaces instructions with NOP ... NOP ATHROW
+                        for (int i = start; i < end; ++i) {
+                            code.data[i] = Opcodes.NOP;
+                        }
+                        code.data[end] = (byte) Opcodes.ATHROW;
+                        // emits a frame for this unreachable block
+                        int frameIndex = startFrame(start, 0, 1);
+                        frame[frameIndex] = Frame.OBJECT
+                                | cw.addType("java/lang/Throwable");
+                        endFrame();
+                        // removes the start-end range from the exception
+                        // handlers
+                        firstHandler = Handler.remove(firstHandler, l, k);
+                    }
+                }
+                l = l.successor;
+            }
+
+            handler = firstHandler;
+            handlerCount = 0;
+            while (handler != null) {
+                handlerCount += 1;
+                handler = handler.next;
+            }
+
+            this.maxStack = max;
+        } else if (compute == MAXS) {
+            // completes the control flow graph with exception handler blocks
+            Handler handler = firstHandler;
+            while (handler != null) {
+                Label l = handler.start;
+                Label h = handler.handler;
+                Label e = handler.end;
+                // adds 'h' as a successor of labels between 'start' and 'end'
+                while (l != e) {
+                    // creates an edge to 'h'
+                    Edge b = new Edge();
+                    b.info = Edge.EXCEPTION;
+                    b.successor = h;
+                    // adds it to the successors of 'l'
+                    if ((l.status & Label.JSR) == 0) {
+                        b.next = l.successors;
+                        l.successors = b;
+                    } else {
+                        // if l is a JSR block, adds b after the first two edges
+                        // to preserve the hypothesis about JSR block successors
+                        // order (see {@link #visitJumpInsn})
+                        b.next = l.successors.next.next;
+                        l.successors.next.next = b;
+                    }
+                    // goes to the next label
+                    l = l.successor;
+                }
+                handler = handler.next;
+            }
+
+            if (subroutines > 0) {
+                // completes the control flow graph with the RET successors
+                /*
+                 * first step: finds the subroutines. This step determines, for
+                 * each basic block, to which subroutine(s) it belongs.
+                 */
+                // finds the basic blocks that belong to the "main" subroutine
+                int id = 0;
+                labels.visitSubroutine(null, 1, subroutines);
+                // finds the basic blocks that belong to the real subroutines
+                Label l = labels;
+                while (l != null) {
+                    if ((l.status & Label.JSR) != 0) {
+                        // the subroutine is defined by l's TARGET, not by l
+                        Label subroutine = l.successors.next.successor;
+                        // if this subroutine has not been visited yet...
+                        if ((subroutine.status & Label.VISITED) == 0) {
+                            // ...assigns it a new id and finds its basic blocks
+                            id += 1;
+                            subroutine.visitSubroutine(null, (id / 32L) << 32
+                                    | (1L << (id % 32)), subroutines);
+                        }
+                    }
+                    l = l.successor;
+                }
+                // second step: finds the successors of RET blocks
+                l = labels;
+                while (l != null) {
+                    if ((l.status & Label.JSR) != 0) {
+                        Label L = labels;
+                        while (L != null) {
+                            L.status &= ~Label.VISITED2;
+                            L = L.successor;
+                        }
+                        // the subroutine is defined by l's TARGET, not by l
+                        Label subroutine = l.successors.next.successor;
+                        subroutine.visitSubroutine(l, 0, subroutines);
+                    }
+                    l = l.successor;
+                }
+            }
+
+            /*
+             * control flow analysis algorithm: while the block stack is not
+             * empty, pop a block from this stack, update the max stack size,
+             * compute the true (non relative) begin stack size of the
+             * successors of this block, and push these successors onto the
+             * stack (unless they have already been pushed onto the stack).
+             * Note: by hypothesis, the {@link Label#inputStackTop} of the
+             * blocks in the block stack are the true (non relative) beginning
+             * stack sizes of these blocks.
+             */
+            int max = 0;
+            Label stack = labels;
+            while (stack != null) {
+                // pops a block from the stack
+                Label l = stack;
+                stack = stack.next;
+                // computes the true (non relative) max stack size of this block
+                int start = l.inputStackTop;
+                int blockMax = start + l.outputStackMax;
+                // updates the global max stack size
+                if (blockMax > max) {
+                    max = blockMax;
+                }
+                // analyzes the successors of the block
+                Edge b = l.successors;
+                if ((l.status & Label.JSR) != 0) {
+                    // ignores the first edge of JSR blocks (virtual successor)
+                    b = b.next;
+                }
+                while (b != null) {
+                    l = b.successor;
+                    // if this successor has not already been pushed...
+                    if ((l.status & Label.PUSHED) == 0) {
+                        // computes its true beginning stack size...
+                        l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start
+                                + b.info;
+                        // ...and pushes it onto the stack
+                        l.status |= Label.PUSHED;
+                        l.next = stack;
+                        stack = l;
+                    }
+                    b = b.next;
+                }
+            }
+            this.maxStack = Math.max(maxStack, max);
+        } else {
+            this.maxStack = maxStack;
+            this.maxLocals = maxLocals;
+        }
+    }
+
+    @Override
+    public void visitEnd() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods: control flow analysis algorithm
+    // ------------------------------------------------------------------------
+
+    /**
+     * Adds a successor to the {@link #currentBlock currentBlock} block.
+     *
+     * @param info
+     *            information about the control flow edge to be added.
+     * @param successor
+     *            the successor block to be added to the current block.
+     */
+    private void addSuccessor(final int info, final Label successor) {
+        // creates and initializes an Edge object...
+        Edge b = new Edge();
+        b.info = info;
+        b.successor = successor;
+        // ...and adds it to the successor list of the currentBlock block
+        b.next = currentBlock.successors;
+        currentBlock.successors = b;
+    }
+
+    /**
+     * Ends the current basic block. This method must be used in the case where
+     * the current basic block does not have any successor.
+     */
+    private void noSuccessor() {
+        if (compute == FRAMES) {
+            Label l = new Label();
+            l.frame = new Frame();
+            l.frame.owner = l;
+            l.resolve(this, code.length, code.data);
+            previousBlock.successor = l;
+            previousBlock = l;
+        } else {
+            currentBlock.outputStackMax = maxStackSize;
+        }
+        currentBlock = null;
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods: stack map frames
+    // ------------------------------------------------------------------------
+
+    /**
+     * Visits a frame that has been computed from scratch.
+     *
+     * @param f
+     *            the frame that must be visited.
+     */
+    private void visitFrame(final Frame f) {
+        int i, t;
+        int nTop = 0;
+        int nLocal = 0;
+        int nStack = 0;
+        int[] locals = f.inputLocals;
+        int[] stacks = f.inputStack;
+        // computes the number of locals (ignores TOP types that are just after
+        // a LONG or a DOUBLE, and all trailing TOP types)
+        for (i = 0; i < locals.length; ++i) {
+            t = locals[i];
+            if (t == Frame.TOP) {
+                ++nTop;
+            } else {
+                nLocal += nTop + 1;
+                nTop = 0;
+            }
+            if (t == Frame.LONG || t == Frame.DOUBLE) {
+                ++i;
+            }
+        }
+        // computes the stack size (ignores TOP types that are just after
+        // a LONG or a DOUBLE)
+        for (i = 0; i < stacks.length; ++i) {
+            t = stacks[i];
+            ++nStack;
+            if (t == Frame.LONG || t == Frame.DOUBLE) {
+                ++i;
+            }
+        }
+        // visits the frame and its content
+        int frameIndex = startFrame(f.owner.position, nLocal, nStack);
+        for (i = 0; nLocal > 0; ++i, --nLocal) {
+            t = locals[i];
+            frame[frameIndex++] = t;
+            if (t == Frame.LONG || t == Frame.DOUBLE) {
+                ++i;
+            }
+        }
+        for (i = 0; i < stacks.length; ++i) {
+            t = stacks[i];
+            frame[frameIndex++] = t;
+            if (t == Frame.LONG || t == Frame.DOUBLE) {
+                ++i;
+            }
+        }
+        endFrame();
+    }
+
+    /**
+     * Visit the implicit first frame of this method.
+     */
+    private void visitImplicitFirstFrame() {
+        // There can be at most descriptor.length() + 1 locals
+        int frameIndex = startFrame(0, descriptor.length() + 1, 0);
+        if ((access & Opcodes.ACC_STATIC) == 0) {
+            if ((access & ACC_CONSTRUCTOR) == 0) {
+                frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName);
+            } else {
+                frame[frameIndex++] = 6; // Opcodes.UNINITIALIZED_THIS;
+            }
+        }
+        int i = 1;
+        loop: while (true) {
+            int j = i;
+            switch (descriptor.charAt(i++)) {
+            case 'Z':
+            case 'C':
+            case 'B':
+            case 'S':
+            case 'I':
+                frame[frameIndex++] = 1; // Opcodes.INTEGER;
+                break;
+            case 'F':
+                frame[frameIndex++] = 2; // Opcodes.FLOAT;
+                break;
+            case 'J':
+                frame[frameIndex++] = 4; // Opcodes.LONG;
+                break;
+            case 'D':
+                frame[frameIndex++] = 3; // Opcodes.DOUBLE;
+                break;
+            case '[':
+                while (descriptor.charAt(i) == '[') {
+                    ++i;
+                }
+                if (descriptor.charAt(i) == 'L') {
+                    ++i;
+                    while (descriptor.charAt(i) != ';') {
+                        ++i;
+                    }
+                }
+                frame[frameIndex++] = Frame.OBJECT
+                        | cw.addType(descriptor.substring(j, ++i));
+                break;
+            case 'L':
+                while (descriptor.charAt(i) != ';') {
+                    ++i;
+                }
+                frame[frameIndex++] = Frame.OBJECT
+                        | cw.addType(descriptor.substring(j + 1, i++));
+                break;
+            default:
+                break loop;
+            }
+        }
+        frame[1] = frameIndex - 3;
+        endFrame();
+    }
+
+    /**
+     * Starts the visit of a stack map frame.
+     *
+     * @param offset
+     *            the offset of the instruction to which the frame corresponds.
+     * @param nLocal
+     *            the number of local variables in the frame.
+     * @param nStack
+     *            the number of stack elements in the frame.
+     * @return the index of the next element to be written in this frame.
+     */
+    private int startFrame(final int offset, final int nLocal, final int nStack) {
+        int n = 3 + nLocal + nStack;
+        if (frame == null || frame.length < n) {
+            frame = new int[n];
+        }
+        frame[0] = offset;
+        frame[1] = nLocal;
+        frame[2] = nStack;
+        return 3;
+    }
+
+    /**
+     * Checks if the visit of the current frame {@link #frame} is finished, and
+     * if yes, write it in the StackMapTable attribute.
+     */
+    private void endFrame() {
+        if (previousFrame != null) { // do not write the first frame
+            if (stackMap == null) {
+                stackMap = new ByteVector();
+            }
+            writeFrame();
+            ++frameCount;
+        }
+        previousFrame = frame;
+        frame = null;
+    }
+
+    /**
+     * Compress and writes the current frame {@link #frame} in the StackMapTable
+     * attribute.
+     */
+    private void writeFrame() {
+        int clocalsSize = frame[1];
+        int cstackSize = frame[2];
+        if ((cw.version & 0xFFFF) < Opcodes.V1_6) {
+            stackMap.putShort(frame[0]).putShort(clocalsSize);
+            writeFrameTypes(3, 3 + clocalsSize);
+            stackMap.putShort(cstackSize);
+            writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
+            return;
+        }
+        int localsSize = previousFrame[1];
+        int type = FULL_FRAME;
+        int k = 0;
+        int delta;
+        if (frameCount == 0) {
+            delta = frame[0];
+        } else {
+            delta = frame[0] - previousFrame[0] - 1;
+        }
+        if (cstackSize == 0) {
+            k = clocalsSize - localsSize;
+            switch (k) {
+            case -3:
+            case -2:
+            case -1:
+                type = CHOP_FRAME;
+                localsSize = clocalsSize;
+                break;
+            case 0:
+                type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
+                break;
+            case 1:
+            case 2:
+            case 3:
+                type = APPEND_FRAME;
+                break;
+            }
+        } else if (clocalsSize == localsSize && cstackSize == 1) {
+            type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME
+                    : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
+        }
+        if (type != FULL_FRAME) {
+            // verify if locals are the same
+            int l = 3;
+            for (int j = 0; j < localsSize; j++) {
+                if (frame[l] != previousFrame[l]) {
+                    type = FULL_FRAME;
+                    break;
+                }
+                l++;
+            }
+        }
+        switch (type) {
+        case SAME_FRAME:
+            stackMap.putByte(delta);
+            break;
+        case SAME_LOCALS_1_STACK_ITEM_FRAME:
+            stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
+            writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
+            break;
+        case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
+            stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort(
+                    delta);
+            writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
+            break;
+        case SAME_FRAME_EXTENDED:
+            stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
+            break;
+        case CHOP_FRAME:
+            stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
+            break;
+        case APPEND_FRAME:
+            stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
+            writeFrameTypes(3 + localsSize, 3 + clocalsSize);
+            break;
+        // case FULL_FRAME:
+        default:
+            stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize);
+            writeFrameTypes(3, 3 + clocalsSize);
+            stackMap.putShort(cstackSize);
+            writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
+        }
+    }
+
+    /**
+     * Writes some types of the current frame {@link #frame} into the
+     * StackMapTableAttribute. This method converts types from the format used
+     * in {@link Label} to the format used in StackMapTable attributes. In
+     * particular, it converts type table indexes to constant pool indexes.
+     *
+     * @param start
+     *            index of the first type in {@link #frame} to write.
+     * @param end
+     *            index of last type in {@link #frame} to write (exclusive).
+     */
+    private void writeFrameTypes(final int start, final int end) {
+        for (int i = start; i < end; ++i) {
+            int t = frame[i];
+            int d = t & Frame.DIM;
+            if (d == 0) {
+                int v = t & Frame.BASE_VALUE;
+                switch (t & Frame.BASE_KIND) {
+                case Frame.OBJECT:
+                    stackMap.putByte(7).putShort(
+                            cw.newClass(cw.typeTable[v].strVal1));
+                    break;
+                case Frame.UNINITIALIZED:
+                    stackMap.putByte(8).putShort(cw.typeTable[v].intVal);
+                    break;
+                default:
+                    stackMap.putByte(v);
+                }
+            } else {
+                StringBuffer buf = new StringBuffer();
+                d >>= 28;
+                while (d-- > 0) {
+                    buf.append('[');
+                }
+                if ((t & Frame.BASE_KIND) == Frame.OBJECT) {
+                    buf.append('L');
+                    buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
+                    buf.append(';');
+                } else {
+                    switch (t & 0xF) {
+                    case 1:
+                        buf.append('I');
+                        break;
+                    case 2:
+                        buf.append('F');
+                        break;
+                    case 3:
+                        buf.append('D');
+                        break;
+                    case 9:
+                        buf.append('Z');
+                        break;
+                    case 10:
+                        buf.append('B');
+                        break;
+                    case 11:
+                        buf.append('C');
+                        break;
+                    case 12:
+                        buf.append('S');
+                        break;
+                    default:
+                        buf.append('J');
+                    }
+                }
+                stackMap.putByte(7).putShort(cw.newClass(buf.toString()));
+            }
+        }
+    }
+
+    private void writeFrameType(final Object type) {
+        if (type instanceof String) {
+            stackMap.putByte(7).putShort(cw.newClass((String) type));
+        } else if (type instanceof Integer) {
+            stackMap.putByte(((Integer) type).intValue());
+        } else {
+            stackMap.putByte(8).putShort(((Label) type).position);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods: dump bytecode array
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the size of the bytecode of this method.
+     *
+     * @return the size of the bytecode of this method.
+     */
+    final int getSize() {
+        if (classReaderOffset != 0) {
+            return 6 + classReaderLength;
+        }
+        if (resize) {
+            // replaces the temporary jump opcodes introduced by Label.resolve.
+            if (ClassReader.RESIZE) {
+                resizeInstructions();
+            } else {
+                throw new RuntimeException("Method code too large!");
+            }
+        }
+        int size = 8;
+        if (code.length > 0) {
+            if (code.length > 65536) {
+                throw new RuntimeException("Method code too large!");
+            }
+            cw.newUTF8("Code");
+            size += 18 + code.length + 8 * handlerCount;
+            if (localVar != null) {
+                cw.newUTF8("LocalVariableTable");
+                size += 8 + localVar.length;
+            }
+            if (localVarType != null) {
+                cw.newUTF8("LocalVariableTypeTable");
+                size += 8 + localVarType.length;
+            }
+            if (lineNumber != null) {
+                cw.newUTF8("LineNumberTable");
+                size += 8 + lineNumber.length;
+            }
+            if (stackMap != null) {
+                boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
+                cw.newUTF8(zip ? "StackMapTable" : "StackMap");
+                size += 8 + stackMap.length;
+            }
+            if (cattrs != null) {
+                size += cattrs.getSize(cw, code.data, code.length, maxStack,
+                        maxLocals);
+            }
+        }
+        if (exceptionCount > 0) {
+            cw.newUTF8("Exceptions");
+            size += 8 + 2 * exceptionCount;
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((cw.version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                cw.newUTF8("Synthetic");
+                size += 6;
+            }
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            cw.newUTF8("Deprecated");
+            size += 6;
+        }
+        if (ClassReader.SIGNATURES && signature != null) {
+            cw.newUTF8("Signature");
+            cw.newUTF8(signature);
+            size += 8;
+        }
+        if (ClassReader.ANNOTATIONS && annd != null) {
+            cw.newUTF8("AnnotationDefault");
+            size += 6 + annd.length;
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            cw.newUTF8("RuntimeVisibleAnnotations");
+            size += 8 + anns.getSize();
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            cw.newUTF8("RuntimeInvisibleAnnotations");
+            size += 8 + ianns.getSize();
+        }
+        if (ClassReader.ANNOTATIONS && panns != null) {
+            cw.newUTF8("RuntimeVisibleParameterAnnotations");
+            size += 7 + 2 * (panns.length - synthetics);
+            for (int i = panns.length - 1; i >= synthetics; --i) {
+                size += panns[i] == null ? 0 : panns[i].getSize();
+            }
+        }
+        if (ClassReader.ANNOTATIONS && ipanns != null) {
+            cw.newUTF8("RuntimeInvisibleParameterAnnotations");
+            size += 7 + 2 * (ipanns.length - synthetics);
+            for (int i = ipanns.length - 1; i >= synthetics; --i) {
+                size += ipanns[i] == null ? 0 : ipanns[i].getSize();
+            }
+        }
+        if (attrs != null) {
+            size += attrs.getSize(cw, null, 0, -1, -1);
+        }
+        return size;
+    }
+
+    /**
+     * Puts the bytecode of this method in the given byte vector.
+     *
+     * @param out
+     *            the byte vector into which the bytecode of this method must be
+     *            copied.
+     */
+    final void put(final ByteVector out) {
+        final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC;
+        int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED
+                | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE
+                | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR);
+        out.putShort(access & ~mask).putShort(name).putShort(desc);
+        if (classReaderOffset != 0) {
+            out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength);
+            return;
+        }
+        int attributeCount = 0;
+        if (code.length > 0) {
+            ++attributeCount;
+        }
+        if (exceptionCount > 0) {
+            ++attributeCount;
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((cw.version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                ++attributeCount;
+            }
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            ++attributeCount;
+        }
+        if (ClassReader.SIGNATURES && signature != null) {
+            ++attributeCount;
+        }
+        if (ClassReader.ANNOTATIONS && annd != null) {
+            ++attributeCount;
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            ++attributeCount;
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            ++attributeCount;
+        }
+        if (ClassReader.ANNOTATIONS && panns != null) {
+            ++attributeCount;
+        }
+        if (ClassReader.ANNOTATIONS && ipanns != null) {
+            ++attributeCount;
+        }
+        if (attrs != null) {
+            attributeCount += attrs.getCount();
+        }
+        out.putShort(attributeCount);
+        if (code.length > 0) {
+            int size = 12 + code.length + 8 * handlerCount;
+            if (localVar != null) {
+                size += 8 + localVar.length;
+            }
+            if (localVarType != null) {
+                size += 8 + localVarType.length;
+            }
+            if (lineNumber != null) {
+                size += 8 + lineNumber.length;
+            }
+            if (stackMap != null) {
+                size += 8 + stackMap.length;
+            }
+            if (cattrs != null) {
+                size += cattrs.getSize(cw, code.data, code.length, maxStack,
+                        maxLocals);
+            }
+            out.putShort(cw.newUTF8("Code")).putInt(size);
+            out.putShort(maxStack).putShort(maxLocals);
+            out.putInt(code.length).putByteArray(code.data, 0, code.length);
+            out.putShort(handlerCount);
+            if (handlerCount > 0) {
+                Handler h = firstHandler;
+                while (h != null) {
+                    out.putShort(h.start.position).putShort(h.end.position)
+                            .putShort(h.handler.position).putShort(h.type);
+                    h = h.next;
+                }
+            }
+            attributeCount = 0;
+            if (localVar != null) {
+                ++attributeCount;
+            }
+            if (localVarType != null) {
+                ++attributeCount;
+            }
+            if (lineNumber != null) {
+                ++attributeCount;
+            }
+            if (stackMap != null) {
+                ++attributeCount;
+            }
+            if (cattrs != null) {
+                attributeCount += cattrs.getCount();
+            }
+            out.putShort(attributeCount);
+            if (localVar != null) {
+                out.putShort(cw.newUTF8("LocalVariableTable"));
+                out.putInt(localVar.length + 2).putShort(localVarCount);
+                out.putByteArray(localVar.data, 0, localVar.length);
+            }
+            if (localVarType != null) {
+                out.putShort(cw.newUTF8("LocalVariableTypeTable"));
+                out.putInt(localVarType.length + 2).putShort(localVarTypeCount);
+                out.putByteArray(localVarType.data, 0, localVarType.length);
+            }
+            if (lineNumber != null) {
+                out.putShort(cw.newUTF8("LineNumberTable"));
+                out.putInt(lineNumber.length + 2).putShort(lineNumberCount);
+                out.putByteArray(lineNumber.data, 0, lineNumber.length);
+            }
+            if (stackMap != null) {
+                boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
+                out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap"));
+                out.putInt(stackMap.length + 2).putShort(frameCount);
+                out.putByteArray(stackMap.data, 0, stackMap.length);
+            }
+            if (cattrs != null) {
+                cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
+            }
+        }
+        if (exceptionCount > 0) {
+            out.putShort(cw.newUTF8("Exceptions")).putInt(
+                    2 * exceptionCount + 2);
+            out.putShort(exceptionCount);
+            for (int i = 0; i < exceptionCount; ++i) {
+                out.putShort(exceptions[i]);
+            }
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if ((cw.version & 0xFFFF) < Opcodes.V1_5
+                    || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) {
+                out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+            }
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+        }
+        if (ClassReader.SIGNATURES && signature != null) {
+            out.putShort(cw.newUTF8("Signature")).putInt(2)
+                    .putShort(cw.newUTF8(signature));
+        }
+        if (ClassReader.ANNOTATIONS && annd != null) {
+            out.putShort(cw.newUTF8("AnnotationDefault"));
+            out.putInt(annd.length);
+            out.putByteArray(annd.data, 0, annd.length);
+        }
+        if (ClassReader.ANNOTATIONS && anns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+            anns.put(out);
+        }
+        if (ClassReader.ANNOTATIONS && ianns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+            ianns.put(out);
+        }
+        if (ClassReader.ANNOTATIONS && panns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
+            AnnotationWriter.put(panns, synthetics, out);
+        }
+        if (ClassReader.ANNOTATIONS && ipanns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
+            AnnotationWriter.put(ipanns, synthetics, out);
+        }
+        if (attrs != null) {
+            attrs.put(cw, null, 0, -1, -1, out);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
+    // ------------------------------------------------------------------------
+
+    /**
+     * Resizes and replaces the temporary instructions inserted by
+     * {@link Label#resolve} for wide forward jumps, while keeping jump offsets
+     * and instruction addresses consistent. This may require to resize other
+     * existing instructions, or even to introduce new instructions: for
+     * example, increasing the size of an instruction by 2 at the middle of a
+     * method can increases the offset of an IFEQ instruction from 32766 to
+     * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
+     * 32765. This, in turn, may require to increase the size of another jump
+     * instruction, and so on... All these operations are handled automatically
+     * by this method.
+     * <p>
+     * <i>This method must be called after all the method that is being built
+     * has been visited</i>. In particular, the {@link Label Label} objects used
+     * to construct the method are no longer valid after this method has been
+     * called.
+     */
+    private void resizeInstructions() {
+        byte[] b = code.data; // bytecode of the method
+        int u, v, label; // indexes in b
+        int i, j; // loop indexes
+        /*
+         * 1st step: As explained above, resizing an instruction may require to
+         * resize another one, which may require to resize yet another one, and
+         * so on. The first step of the algorithm consists in finding all the
+         * instructions that need to be resized, without modifying the code.
+         * This is done by the following "fix point" algorithm:
+         *
+         * Parse the code to find the jump instructions whose offset will need
+         * more than 2 bytes to be stored (the future offset is computed from
+         * the current offset and from the number of bytes that will be inserted
+         * or removed between the source and target instructions). For each such
+         * instruction, adds an entry in (a copy of) the indexes and sizes
+         * arrays (if this has not already been done in a previous iteration!).
+         *
+         * If at least one entry has been added during the previous step, go
+         * back to the beginning, otherwise stop.
+         *
+         * In fact the real algorithm is complicated by the fact that the size
+         * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
+         * position in the bytecode (because of padding). In order to ensure the
+         * convergence of the algorithm, the number of bytes to be added or
+         * removed from these instructions is over estimated during the previous
+         * loop, and computed exactly only after the loop is finished (this
+         * requires another pass to parse the bytecode of the method).
+         */
+        int[] allIndexes = new int[0]; // copy of indexes
+        int[] allSizes = new int[0]; // copy of sizes
+        boolean[] resize; // instructions to be resized
+        int newOffset; // future offset of a jump instruction
+
+        resize = new boolean[code.length];
+
+        // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
+        int state = 3;
+        do {
+            if (state == 3) {
+                state = 2;
+            }
+            u = 0;
+            while (u < b.length) {
+                int opcode = b[u] & 0xFF; // opcode of current instruction
+                int insert = 0; // bytes to be added after this instruction
+
+                switch (ClassWriter.TYPE[opcode]) {
+                case ClassWriter.NOARG_INSN:
+                case ClassWriter.IMPLVAR_INSN:
+                    u += 1;
+                    break;
+                case ClassWriter.LABEL_INSN:
+                    if (opcode > 201) {
+                        // converts temporary opcodes 202 to 217, 218 and
+                        // 219 to IFEQ ... JSR (inclusive), IFNULL and
+                        // IFNONNULL
+                        opcode = opcode < 218 ? opcode - 49 : opcode - 20;
+                        label = u + readUnsignedShort(b, u + 1);
+                    } else {
+                        label = u + readShort(b, u + 1);
+                    }
+                    newOffset = getNewOffset(allIndexes, allSizes, u, label);
+                    if (newOffset < Short.MIN_VALUE
+                            || newOffset > Short.MAX_VALUE) {
+                        if (!resize[u]) {
+                            if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) {
+                                // two additional bytes will be required to
+                                // replace this GOTO or JSR instruction with
+                                // a GOTO_W or a JSR_W
+                                insert = 2;
+                            } else {
+                                // five additional bytes will be required to
+                                // replace this IFxxx <l> instruction with
+                                // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
+                                // is the "opposite" opcode of IFxxx (i.e.,
+                                // IFNE for IFEQ) and where <l'> designates
+                                // the instruction just after the GOTO_W.
+                                insert = 5;
+                            }
+                            resize[u] = true;
+                        }
+                    }
+                    u += 3;
+                    break;
+                case ClassWriter.LABELW_INSN:
+                    u += 5;
+                    break;
+                case ClassWriter.TABL_INSN:
+                    if (state == 1) {
+                        // true number of bytes to be added (or removed)
+                        // from this instruction = (future number of padding
+                        // bytes - current number of padding byte) -
+                        // previously over estimated variation =
+                        // = ((3 - newOffset%4) - (3 - u%4)) - u%4
+                        // = (-newOffset%4 + u%4) - u%4
+                        // = -(newOffset & 3)
+                        newOffset = getNewOffset(allIndexes, allSizes, 0, u);
+                        insert = -(newOffset & 3);
+                    } else if (!resize[u]) {
+                        // over estimation of the number of bytes to be
+                        // added to this instruction = 3 - current number
+                        // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
+                        insert = u & 3;
+                        resize[u] = true;
+                    }
+                    // skips instruction
+                    u = u + 4 - (u & 3);
+                    u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
+                    break;
+                case ClassWriter.LOOK_INSN:
+                    if (state == 1) {
+                        // like TABL_INSN
+                        newOffset = getNewOffset(allIndexes, allSizes, 0, u);
+                        insert = -(newOffset & 3);
+                    } else if (!resize[u]) {
+                        // like TABL_INSN
+                        insert = u & 3;
+                        resize[u] = true;
+                    }
+                    // skips instruction
+                    u = u + 4 - (u & 3);
+                    u += 8 * readInt(b, u + 4) + 8;
+                    break;
+                case ClassWriter.WIDE_INSN:
+                    opcode = b[u + 1] & 0xFF;
+                    if (opcode == Opcodes.IINC) {
+                        u += 6;
+                    } else {
+                        u += 4;
+                    }
+                    break;
+                case ClassWriter.VAR_INSN:
+                case ClassWriter.SBYTE_INSN:
+                case ClassWriter.LDC_INSN:
+                    u += 2;
+                    break;
+                case ClassWriter.SHORT_INSN:
+                case ClassWriter.LDCW_INSN:
+                case ClassWriter.FIELDORMETH_INSN:
+                case ClassWriter.TYPE_INSN:
+                case ClassWriter.IINC_INSN:
+                    u += 3;
+                    break;
+                case ClassWriter.ITFMETH_INSN:
+                case ClassWriter.INDYMETH_INSN:
+                    u += 5;
+                    break;
+                // case ClassWriter.MANA_INSN:
+                default:
+                    u += 4;
+                    break;
+                }
+                if (insert != 0) {
+                    // adds a new (u, insert) entry in the allIndexes and
+                    // allSizes arrays
+                    int[] newIndexes = new int[allIndexes.length + 1];
+                    int[] newSizes = new int[allSizes.length + 1];
+                    System.arraycopy(allIndexes, 0, newIndexes, 0,
+                            allIndexes.length);
+                    System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length);
+                    newIndexes[allIndexes.length] = u;
+                    newSizes[allSizes.length] = insert;
+                    allIndexes = newIndexes;
+                    allSizes = newSizes;
+                    if (insert > 0) {
+                        state = 3;
+                    }
+                }
+            }
+            if (state < 3) {
+                --state;
+            }
+        } while (state != 0);
+
+        // 2nd step:
+        // copies the bytecode of the method into a new bytevector, updates the
+        // offsets, and inserts (or removes) bytes as requested.
+
+        ByteVector newCode = new ByteVector(code.length);
+
+        u = 0;
+        while (u < code.length) {
+            int opcode = b[u] & 0xFF;
+            switch (ClassWriter.TYPE[opcode]) {
+            case ClassWriter.NOARG_INSN:
+            case ClassWriter.IMPLVAR_INSN:
+                newCode.putByte(opcode);
+                u += 1;
+                break;
+            case ClassWriter.LABEL_INSN:
+                if (opcode > 201) {
+                    // changes temporary opcodes 202 to 217 (inclusive), 218
+                    // and 219 to IFEQ ... JSR (inclusive), IFNULL and
+                    // IFNONNULL
+                    opcode = opcode < 218 ? opcode - 49 : opcode - 20;
+                    label = u + readUnsignedShort(b, u + 1);
+                } else {
+                    label = u + readShort(b, u + 1);
+                }
+                newOffset = getNewOffset(allIndexes, allSizes, u, label);
+                if (resize[u]) {
+                    // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
+                    // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
+                    // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
+                    // and where <l'> designates the instruction just after
+                    // the GOTO_W.
+                    if (opcode == Opcodes.GOTO) {
+                        newCode.putByte(200); // GOTO_W
+                    } else if (opcode == Opcodes.JSR) {
+                        newCode.putByte(201); // JSR_W
+                    } else {
+                        newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1
+                                : opcode ^ 1);
+                        newCode.putShort(8); // jump offset
+                        newCode.putByte(200); // GOTO_W
+                        // newOffset now computed from start of GOTO_W
+                        newOffset -= 3;
+                    }
+                    newCode.putInt(newOffset);
+                } else {
+                    newCode.putByte(opcode);
+                    newCode.putShort(newOffset);
+                }
+                u += 3;
+                break;
+            case ClassWriter.LABELW_INSN:
+                label = u + readInt(b, u + 1);
+                newOffset = getNewOffset(allIndexes, allSizes, u, label);
+                newCode.putByte(opcode);
+                newCode.putInt(newOffset);
+                u += 5;
+                break;
+            case ClassWriter.TABL_INSN:
+                // skips 0 to 3 padding bytes
+                v = u;
+                u = u + 4 - (v & 3);
+                // reads and copies instruction
+                newCode.putByte(Opcodes.TABLESWITCH);
+                newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
+                label = v + readInt(b, u);
+                u += 4;
+                newOffset = getNewOffset(allIndexes, allSizes, v, label);
+                newCode.putInt(newOffset);
+                j = readInt(b, u);
+                u += 4;
+                newCode.putInt(j);
+                j = readInt(b, u) - j + 1;
+                u += 4;
+                newCode.putInt(readInt(b, u - 4));
+                for (; j > 0; --j) {
+                    label = v + readInt(b, u);
+                    u += 4;
+                    newOffset = getNewOffset(allIndexes, allSizes, v, label);
+                    newCode.putInt(newOffset);
+                }
+                break;
+            case ClassWriter.LOOK_INSN:
+                // skips 0 to 3 padding bytes
+                v = u;
+                u = u + 4 - (v & 3);
+                // reads and copies instruction
+                newCode.putByte(Opcodes.LOOKUPSWITCH);
+                newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4);
+                label = v + readInt(b, u);
+                u += 4;
+                newOffset = getNewOffset(allIndexes, allSizes, v, label);
+                newCode.putInt(newOffset);
+                j = readInt(b, u);
+                u += 4;
+                newCode.putInt(j);
+                for (; j > 0; --j) {
+                    newCode.putInt(readInt(b, u));
+                    u += 4;
+                    label = v + readInt(b, u);
+                    u += 4;
+                    newOffset = getNewOffset(allIndexes, allSizes, v, label);
+                    newCode.putInt(newOffset);
+                }
+                break;
+            case ClassWriter.WIDE_INSN:
+                opcode = b[u + 1] & 0xFF;
+                if (opcode == Opcodes.IINC) {
+                    newCode.putByteArray(b, u, 6);
+                    u += 6;
+                } else {
+                    newCode.putByteArray(b, u, 4);
+                    u += 4;
+                }
+                break;
+            case ClassWriter.VAR_INSN:
+            case ClassWriter.SBYTE_INSN:
+            case ClassWriter.LDC_INSN:
+                newCode.putByteArray(b, u, 2);
+                u += 2;
+                break;
+            case ClassWriter.SHORT_INSN:
+            case ClassWriter.LDCW_INSN:
+            case ClassWriter.FIELDORMETH_INSN:
+            case ClassWriter.TYPE_INSN:
+            case ClassWriter.IINC_INSN:
+                newCode.putByteArray(b, u, 3);
+                u += 3;
+                break;
+            case ClassWriter.ITFMETH_INSN:
+            case ClassWriter.INDYMETH_INSN:
+                newCode.putByteArray(b, u, 5);
+                u += 5;
+                break;
+            // case MANA_INSN:
+            default:
+                newCode.putByteArray(b, u, 4);
+                u += 4;
+                break;
+            }
+        }
+
+        // recomputes the stack map frames
+        if (frameCount > 0) {
+            if (compute == FRAMES) {
+                frameCount = 0;
+                stackMap = null;
+                previousFrame = null;
+                frame = null;
+                Frame f = new Frame();
+                f.owner = labels;
+                Type[] args = Type.getArgumentTypes(descriptor);
+                f.initInputFrame(cw, access, args, maxLocals);
+                visitFrame(f);
+                Label l = labels;
+                while (l != null) {
+                    /*
+                     * here we need the original label position. getNewOffset
+                     * must therefore never have been called for this label.
+                     */
+                    u = l.position - 3;
+                    if ((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) {
+                        getNewOffset(allIndexes, allSizes, l);
+                        // TODO update offsets in UNINITIALIZED values
+                        visitFrame(l.frame);
+                    }
+                    l = l.successor;
+                }
+            } else {
+                /*
+                 * Resizing an existing stack map frame table is really hard.
+                 * Not only the table must be parsed to update the offets, but
+                 * new frames may be needed for jump instructions that were
+                 * inserted by this method. And updating the offsets or
+                 * inserting frames can change the format of the following
+                 * frames, in case of packed frames. In practice the whole table
+                 * must be recomputed. For this the frames are marked as
+                 * potentially invalid. This will cause the whole class to be
+                 * reread and rewritten with the COMPUTE_FRAMES option (see the
+                 * ClassWriter.toByteArray method). This is not very efficient
+                 * but is much easier and requires much less code than any other
+                 * method I can think of.
+                 */
+                cw.invalidFrames = true;
+            }
+        }
+        // updates the exception handler block labels
+        Handler h = firstHandler;
+        while (h != null) {
+            getNewOffset(allIndexes, allSizes, h.start);
+            getNewOffset(allIndexes, allSizes, h.end);
+            getNewOffset(allIndexes, allSizes, h.handler);
+            h = h.next;
+        }
+        // updates the instructions addresses in the
+        // local var and line number tables
+        for (i = 0; i < 2; ++i) {
+            ByteVector bv = i == 0 ? localVar : localVarType;
+            if (bv != null) {
+                b = bv.data;
+                u = 0;
+                while (u < bv.length) {
+                    label = readUnsignedShort(b, u);
+                    newOffset = getNewOffset(allIndexes, allSizes, 0, label);
+                    writeShort(b, u, newOffset);
+                    label += readUnsignedShort(b, u + 2);
+                    newOffset = getNewOffset(allIndexes, allSizes, 0, label)
+                            - newOffset;
+                    writeShort(b, u + 2, newOffset);
+                    u += 10;
+                }
+            }
+        }
+        if (lineNumber != null) {
+            b = lineNumber.data;
+            u = 0;
+            while (u < lineNumber.length) {
+                writeShort(
+                        b,
+                        u,
+                        getNewOffset(allIndexes, allSizes, 0,
+                                readUnsignedShort(b, u)));
+                u += 4;
+            }
+        }
+        // updates the labels of the other attributes
+        Attribute attr = cattrs;
+        while (attr != null) {
+            Label[] labels = attr.getLabels();
+            if (labels != null) {
+                for (i = labels.length - 1; i >= 0; --i) {
+                    getNewOffset(allIndexes, allSizes, labels[i]);
+                }
+            }
+            attr = attr.next;
+        }
+
+        // replaces old bytecodes with new ones
+        code = newCode;
+    }
+
+    /**
+     * Reads an unsigned short value in the given byte array.
+     *
+     * @param b
+     *            a byte array.
+     * @param index
+     *            the start index of the value to be read.
+     * @return the read value.
+     */
+    static int readUnsignedShort(final byte[] b, final int index) {
+        return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
+    }
+
+    /**
+     * Reads a signed short value in the given byte array.
+     *
+     * @param b
+     *            a byte array.
+     * @param index
+     *            the start index of the value to be read.
+     * @return the read value.
+     */
+    static short readShort(final byte[] b, final int index) {
+        return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
+    }
+
+    /**
+     * Reads a signed int value in the given byte array.
+     *
+     * @param b
+     *            a byte array.
+     * @param index
+     *            the start index of the value to be read.
+     * @return the read value.
+     */
+    static int readInt(final byte[] b, final int index) {
+        return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
+                | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
+    }
+
+    /**
+     * Writes a short value in the given byte array.
+     *
+     * @param b
+     *            a byte array.
+     * @param index
+     *            where the first byte of the short value must be written.
+     * @param s
+     *            the value to be written in the given byte array.
+     */
+    static void writeShort(final byte[] b, final int index, final int s) {
+        b[index] = (byte) (s >>> 8);
+        b[index + 1] = (byte) s;
+    }
+
+    /**
+     * Computes the future value of a bytecode offset.
+     * <p>
+     * Note: it is possible to have several entries for the same instruction in
+     * the <tt>indexes</tt> and <tt>sizes</tt>: two entries (index=a,size=b) and
+     * (index=a,size=b') are equivalent to a single entry (index=a,size=b+b').
+     *
+     * @param indexes
+     *            current positions of the instructions to be resized. Each
+     *            instruction must be designated by the index of its <i>last</i>
+     *            byte, plus one (or, in other words, by the index of the
+     *            <i>first</i> byte of the <i>next</i> instruction).
+     * @param sizes
+     *            the number of bytes to be <i>added</i> to the above
+     *            instructions. More precisely, for each i < <tt>len</tt>,
+     *            <tt>sizes</tt>[i] bytes will be added at the end of the
+     *            instruction designated by <tt>indexes</tt>[i] or, if
+     *            <tt>sizes</tt>[i] is negative, the <i>last</i> |
+     *            <tt>sizes[i]</tt>| bytes of the instruction will be removed
+     *            (the instruction size <i>must not</i> become negative or
+     *            null).
+     * @param begin
+     *            index of the first byte of the source instruction.
+     * @param end
+     *            index of the first byte of the target instruction.
+     * @return the future value of the given bytecode offset.
+     */
+    static int getNewOffset(final int[] indexes, final int[] sizes,
+            final int begin, final int end) {
+        int offset = end - begin;
+        for (int i = 0; i < indexes.length; ++i) {
+            if (begin < indexes[i] && indexes[i] <= end) {
+                // forward jump
+                offset += sizes[i];
+            } else if (end < indexes[i] && indexes[i] <= begin) {
+                // backward jump
+                offset -= sizes[i];
+            }
+        }
+        return offset;
+    }
+
+    /**
+     * Updates the offset of the given label.
+     *
+     * @param indexes
+     *            current positions of the instructions to be resized. Each
+     *            instruction must be designated by the index of its <i>last</i>
+     *            byte, plus one (or, in other words, by the index of the
+     *            <i>first</i> byte of the <i>next</i> instruction).
+     * @param sizes
+     *            the number of bytes to be <i>added</i> to the above
+     *            instructions. More precisely, for each i < <tt>len</tt>,
+     *            <tt>sizes</tt>[i] bytes will be added at the end of the
+     *            instruction designated by <tt>indexes</tt>[i] or, if
+     *            <tt>sizes</tt>[i] is negative, the <i>last</i> |
+     *            <tt>sizes[i]</tt>| bytes of the instruction will be removed
+     *            (the instruction size <i>must not</i> become negative or
+     *            null).
+     * @param label
+     *            the label whose offset must be updated.
+     */
+    static void getNewOffset(final int[] indexes, final int[] sizes,
+            final Label label) {
+        if ((label.status & Label.RESIZED) == 0) {
+            label.position = getNewOffset(indexes, sizes, 0, label.position);
+            label.status |= Label.RESIZED;
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/Opcodes.java b/src/jvm/clojure/asm/Opcodes.java
new file mode 100644
index 0000000..b126807
--- /dev/null
+++ b/src/jvm/clojure/asm/Opcodes.java
@@ -0,0 +1,358 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+/**
+ * Defines the JVM opcodes, access flags and array type codes. This interface
+ * does not define all the JVM opcodes because some opcodes are automatically
+ * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced
+ * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n
+ * opcodes are therefore not defined in this interface. Likewise for LDC,
+ * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and
+ * JSR_W.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public interface Opcodes {
+
+    // ASM API versions
+
+    int ASM4 = 4 << 16 | 0 << 8 | 0;
+
+    // versions
+
+    int V1_1 = 3 << 16 | 45;
+    int V1_2 = 0 << 16 | 46;
+    int V1_3 = 0 << 16 | 47;
+    int V1_4 = 0 << 16 | 48;
+    int V1_5 = 0 << 16 | 49;
+    int V1_6 = 0 << 16 | 50;
+    int V1_7 = 0 << 16 | 51;
+
+    // access flags
+
+    int ACC_PUBLIC = 0x0001; // class, field, method
+    int ACC_PRIVATE = 0x0002; // class, field, method
+    int ACC_PROTECTED = 0x0004; // class, field, method
+    int ACC_STATIC = 0x0008; // field, method
+    int ACC_FINAL = 0x0010; // class, field, method
+    int ACC_SUPER = 0x0020; // class
+    int ACC_SYNCHRONIZED = 0x0020; // method
+    int ACC_VOLATILE = 0x0040; // field
+    int ACC_BRIDGE = 0x0040; // method
+    int ACC_VARARGS = 0x0080; // method
+    int ACC_TRANSIENT = 0x0080; // field
+    int ACC_NATIVE = 0x0100; // method
+    int ACC_INTERFACE = 0x0200; // class
+    int ACC_ABSTRACT = 0x0400; // class, method
+    int ACC_STRICT = 0x0800; // method
+    int ACC_SYNTHETIC = 0x1000; // class, field, method
+    int ACC_ANNOTATION = 0x2000; // class
+    int ACC_ENUM = 0x4000; // class(?) field inner
+
+    // ASM specific pseudo access flags
+
+    int ACC_DEPRECATED = 0x20000; // class, field, method
+
+    // types for NEWARRAY
+
+    int T_BOOLEAN = 4;
+    int T_CHAR = 5;
+    int T_FLOAT = 6;
+    int T_DOUBLE = 7;
+    int T_BYTE = 8;
+    int T_SHORT = 9;
+    int T_INT = 10;
+    int T_LONG = 11;
+
+    // tags for Handle
+
+    int H_GETFIELD = 1;
+    int H_GETSTATIC = 2;
+    int H_PUTFIELD = 3;
+    int H_PUTSTATIC = 4;
+    int H_INVOKEVIRTUAL = 5;
+    int H_INVOKESTATIC = 6;
+    int H_INVOKESPECIAL = 7;
+    int H_NEWINVOKESPECIAL = 8;
+    int H_INVOKEINTERFACE = 9;
+
+    // stack map frame types
+
+    /**
+     * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}.
+     */
+    int F_NEW = -1;
+
+    /**
+     * Represents a compressed frame with complete frame data.
+     */
+    int F_FULL = 0;
+
+    /**
+     * Represents a compressed frame where locals are the same as the locals in
+     * the previous frame, except that additional 1-3 locals are defined, and
+     * with an empty stack.
+     */
+    int F_APPEND = 1;
+
+    /**
+     * Represents a compressed frame where locals are the same as the locals in
+     * the previous frame, except that the last 1-3 locals are absent and with
+     * an empty stack.
+     */
+    int F_CHOP = 2;
+
+    /**
+     * Represents a compressed frame with exactly the same locals as the
+     * previous frame and with an empty stack.
+     */
+    int F_SAME = 3;
+
+    /**
+     * Represents a compressed frame with exactly the same locals as the
+     * previous frame and with a single value on the stack.
+     */
+    int F_SAME1 = 4;
+
+    Integer TOP = new Integer(0);
+    Integer INTEGER = new Integer(1);
+    Integer FLOAT = new Integer(2);
+    Integer DOUBLE = new Integer(3);
+    Integer LONG = new Integer(4);
+    Integer NULL = new Integer(5);
+    Integer UNINITIALIZED_THIS = new Integer(6);
+
+    // opcodes // visit method (- = idem)
+
+    int NOP = 0; // visitInsn
+    int ACONST_NULL = 1; // -
+    int ICONST_M1 = 2; // -
+    int ICONST_0 = 3; // -
+    int ICONST_1 = 4; // -
+    int ICONST_2 = 5; // -
+    int ICONST_3 = 6; // -
+    int ICONST_4 = 7; // -
+    int ICONST_5 = 8; // -
+    int LCONST_0 = 9; // -
+    int LCONST_1 = 10; // -
+    int FCONST_0 = 11; // -
+    int FCONST_1 = 12; // -
+    int FCONST_2 = 13; // -
+    int DCONST_0 = 14; // -
+    int DCONST_1 = 15; // -
+    int BIPUSH = 16; // visitIntInsn
+    int SIPUSH = 17; // -
+    int LDC = 18; // visitLdcInsn
+    // int LDC_W = 19; // -
+    // int LDC2_W = 20; // -
+    int ILOAD = 21; // visitVarInsn
+    int LLOAD = 22; // -
+    int FLOAD = 23; // -
+    int DLOAD = 24; // -
+    int ALOAD = 25; // -
+    // int ILOAD_0 = 26; // -
+    // int ILOAD_1 = 27; // -
+    // int ILOAD_2 = 28; // -
+    // int ILOAD_3 = 29; // -
+    // int LLOAD_0 = 30; // -
+    // int LLOAD_1 = 31; // -
+    // int LLOAD_2 = 32; // -
+    // int LLOAD_3 = 33; // -
+    // int FLOAD_0 = 34; // -
+    // int FLOAD_1 = 35; // -
+    // int FLOAD_2 = 36; // -
+    // int FLOAD_3 = 37; // -
+    // int DLOAD_0 = 38; // -
+    // int DLOAD_1 = 39; // -
+    // int DLOAD_2 = 40; // -
+    // int DLOAD_3 = 41; // -
+    // int ALOAD_0 = 42; // -
+    // int ALOAD_1 = 43; // -
+    // int ALOAD_2 = 44; // -
+    // int ALOAD_3 = 45; // -
+    int IALOAD = 46; // visitInsn
+    int LALOAD = 47; // -
+    int FALOAD = 48; // -
+    int DALOAD = 49; // -
+    int AALOAD = 50; // -
+    int BALOAD = 51; // -
+    int CALOAD = 52; // -
+    int SALOAD = 53; // -
+    int ISTORE = 54; // visitVarInsn
+    int LSTORE = 55; // -
+    int FSTORE = 56; // -
+    int DSTORE = 57; // -
+    int ASTORE = 58; // -
+    // int ISTORE_0 = 59; // -
+    // int ISTORE_1 = 60; // -
+    // int ISTORE_2 = 61; // -
+    // int ISTORE_3 = 62; // -
+    // int LSTORE_0 = 63; // -
+    // int LSTORE_1 = 64; // -
+    // int LSTORE_2 = 65; // -
+    // int LSTORE_3 = 66; // -
+    // int FSTORE_0 = 67; // -
+    // int FSTORE_1 = 68; // -
+    // int FSTORE_2 = 69; // -
+    // int FSTORE_3 = 70; // -
+    // int DSTORE_0 = 71; // -
+    // int DSTORE_1 = 72; // -
+    // int DSTORE_2 = 73; // -
+    // int DSTORE_3 = 74; // -
+    // int ASTORE_0 = 75; // -
+    // int ASTORE_1 = 76; // -
+    // int ASTORE_2 = 77; // -
+    // int ASTORE_3 = 78; // -
+    int IASTORE = 79; // visitInsn
+    int LASTORE = 80; // -
+    int FASTORE = 81; // -
+    int DASTORE = 82; // -
+    int AASTORE = 83; // -
+    int BASTORE = 84; // -
+    int CASTORE = 85; // -
+    int SASTORE = 86; // -
+    int POP = 87; // -
+    int POP2 = 88; // -
+    int DUP = 89; // -
+    int DUP_X1 = 90; // -
+    int DUP_X2 = 91; // -
+    int DUP2 = 92; // -
+    int DUP2_X1 = 93; // -
+    int DUP2_X2 = 94; // -
+    int SWAP = 95; // -
+    int IADD = 96; // -
+    int LADD = 97; // -
+    int FADD = 98; // -
+    int DADD = 99; // -
+    int ISUB = 100; // -
+    int LSUB = 101; // -
+    int FSUB = 102; // -
+    int DSUB = 103; // -
+    int IMUL = 104; // -
+    int LMUL = 105; // -
+    int FMUL = 106; // -
+    int DMUL = 107; // -
+    int IDIV = 108; // -
+    int LDIV = 109; // -
+    int FDIV = 110; // -
+    int DDIV = 111; // -
+    int IREM = 112; // -
+    int LREM = 113; // -
+    int FREM = 114; // -
+    int DREM = 115; // -
+    int INEG = 116; // -
+    int LNEG = 117; // -
+    int FNEG = 118; // -
+    int DNEG = 119; // -
+    int ISHL = 120; // -
+    int LSHL = 121; // -
+    int ISHR = 122; // -
+    int LSHR = 123; // -
+    int IUSHR = 124; // -
+    int LUSHR = 125; // -
+    int IAND = 126; // -
+    int LAND = 127; // -
+    int IOR = 128; // -
+    int LOR = 129; // -
+    int IXOR = 130; // -
+    int LXOR = 131; // -
+    int IINC = 132; // visitIincInsn
+    int I2L = 133; // visitInsn
+    int I2F = 134; // -
+    int I2D = 135; // -
+    int L2I = 136; // -
+    int L2F = 137; // -
+    int L2D = 138; // -
+    int F2I = 139; // -
+    int F2L = 140; // -
+    int F2D = 141; // -
+    int D2I = 142; // -
+    int D2L = 143; // -
+    int D2F = 144; // -
+    int I2B = 145; // -
+    int I2C = 146; // -
+    int I2S = 147; // -
+    int LCMP = 148; // -
+    int FCMPL = 149; // -
+    int FCMPG = 150; // -
+    int DCMPL = 151; // -
+    int DCMPG = 152; // -
+    int IFEQ = 153; // visitJumpInsn
+    int IFNE = 154; // -
+    int IFLT = 155; // -
+    int IFGE = 156; // -
+    int IFGT = 157; // -
+    int IFLE = 158; // -
+    int IF_ICMPEQ = 159; // -
+    int IF_ICMPNE = 160; // -
+    int IF_ICMPLT = 161; // -
+    int IF_ICMPGE = 162; // -
+    int IF_ICMPGT = 163; // -
+    int IF_ICMPLE = 164; // -
+    int IF_ACMPEQ = 165; // -
+    int IF_ACMPNE = 166; // -
+    int GOTO = 167; // -
+    int JSR = 168; // -
+    int RET = 169; // visitVarInsn
+    int TABLESWITCH = 170; // visiTableSwitchInsn
+    int LOOKUPSWITCH = 171; // visitLookupSwitch
+    int IRETURN = 172; // visitInsn
+    int LRETURN = 173; // -
+    int FRETURN = 174; // -
+    int DRETURN = 175; // -
+    int ARETURN = 176; // -
+    int RETURN = 177; // -
+    int GETSTATIC = 178; // visitFieldInsn
+    int PUTSTATIC = 179; // -
+    int GETFIELD = 180; // -
+    int PUTFIELD = 181; // -
+    int INVOKEVIRTUAL = 182; // visitMethodInsn
+    int INVOKESPECIAL = 183; // -
+    int INVOKESTATIC = 184; // -
+    int INVOKEINTERFACE = 185; // -
+    int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn
+    int NEW = 187; // visitTypeInsn
+    int NEWARRAY = 188; // visitIntInsn
+    int ANEWARRAY = 189; // visitTypeInsn
+    int ARRAYLENGTH = 190; // visitInsn
+    int ATHROW = 191; // -
+    int CHECKCAST = 192; // visitTypeInsn
+    int INSTANCEOF = 193; // -
+    int MONITORENTER = 194; // visitInsn
+    int MONITOREXIT = 195; // -
+    // int WIDE = 196; // NOT VISITED
+    int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn
+    int IFNULL = 198; // visitJumpInsn
+    int IFNONNULL = 199; // -
+    // int GOTO_W = 200; // -
+    // int JSR_W = 201; // -
+}
diff --git a/src/jvm/clojure/asm/Type.java b/src/jvm/clojure/asm/Type.java
new file mode 100644
index 0000000..31db08d
--- /dev/null
+++ b/src/jvm/clojure/asm/Type.java
@@ -0,0 +1,895 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/**
+ * A Java field or method type. This class can be used to make it easier to
+ * manipulate type and method descriptors.
+ *
+ * @author Eric Bruneton
+ * @author Chris Nokleberg
+ */
+public class Type {
+
+    /**
+     * The sort of the <tt>void</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int VOID = 0;
+
+    /**
+     * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int BOOLEAN = 1;
+
+    /**
+     * The sort of the <tt>char</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int CHAR = 2;
+
+    /**
+     * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int BYTE = 3;
+
+    /**
+     * The sort of the <tt>short</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int SHORT = 4;
+
+    /**
+     * The sort of the <tt>int</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int INT = 5;
+
+    /**
+     * The sort of the <tt>float</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int FLOAT = 6;
+
+    /**
+     * The sort of the <tt>long</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int LONG = 7;
+
+    /**
+     * The sort of the <tt>double</tt> type. See {@link #getSort getSort}.
+     */
+    public static final int DOUBLE = 8;
+
+    /**
+     * The sort of array reference types. See {@link #getSort getSort}.
+     */
+    public static final int ARRAY = 9;
+
+    /**
+     * The sort of object reference types. See {@link #getSort getSort}.
+     */
+    public static final int OBJECT = 10;
+
+    /**
+     * The sort of method types. See {@link #getSort getSort}.
+     */
+    public static final int METHOD = 11;
+
+    /**
+     * The <tt>void</tt> type.
+     */
+    public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24)
+            | (5 << 16) | (0 << 8) | 0, 1);
+
+    /**
+     * The <tt>boolean</tt> type.
+     */
+    public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24)
+            | (0 << 16) | (5 << 8) | 1, 1);
+
+    /**
+     * The <tt>char</tt> type.
+     */
+    public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24)
+            | (0 << 16) | (6 << 8) | 1, 1);
+
+    /**
+     * The <tt>byte</tt> type.
+     */
+    public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24)
+            | (0 << 16) | (5 << 8) | 1, 1);
+
+    /**
+     * The <tt>short</tt> type.
+     */
+    public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24)
+            | (0 << 16) | (7 << 8) | 1, 1);
+
+    /**
+     * The <tt>int</tt> type.
+     */
+    public static final Type INT_TYPE = new Type(INT, null, ('I' << 24)
+            | (0 << 16) | (0 << 8) | 1, 1);
+
+    /**
+     * The <tt>float</tt> type.
+     */
+    public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24)
+            | (2 << 16) | (2 << 8) | 1, 1);
+
+    /**
+     * The <tt>long</tt> type.
+     */
+    public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24)
+            | (1 << 16) | (1 << 8) | 2, 1);
+
+    /**
+     * The <tt>double</tt> type.
+     */
+    public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24)
+            | (3 << 16) | (3 << 8) | 2, 1);
+
+    // ------------------------------------------------------------------------
+    // Fields
+    // ------------------------------------------------------------------------
+
+    /**
+     * The sort of this Java type.
+     */
+    private final int sort;
+
+    /**
+     * A buffer containing the internal name of this Java type. This field is
+     * only used for reference types.
+     */
+    private final char[] buf;
+
+    /**
+     * The offset of the internal name of this Java type in {@link #buf buf} or,
+     * for primitive types, the size, descriptor and getOpcode offsets for this
+     * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset
+     * for IALOAD or IASTORE, byte 3 the offset for all other instructions).
+     */
+    private final int off;
+
+    /**
+     * The length of the internal name of this Java type.
+     */
+    private final int len;
+
+    // ------------------------------------------------------------------------
+    // Constructors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a reference type.
+     *
+     * @param sort
+     *            the sort of the reference type to be constructed.
+     * @param buf
+     *            a buffer containing the descriptor of the previous type.
+     * @param off
+     *            the offset of this descriptor in the previous buffer.
+     * @param len
+     *            the length of this descriptor.
+     */
+    private Type(final int sort, final char[] buf, final int off, final int len) {
+        this.sort = sort;
+        this.buf = buf;
+        this.off = off;
+        this.len = len;
+    }
+
+    /**
+     * Returns the Java type corresponding to the given type descriptor.
+     *
+     * @param typeDescriptor
+     *            a field or method type descriptor.
+     * @return the Java type corresponding to the given type descriptor.
+     */
+    public static Type getType(final String typeDescriptor) {
+        return getType(typeDescriptor.toCharArray(), 0);
+    }
+
+    /**
+     * Returns the Java type corresponding to the given internal name.
+     *
+     * @param internalName
+     *            an internal name.
+     * @return the Java type corresponding to the given internal name.
+     */
+    public static Type getObjectType(final String internalName) {
+        char[] buf = internalName.toCharArray();
+        return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length);
+    }
+
+    /**
+     * Returns the Java type corresponding to the given method descriptor.
+     * Equivalent to <code>Type.getType(methodDescriptor)</code>.
+     *
+     * @param methodDescriptor
+     *            a method descriptor.
+     * @return the Java type corresponding to the given method descriptor.
+     */
+    public static Type getMethodType(final String methodDescriptor) {
+        return getType(methodDescriptor.toCharArray(), 0);
+    }
+
+    /**
+     * Returns the Java method type corresponding to the given argument and
+     * return types.
+     *
+     * @param returnType
+     *            the return type of the method.
+     * @param argumentTypes
+     *            the argument types of the method.
+     * @return the Java type corresponding to the given argument and return
+     *         types.
+     */
+    public static Type getMethodType(final Type returnType,
+            final Type... argumentTypes) {
+        return getType(getMethodDescriptor(returnType, argumentTypes));
+    }
+
+    /**
+     * Returns the Java type corresponding to the given class.
+     *
+     * @param c
+     *            a class.
+     * @return the Java type corresponding to the given class.
+     */
+    public static Type getType(final Class<?> c) {
+        if (c.isPrimitive()) {
+            if (c == Integer.TYPE) {
+                return INT_TYPE;
+            } else if (c == Void.TYPE) {
+                return VOID_TYPE;
+            } else if (c == Boolean.TYPE) {
+                return BOOLEAN_TYPE;
+            } else if (c == Byte.TYPE) {
+                return BYTE_TYPE;
+            } else if (c == Character.TYPE) {
+                return CHAR_TYPE;
+            } else if (c == Short.TYPE) {
+                return SHORT_TYPE;
+            } else if (c == Double.TYPE) {
+                return DOUBLE_TYPE;
+            } else if (c == Float.TYPE) {
+                return FLOAT_TYPE;
+            } else /* if (c == Long.TYPE) */{
+                return LONG_TYPE;
+            }
+        } else {
+            return getType(getDescriptor(c));
+        }
+    }
+
+    /**
+     * Returns the Java method type corresponding to the given constructor.
+     *
+     * @param c
+     *            a {@link Constructor Constructor} object.
+     * @return the Java method type corresponding to the given constructor.
+     */
+    public static Type getType(final Constructor<?> c) {
+        return getType(getConstructorDescriptor(c));
+    }
+
+    /**
+     * Returns the Java method type corresponding to the given method.
+     *
+     * @param m
+     *            a {@link Method Method} object.
+     * @return the Java method type corresponding to the given method.
+     */
+    public static Type getType(final Method m) {
+        return getType(getMethodDescriptor(m));
+    }
+
+    /**
+     * Returns the Java types corresponding to the argument types of the given
+     * method descriptor.
+     *
+     * @param methodDescriptor
+     *            a method descriptor.
+     * @return the Java types corresponding to the argument types of the given
+     *         method descriptor.
+     */
+    public static Type[] getArgumentTypes(final String methodDescriptor) {
+        char[] buf = methodDescriptor.toCharArray();
+        int off = 1;
+        int size = 0;
+        while (true) {
+            char car = buf[off++];
+            if (car == ')') {
+                break;
+            } else if (car == 'L') {
+                while (buf[off++] != ';') {
+                }
+                ++size;
+            } else if (car != '[') {
+                ++size;
+            }
+        }
+        Type[] args = new Type[size];
+        off = 1;
+        size = 0;
+        while (buf[off] != ')') {
+            args[size] = getType(buf, off);
+            off += args[size].len + (args[size].sort == OBJECT ? 2 : 0);
+            size += 1;
+        }
+        return args;
+    }
+
+    /**
+     * Returns the Java types corresponding to the argument types of the given
+     * method.
+     *
+     * @param method
+     *            a method.
+     * @return the Java types corresponding to the argument types of the given
+     *         method.
+     */
+    public static Type[] getArgumentTypes(final Method method) {
+        Class<?>[] classes = method.getParameterTypes();
+        Type[] types = new Type[classes.length];
+        for (int i = classes.length - 1; i >= 0; --i) {
+            types[i] = getType(classes[i]);
+        }
+        return types;
+    }
+
+    /**
+     * Returns the Java type corresponding to the return type of the given
+     * method descriptor.
+     *
+     * @param methodDescriptor
+     *            a method descriptor.
+     * @return the Java type corresponding to the return type of the given
+     *         method descriptor.
+     */
+    public static Type getReturnType(final String methodDescriptor) {
+        char[] buf = methodDescriptor.toCharArray();
+        return getType(buf, methodDescriptor.indexOf(')') + 1);
+    }
+
+    /**
+     * Returns the Java type corresponding to the return type of the given
+     * method.
+     *
+     * @param method
+     *            a method.
+     * @return the Java type corresponding to the return type of the given
+     *         method.
+     */
+    public static Type getReturnType(final Method method) {
+        return getType(method.getReturnType());
+    }
+
+    /**
+     * Computes the size of the arguments and of the return value of a method.
+     *
+     * @param desc
+     *            the descriptor of a method.
+     * @return the size of the arguments of the method (plus one for the
+     *         implicit this argument), argSize, and the size of its return
+     *         value, retSize, packed into a single int i =
+     *         <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to
+     *         <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
+     */
+    public static int getArgumentsAndReturnSizes(final String desc) {
+        int n = 1;
+        int c = 1;
+        while (true) {
+            char car = desc.charAt(c++);
+            if (car == ')') {
+                car = desc.charAt(c);
+                return n << 2
+                        | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));
+            } else if (car == 'L') {
+                while (desc.charAt(c++) != ';') {
+                }
+                n += 1;
+            } else if (car == '[') {
+                while ((car = desc.charAt(c)) == '[') {
+                    ++c;
+                }
+                if (car == 'D' || car == 'J') {
+                    n -= 1;
+                }
+            } else if (car == 'D' || car == 'J') {
+                n += 2;
+            } else {
+                n += 1;
+            }
+        }
+    }
+
+    /**
+     * Returns the Java type corresponding to the given type descriptor. For
+     * method descriptors, buf is supposed to contain nothing more than the
+     * descriptor itself.
+     *
+     * @param buf
+     *            a buffer containing a type descriptor.
+     * @param off
+     *            the offset of this descriptor in the previous buffer.
+     * @return the Java type corresponding to the given type descriptor.
+     */
+    private static Type getType(final char[] buf, final int off) {
+        int len;
+        switch (buf[off]) {
+        case 'V':
+            return VOID_TYPE;
+        case 'Z':
+            return BOOLEAN_TYPE;
+        case 'C':
+            return CHAR_TYPE;
+        case 'B':
+            return BYTE_TYPE;
+        case 'S':
+            return SHORT_TYPE;
+        case 'I':
+            return INT_TYPE;
+        case 'F':
+            return FLOAT_TYPE;
+        case 'J':
+            return LONG_TYPE;
+        case 'D':
+            return DOUBLE_TYPE;
+        case '[':
+            len = 1;
+            while (buf[off + len] == '[') {
+                ++len;
+            }
+            if (buf[off + len] == 'L') {
+                ++len;
+                while (buf[off + len] != ';') {
+                    ++len;
+                }
+            }
+            return new Type(ARRAY, buf, off, len + 1);
+        case 'L':
+            len = 1;
+            while (buf[off + len] != ';') {
+                ++len;
+            }
+            return new Type(OBJECT, buf, off + 1, len - 1);
+            // case '(':
+        default:
+            return new Type(METHOD, buf, off, buf.length - off);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Accessors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the sort of this Java type.
+     *
+     * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR},
+     *         {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT},
+     *         {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE},
+     *         {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD
+     *         METHOD}.
+     */
+    public int getSort() {
+        return sort;
+    }
+
+    /**
+     * Returns the number of dimensions of this array type. This method should
+     * only be used for an array type.
+     *
+     * @return the number of dimensions of this array type.
+     */
+    public int getDimensions() {
+        int i = 1;
+        while (buf[off + i] == '[') {
+            ++i;
+        }
+        return i;
+    }
+
+    /**
+     * Returns the type of the elements of this array type. This method should
+     * only be used for an array type.
+     *
+     * @return Returns the type of the elements of this array type.
+     */
+    public Type getElementType() {
+        return getType(buf, off + getDimensions());
+    }
+
+    /**
+     * Returns the binary name of the class corresponding to this type. This
+     * method must not be used on method types.
+     *
+     * @return the binary name of the class corresponding to this type.
+     */
+    public String getClassName() {
+        switch (sort) {
+        case VOID:
+            return "void";
+        case BOOLEAN:
+            return "boolean";
+        case CHAR:
+            return "char";
+        case BYTE:
+            return "byte";
+        case SHORT:
+            return "short";
+        case INT:
+            return "int";
+        case FLOAT:
+            return "float";
+        case LONG:
+            return "long";
+        case DOUBLE:
+            return "double";
+        case ARRAY:
+            StringBuffer b = new StringBuffer(getElementType().getClassName());
+            for (int i = getDimensions(); i > 0; --i) {
+                b.append("[]");
+            }
+            return b.toString();
+        case OBJECT:
+            return new String(buf, off, len).replace('/', '.');
+        default:
+            return null;
+        }
+    }
+
+    /**
+     * Returns the internal name of the class corresponding to this object or
+     * array type. The internal name of a class is its fully qualified name (as
+     * returned by Class.getName(), where '.' are replaced by '/'. This method
+     * should only be used for an object or array type.
+     *
+     * @return the internal name of the class corresponding to this object type.
+     */
+    public String getInternalName() {
+        return new String(buf, off, len);
+    }
+
+    /**
+     * Returns the argument types of methods of this type. This method should
+     * only be used for method types.
+     *
+     * @return the argument types of methods of this type.
+     */
+    public Type[] getArgumentTypes() {
+        return getArgumentTypes(getDescriptor());
+    }
+
+    /**
+     * Returns the return type of methods of this type. This method should only
+     * be used for method types.
+     *
+     * @return the return type of methods of this type.
+     */
+    public Type getReturnType() {
+        return getReturnType(getDescriptor());
+    }
+
+    /**
+     * Returns the size of the arguments and of the return value of methods of
+     * this type. This method should only be used for method types.
+     *
+     * @return the size of the arguments (plus one for the implicit this
+     *         argument), argSize, and the size of the return value, retSize,
+     *         packed into a single int i = <tt>(argSize << 2) | retSize</tt>
+     *         (argSize is therefore equal to <tt>i >> 2</tt>, and retSize to
+     *         <tt>i & 0x03</tt>).
+     */
+    public int getArgumentsAndReturnSizes() {
+        return getArgumentsAndReturnSizes(getDescriptor());
+    }
+
+    // ------------------------------------------------------------------------
+    // Conversion to type descriptors
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the descriptor corresponding to this Java type.
+     *
+     * @return the descriptor corresponding to this Java type.
+     */
+    public String getDescriptor() {
+        StringBuffer buf = new StringBuffer();
+        getDescriptor(buf);
+        return buf.toString();
+    }
+
+    /**
+     * Returns the descriptor corresponding to the given argument and return
+     * types.
+     *
+     * @param returnType
+     *            the return type of the method.
+     * @param argumentTypes
+     *            the argument types of the method.
+     * @return the descriptor corresponding to the given argument and return
+     *         types.
+     */
+    public static String getMethodDescriptor(final Type returnType,
+            final Type... argumentTypes) {
+        StringBuffer buf = new StringBuffer();
+        buf.append('(');
+        for (int i = 0; i < argumentTypes.length; ++i) {
+            argumentTypes[i].getDescriptor(buf);
+        }
+        buf.append(')');
+        returnType.getDescriptor(buf);
+        return buf.toString();
+    }
+
+    /**
+     * Appends the descriptor corresponding to this Java type to the given
+     * string buffer.
+     *
+     * @param buf
+     *            the string buffer to which the descriptor must be appended.
+     */
+    private void getDescriptor(final StringBuffer buf) {
+        if (this.buf == null) {
+            // descriptor is in byte 3 of 'off' for primitive types (buf ==
+            // null)
+            buf.append((char) ((off & 0xFF000000) >>> 24));
+        } else if (sort == OBJECT) {
+            buf.append('L');
+            buf.append(this.buf, off, len);
+            buf.append(';');
+        } else { // sort == ARRAY || sort == METHOD
+            buf.append(this.buf, off, len);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Direct conversion from classes to type descriptors,
+    // without intermediate Type objects
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the internal name of the given class. The internal name of a
+     * class is its fully qualified name, as returned by Class.getName(), where
+     * '.' are replaced by '/'.
+     *
+     * @param c
+     *            an object or array class.
+     * @return the internal name of the given class.
+     */
+    public static String getInternalName(final Class<?> c) {
+        return c.getName().replace('.', '/');
+    }
+
+    /**
+     * Returns the descriptor corresponding to the given Java type.
+     *
+     * @param c
+     *            an object class, a primitive class or an array class.
+     * @return the descriptor corresponding to the given class.
+     */
+    public static String getDescriptor(final Class<?> c) {
+        StringBuffer buf = new StringBuffer();
+        getDescriptor(buf, c);
+        return buf.toString();
+    }
+
+    /**
+     * Returns the descriptor corresponding to the given constructor.
+     *
+     * @param c
+     *            a {@link Constructor Constructor} object.
+     * @return the descriptor of the given constructor.
+     */
+    public static String getConstructorDescriptor(final Constructor<?> c) {
+        Class<?>[] parameters = c.getParameterTypes();
+        StringBuffer buf = new StringBuffer();
+        buf.append('(');
+        for (int i = 0; i < parameters.length; ++i) {
+            getDescriptor(buf, parameters[i]);
+        }
+        return buf.append(")V").toString();
+    }
+
+    /**
+     * Returns the descriptor corresponding to the given method.
+     *
+     * @param m
+     *            a {@link Method Method} object.
+     * @return the descriptor of the given method.
+     */
+    public static String getMethodDescriptor(final Method m) {
+        Class<?>[] parameters = m.getParameterTypes();
+        StringBuffer buf = new StringBuffer();
+        buf.append('(');
+        for (int i = 0; i < parameters.length; ++i) {
+            getDescriptor(buf, parameters[i]);
+        }
+        buf.append(')');
+        getDescriptor(buf, m.getReturnType());
+        return buf.toString();
+    }
+
+    /**
+     * Appends the descriptor of the given class to the given string buffer.
+     *
+     * @param buf
+     *            the string buffer to which the descriptor must be appended.
+     * @param c
+     *            the class whose descriptor must be computed.
+     */
+    private static void getDescriptor(final StringBuffer buf, final Class<?> c) {
+        Class<?> d = c;
+        while (true) {
+            if (d.isPrimitive()) {
+                char car;
+                if (d == Integer.TYPE) {
+                    car = 'I';
+                } else if (d == Void.TYPE) {
+                    car = 'V';
+                } else if (d == Boolean.TYPE) {
+                    car = 'Z';
+                } else if (d == Byte.TYPE) {
+                    car = 'B';
+                } else if (d == Character.TYPE) {
+                    car = 'C';
+                } else if (d == Short.TYPE) {
+                    car = 'S';
+                } else if (d == Double.TYPE) {
+                    car = 'D';
+                } else if (d == Float.TYPE) {
+                    car = 'F';
+                } else /* if (d == Long.TYPE) */{
+                    car = 'J';
+                }
+                buf.append(car);
+                return;
+            } else if (d.isArray()) {
+                buf.append('[');
+                d = d.getComponentType();
+            } else {
+                buf.append('L');
+                String name = d.getName();
+                int len = name.length();
+                for (int i = 0; i < len; ++i) {
+                    char car = name.charAt(i);
+                    buf.append(car == '.' ? '/' : car);
+                }
+                buf.append(';');
+                return;
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Corresponding size and opcodes
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the size of values of this type. This method must not be used for
+     * method types.
+     *
+     * @return the size of values of this type, i.e., 2 for <tt>long</tt> and
+     *         <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise.
+     */
+    public int getSize() {
+        // the size is in byte 0 of 'off' for primitive types (buf == null)
+        return buf == null ? (off & 0xFF) : 1;
+    }
+
+    /**
+     * Returns a JVM instruction opcode adapted to this Java type. This method
+     * must not be used for method types.
+     *
+     * @param opcode
+     *            a JVM instruction opcode. This opcode must be one of ILOAD,
+     *            ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG,
+     *            ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN.
+     * @return an opcode that is similar to the given opcode, but adapted to
+     *         this Java type. For example, if this type is <tt>float</tt> and
+     *         <tt>opcode</tt> is IRETURN, this method returns FRETURN.
+     */
+    public int getOpcode(final int opcode) {
+        if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) {
+            // the offset for IALOAD or IASTORE is in byte 1 of 'off' for
+            // primitive types (buf == null)
+            return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4);
+        } else {
+            // the offset for other instructions is in byte 2 of 'off' for
+            // primitive types (buf == null)
+            return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Equals, hashCode and toString
+    // ------------------------------------------------------------------------
+
+    /**
+     * Tests if the given object is equal to this type.
+     *
+     * @param o
+     *            the object to be compared to this type.
+     * @return <tt>true</tt> if the given object is equal to this type.
+     */
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof Type)) {
+            return false;
+        }
+        Type t = (Type) o;
+        if (sort != t.sort) {
+            return false;
+        }
+        if (sort >= ARRAY) {
+            if (len != t.len) {
+                return false;
+            }
+            for (int i = off, j = t.off, end = i + len; i < end; i++, j++) {
+                if (buf[i] != t.buf[j]) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns a hash code value for this type.
+     *
+     * @return a hash code value for this type.
+     */
+    @Override
+    public int hashCode() {
+        int hc = 13 * sort;
+        if (sort >= ARRAY) {
+            for (int i = off, end = i + len; i < end; i++) {
+                hc = 17 * (hc + buf[i]);
+            }
+        }
+        return hc;
+    }
+
+    /**
+     * Returns a string representation of this type.
+     *
+     * @return the descriptor of this type.
+     */
+    @Override
+    public String toString() {
+        return getDescriptor();
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/AdviceAdapter.java b/src/jvm/clojure/asm/commons/AdviceAdapter.java
new file mode 100644
index 0000000..8ec38d2
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/AdviceAdapter.java
@@ -0,0 +1,625 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import clojure.asm.Handle;
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link clojure.asm.MethodVisitor} to insert before, after and around
+ * advices in methods and constructors.
+ * <p>
+ * The behavior for constructors is like this:
+ * <ol>
+ *
+ * <li>as long as the INVOKESPECIAL for the object initialization has not been
+ * reached, every bytecode instruction is dispatched in the ctor code visitor</li>
+ *
+ * <li>when this one is reached, it is only added in the ctor code visitor and a
+ * JP invoke is added</li>
+ *
+ * <li>after that, only the other code visitor receives the instructions</li>
+ *
+ * </ol>
+ *
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes {
+
+    private static final Object THIS = new Object();
+
+    private static final Object OTHER = new Object();
+
+    protected int methodAccess;
+
+    protected String methodDesc;
+
+    private boolean constructor;
+
+    private boolean superInitialized;
+
+    private List<Object> stackFrame;
+
+    private Map<Label, List<Object>> branches;
+
+    /**
+     * Creates a new {@link AdviceAdapter}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     * @param access
+     *            the method's access flags (see {@link Opcodes}).
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     */
+    protected AdviceAdapter(final int api, final MethodVisitor mv,
+            final int access, final String name, final String desc) {
+        super(api, mv, access, name, desc);
+        methodAccess = access;
+        methodDesc = desc;
+        constructor = "<init>".equals(name);
+    }
+
+    @Override
+    public void visitCode() {
+        mv.visitCode();
+        if (constructor) {
+            stackFrame = new ArrayList<Object>();
+            branches = new HashMap<Label, List<Object>>();
+        } else {
+            superInitialized = true;
+            onMethodEnter();
+        }
+    }
+
+    @Override
+    public void visitLabel(final Label label) {
+        mv.visitLabel(label);
+        if (constructor && branches != null) {
+            List<Object> frame = branches.get(label);
+            if (frame != null) {
+                stackFrame = frame;
+                branches.remove(label);
+            }
+        }
+    }
+
+    @Override
+    public void visitInsn(final int opcode) {
+        if (constructor) {
+            int s;
+            switch (opcode) {
+            case RETURN: // empty stack
+                onMethodExit(opcode);
+                break;
+            case IRETURN: // 1 before n/a after
+            case FRETURN: // 1 before n/a after
+            case ARETURN: // 1 before n/a after
+            case ATHROW: // 1 before n/a after
+                popValue();
+                onMethodExit(opcode);
+                break;
+            case LRETURN: // 2 before n/a after
+            case DRETURN: // 2 before n/a after
+                popValue();
+                popValue();
+                onMethodExit(opcode);
+                break;
+            case NOP:
+            case LALOAD: // remove 2 add 2
+            case DALOAD: // remove 2 add 2
+            case LNEG:
+            case DNEG:
+            case FNEG:
+            case INEG:
+            case L2D:
+            case D2L:
+            case F2I:
+            case I2B:
+            case I2C:
+            case I2S:
+            case I2F:
+            case ARRAYLENGTH:
+                break;
+            case ACONST_NULL:
+            case ICONST_M1:
+            case ICONST_0:
+            case ICONST_1:
+            case ICONST_2:
+            case ICONST_3:
+            case ICONST_4:
+            case ICONST_5:
+            case FCONST_0:
+            case FCONST_1:
+            case FCONST_2:
+            case F2L: // 1 before 2 after
+            case F2D:
+            case I2L:
+            case I2D:
+                pushValue(OTHER);
+                break;
+            case LCONST_0:
+            case LCONST_1:
+            case DCONST_0:
+            case DCONST_1:
+                pushValue(OTHER);
+                pushValue(OTHER);
+                break;
+            case IALOAD: // remove 2 add 1
+            case FALOAD: // remove 2 add 1
+            case AALOAD: // remove 2 add 1
+            case BALOAD: // remove 2 add 1
+            case CALOAD: // remove 2 add 1
+            case SALOAD: // remove 2 add 1
+            case POP:
+            case IADD:
+            case FADD:
+            case ISUB:
+            case LSHL: // 3 before 2 after
+            case LSHR: // 3 before 2 after
+            case LUSHR: // 3 before 2 after
+            case L2I: // 2 before 1 after
+            case L2F: // 2 before 1 after
+            case D2I: // 2 before 1 after
+            case D2F: // 2 before 1 after
+            case FSUB:
+            case FMUL:
+            case FDIV:
+            case FREM:
+            case FCMPL: // 2 before 1 after
+            case FCMPG: // 2 before 1 after
+            case IMUL:
+            case IDIV:
+            case IREM:
+            case ISHL:
+            case ISHR:
+            case IUSHR:
+            case IAND:
+            case IOR:
+            case IXOR:
+            case MONITORENTER:
+            case MONITOREXIT:
+                popValue();
+                break;
+            case POP2:
+            case LSUB:
+            case LMUL:
+            case LDIV:
+            case LREM:
+            case LADD:
+            case LAND:
+            case LOR:
+            case LXOR:
+            case DADD:
+            case DMUL:
+            case DSUB:
+            case DDIV:
+            case DREM:
+                popValue();
+                popValue();
+                break;
+            case IASTORE:
+            case FASTORE:
+            case AASTORE:
+            case BASTORE:
+            case CASTORE:
+            case SASTORE:
+            case LCMP: // 4 before 1 after
+            case DCMPL:
+            case DCMPG:
+                popValue();
+                popValue();
+                popValue();
+                break;
+            case LASTORE:
+            case DASTORE:
+                popValue();
+                popValue();
+                popValue();
+                popValue();
+                break;
+            case DUP:
+                pushValue(peekValue());
+                break;
+            case DUP_X1:
+                s = stackFrame.size();
+                stackFrame.add(s - 2, stackFrame.get(s - 1));
+                break;
+            case DUP_X2:
+                s = stackFrame.size();
+                stackFrame.add(s - 3, stackFrame.get(s - 1));
+                break;
+            case DUP2:
+                s = stackFrame.size();
+                stackFrame.add(s - 2, stackFrame.get(s - 1));
+                stackFrame.add(s - 2, stackFrame.get(s - 1));
+                break;
+            case DUP2_X1:
+                s = stackFrame.size();
+                stackFrame.add(s - 3, stackFrame.get(s - 1));
+                stackFrame.add(s - 3, stackFrame.get(s - 1));
+                break;
+            case DUP2_X2:
+                s = stackFrame.size();
+                stackFrame.add(s - 4, stackFrame.get(s - 1));
+                stackFrame.add(s - 4, stackFrame.get(s - 1));
+                break;
+            case SWAP:
+                s = stackFrame.size();
+                stackFrame.add(s - 2, stackFrame.get(s - 1));
+                stackFrame.remove(s);
+                break;
+            }
+        } else {
+            switch (opcode) {
+            case RETURN:
+            case IRETURN:
+            case FRETURN:
+            case ARETURN:
+            case LRETURN:
+            case DRETURN:
+            case ATHROW:
+                onMethodExit(opcode);
+                break;
+            }
+        }
+        mv.visitInsn(opcode);
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        super.visitVarInsn(opcode, var);
+        if (constructor) {
+            switch (opcode) {
+            case ILOAD:
+            case FLOAD:
+                pushValue(OTHER);
+                break;
+            case LLOAD:
+            case DLOAD:
+                pushValue(OTHER);
+                pushValue(OTHER);
+                break;
+            case ALOAD:
+                pushValue(var == 0 ? THIS : OTHER);
+                break;
+            case ASTORE:
+            case ISTORE:
+            case FSTORE:
+                popValue();
+                break;
+            case LSTORE:
+            case DSTORE:
+                popValue();
+                popValue();
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void visitFieldInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        mv.visitFieldInsn(opcode, owner, name, desc);
+        if (constructor) {
+            char c = desc.charAt(0);
+            boolean longOrDouble = c == 'J' || c == 'D';
+            switch (opcode) {
+            case GETSTATIC:
+                pushValue(OTHER);
+                if (longOrDouble) {
+                    pushValue(OTHER);
+                }
+                break;
+            case PUTSTATIC:
+                popValue();
+                if (longOrDouble) {
+                    popValue();
+                }
+                break;
+            case PUTFIELD:
+                popValue();
+                if (longOrDouble) {
+                    popValue();
+                    popValue();
+                }
+                break;
+            // case GETFIELD:
+            default:
+                if (longOrDouble) {
+                    pushValue(OTHER);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void visitIntInsn(final int opcode, final int operand) {
+        mv.visitIntInsn(opcode, operand);
+        if (constructor && opcode != NEWARRAY) {
+            pushValue(OTHER);
+        }
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        mv.visitLdcInsn(cst);
+        if (constructor) {
+            pushValue(OTHER);
+            if (cst instanceof Double || cst instanceof Long) {
+                pushValue(OTHER);
+            }
+        }
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        mv.visitMultiANewArrayInsn(desc, dims);
+        if (constructor) {
+            for (int i = 0; i < dims; i++) {
+                popValue();
+            }
+            pushValue(OTHER);
+        }
+    }
+
+    @Override
+    public void visitTypeInsn(final int opcode, final String type) {
+        mv.visitTypeInsn(opcode, type);
+        // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
+        if (constructor && opcode == NEW) {
+            pushValue(OTHER);
+        }
+    }
+
+    @Override
+    public void visitMethodInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        mv.visitMethodInsn(opcode, owner, name, desc);
+        if (constructor) {
+            Type[] types = Type.getArgumentTypes(desc);
+            for (int i = 0; i < types.length; i++) {
+                popValue();
+                if (types[i].getSize() == 2) {
+                    popValue();
+                }
+            }
+            switch (opcode) {
+            // case INVOKESTATIC:
+            // break;
+            case INVOKEINTERFACE:
+            case INVOKEVIRTUAL:
+                popValue(); // objectref
+                break;
+            case INVOKESPECIAL:
+                Object type = popValue(); // objectref
+                if (type == THIS && !superInitialized) {
+                    onMethodEnter();
+                    superInitialized = true;
+                    // once super has been initialized it is no longer
+                    // necessary to keep track of stack state
+                    constructor = false;
+                }
+                break;
+            }
+
+            Type returnType = Type.getReturnType(desc);
+            if (returnType != Type.VOID_TYPE) {
+                pushValue(OTHER);
+                if (returnType.getSize() == 2) {
+                    pushValue(OTHER);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
+        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+        if (constructor) {
+            Type[] types = Type.getArgumentTypes(desc);
+            for (int i = 0; i < types.length; i++) {
+                popValue();
+                if (types[i].getSize() == 2) {
+                    popValue();
+                }
+            }
+
+            Type returnType = Type.getReturnType(desc);
+            if (returnType != Type.VOID_TYPE) {
+                pushValue(OTHER);
+                if (returnType.getSize() == 2) {
+                    pushValue(OTHER);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void visitJumpInsn(final int opcode, final Label label) {
+        mv.visitJumpInsn(opcode, label);
+        if (constructor) {
+            switch (opcode) {
+            case IFEQ:
+            case IFNE:
+            case IFLT:
+            case IFGE:
+            case IFGT:
+            case IFLE:
+            case IFNULL:
+            case IFNONNULL:
+                popValue();
+                break;
+            case IF_ICMPEQ:
+            case IF_ICMPNE:
+            case IF_ICMPLT:
+            case IF_ICMPGE:
+            case IF_ICMPGT:
+            case IF_ICMPLE:
+            case IF_ACMPEQ:
+            case IF_ACMPNE:
+                popValue();
+                popValue();
+                break;
+            case JSR:
+                pushValue(OTHER);
+                break;
+            }
+            addBranch(label);
+        }
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
+            final Label[] labels) {
+        mv.visitLookupSwitchInsn(dflt, keys, labels);
+        if (constructor) {
+            popValue();
+            addBranches(dflt, labels);
+        }
+    }
+
+    @Override
+    public void visitTableSwitchInsn(final int min, final int max,
+            final Label dflt, final Label... labels) {
+        mv.visitTableSwitchInsn(min, max, dflt, labels);
+        if (constructor) {
+            popValue();
+            addBranches(dflt, labels);
+        }
+    }
+
+    @Override
+    public void visitTryCatchBlock(Label start, Label end, Label handler,
+            String type) {
+        super.visitTryCatchBlock(start, end, handler, type);
+        if (constructor && !branches.containsKey(handler)) {
+            List<Object> stackFrame = new ArrayList<Object>();
+            stackFrame.add(OTHER);
+            branches.put(handler, stackFrame);
+        }
+    }
+
+    private void addBranches(final Label dflt, final Label[] labels) {
+        addBranch(dflt);
+        for (int i = 0; i < labels.length; i++) {
+            addBranch(labels[i]);
+        }
+    }
+
+    private void addBranch(final Label label) {
+        if (branches.containsKey(label)) {
+            return;
+        }
+        branches.put(label, new ArrayList<Object>(stackFrame));
+    }
+
+    private Object popValue() {
+        return stackFrame.remove(stackFrame.size() - 1);
+    }
+
+    private Object peekValue() {
+        return stackFrame.get(stackFrame.size() - 1);
+    }
+
+    private void pushValue(final Object o) {
+        stackFrame.add(o);
+    }
+
+    /**
+     * Called at the beginning of the method or after super class class call in
+     * the constructor. <br>
+     * <br>
+     *
+     * <i>Custom code can use or change all the local variables, but should not
+     * change state of the stack.</i>
+     */
+    protected void onMethodEnter() {
+    }
+
+    /**
+     * Called before explicit exit from the method using either return or throw.
+     * Top element on the stack contains the return value or exception instance.
+     * For example:
+     *
+     * <pre>
+     *   public void onMethodExit(int opcode) {
+     *     if(opcode==RETURN) {
+     *         visitInsn(ACONST_NULL);
+     *     } else if(opcode==ARETURN || opcode==ATHROW) {
+     *         dup();
+     *     } else {
+     *         if(opcode==LRETURN || opcode==DRETURN) {
+     *             dup2();
+     *         } else {
+     *             dup();
+     *         }
+     *         box(Type.getReturnType(this.methodDesc));
+     *     }
+     *     visitIntInsn(SIPUSH, opcode);
+     *     visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V");
+     *   }
+     *
+     *   // an actual call back method
+     *   public static void onExit(Object param, int opcode) {
+     *     ...
+     * </pre>
+     *
+     * <br>
+     * <br>
+     *
+     * <i>Custom code can use or change all the local variables, but should not
+     * change state of the stack.</i>
+     *
+     * @param opcode
+     *            one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN
+     *            or ATHROW
+     *
+     */
+    protected void onMethodExit(int opcode) {
+    }
+
+    // TODO onException, onMethodCall
+}
diff --git a/src/jvm/clojure/asm/commons/AnalyzerAdapter.java b/src/jvm/clojure/asm/commons/AnalyzerAdapter.java
new file mode 100644
index 0000000..429d8db
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/AnalyzerAdapter.java
@@ -0,0 +1,920 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import clojure.asm.Handle;
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link MethodVisitor} that keeps track of stack map frame changes between
+ * {@link #visitFrame(int, int, Object[], int, Object[]) visitFrame} calls. This
+ * adapter must be used with the
+ * {@link clojure.asm.ClassReader#EXPAND_FRAMES} option. Each
+ * visit<i>X</i> instruction delegates to the next visitor in the chain, if any,
+ * and then simulates the effect of this instruction on the stack map frame,
+ * represented by {@link #locals} and {@link #stack}. The next visitor in the
+ * chain can get the state of the stack map frame <i>before</i> each instruction
+ * by reading the value of these fields in its visit<i>X</i> methods (this
+ * requires a reference to the AnalyzerAdapter that is before it in the chain).
+ * If this adapter is used with a class that does not contain stack map table
+ * attributes (i.e., pre Java 6 classes) then this adapter may not be able to
+ * compute the stack map frame for each instruction. In this case no exception
+ * is thrown but the {@link #locals} and {@link #stack} fields will be null for
+ * these instructions.
+ *
+ * @author Eric Bruneton
+ */
+public class AnalyzerAdapter extends MethodVisitor {
+
+    /**
+     * <code>List</code> of the local variable slots for current execution
+     * frame. Primitive types are represented by {@link Opcodes#TOP},
+     * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+     * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+     * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by
+     * two elements, the second one being TOP). Reference types are represented
+     * by String objects (representing internal names), and uninitialized types
+     * by Label objects (this label designates the NEW instruction that created
+     * this uninitialized value). This field is <tt>null</tt> for unreachable
+     * instructions.
+     */
+    public List<Object> locals;
+
+    /**
+     * <code>List</code> of the operand stack slots for current execution frame.
+     * Primitive types are represented by {@link Opcodes#TOP},
+     * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG},
+     * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or
+     * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by
+     * two elements, the second one being TOP). Reference types are represented
+     * by String objects (representing internal names), and uninitialized types
+     * by Label objects (this label designates the NEW instruction that created
+     * this uninitialized value). This field is <tt>null</tt> for unreachable
+     * instructions.
+     */
+    public List<Object> stack;
+
+    /**
+     * The labels that designate the next instruction to be visited. May be
+     * <tt>null</tt>.
+     */
+    private List<Label> labels;
+
+    /**
+     * Information about uninitialized types in the current execution frame.
+     * This map associates internal names to Label objects. Each label
+     * designates a NEW instruction that created the currently uninitialized
+     * types, and the associated internal name represents the NEW operand, i.e.
+     * the final, initialized type value.
+     */
+    public Map<Object, Object> uninitializedTypes;
+
+    /**
+     * The maximum stack size of this method.
+     */
+    private int maxStack;
+
+    /**
+     * The maximum number of local variables of this method.
+     */
+    private int maxLocals;
+
+    /**
+     * The owner's class name.
+     */
+    private String owner;
+
+    /**
+     * Creates a new {@link AnalyzerAdapter}. <i>Subclasses must not use this
+     * constructor</i>. Instead, they must use the
+     * {@link #AnalyzerAdapter(int, String, int, String, String, MethodVisitor)}
+     * version.
+     *
+     * @param owner
+     *            the owner's class name.
+     * @param access
+     *            the method's access flags (see {@link Opcodes}).
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     * @param mv
+     *            the method visitor to which this adapter delegates calls. May
+     *            be <tt>null</tt>.
+     */
+    public AnalyzerAdapter(final String owner, final int access,
+            final String name, final String desc, final MethodVisitor mv) {
+        this(Opcodes.ASM4, owner, access, name, desc, mv);
+    }
+
+    /**
+     * Creates a new {@link AnalyzerAdapter}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param owner
+     *            the owner's class name.
+     * @param access
+     *            the method's access flags (see {@link Opcodes}).
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     * @param mv
+     *            the method visitor to which this adapter delegates calls. May
+     *            be <tt>null</tt>.
+     */
+    protected AnalyzerAdapter(final int api, final String owner,
+            final int access, final String name, final String desc,
+            final MethodVisitor mv) {
+        super(api, mv);
+        this.owner = owner;
+        locals = new ArrayList<Object>();
+        stack = new ArrayList<Object>();
+        uninitializedTypes = new HashMap<Object, Object>();
+
+        if ((access & Opcodes.ACC_STATIC) == 0) {
+            if ("<init>".equals(name)) {
+                locals.add(Opcodes.UNINITIALIZED_THIS);
+            } else {
+                locals.add(owner);
+            }
+        }
+        Type[] types = Type.getArgumentTypes(desc);
+        for (int i = 0; i < types.length; ++i) {
+            Type type = types[i];
+            switch (type.getSort()) {
+            case Type.BOOLEAN:
+            case Type.CHAR:
+            case Type.BYTE:
+            case Type.SHORT:
+            case Type.INT:
+                locals.add(Opcodes.INTEGER);
+                break;
+            case Type.FLOAT:
+                locals.add(Opcodes.FLOAT);
+                break;
+            case Type.LONG:
+                locals.add(Opcodes.LONG);
+                locals.add(Opcodes.TOP);
+                break;
+            case Type.DOUBLE:
+                locals.add(Opcodes.DOUBLE);
+                locals.add(Opcodes.TOP);
+                break;
+            case Type.ARRAY:
+                locals.add(types[i].getDescriptor());
+                break;
+            // case Type.OBJECT:
+            default:
+                locals.add(types[i].getInternalName());
+            }
+        }
+    }
+
+    @Override
+    public void visitFrame(final int type, final int nLocal,
+            final Object[] local, final int nStack, final Object[] stack) {
+        if (type != Opcodes.F_NEW) { // uncompressed frame
+            throw new IllegalStateException(
+                    "ClassReader.accept() should be called with EXPAND_FRAMES flag");
+        }
+
+        if (mv != null) {
+            mv.visitFrame(type, nLocal, local, nStack, stack);
+        }
+
+        if (this.locals != null) {
+            this.locals.clear();
+            this.stack.clear();
+        } else {
+            this.locals = new ArrayList<Object>();
+            this.stack = new ArrayList<Object>();
+        }
+        visitFrameTypes(nLocal, local, this.locals);
+        visitFrameTypes(nStack, stack, this.stack);
+        maxStack = Math.max(maxStack, this.stack.size());
+    }
+
+    private static void visitFrameTypes(final int n, final Object[] types,
+            final List<Object> result) {
+        for (int i = 0; i < n; ++i) {
+            Object type = types[i];
+            result.add(type);
+            if (type == Opcodes.LONG || type == Opcodes.DOUBLE) {
+                result.add(Opcodes.TOP);
+            }
+        }
+    }
+
+    @Override
+    public void visitInsn(final int opcode) {
+        if (mv != null) {
+            mv.visitInsn(opcode);
+        }
+        execute(opcode, 0, null);
+        if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
+                || opcode == Opcodes.ATHROW) {
+            this.locals = null;
+            this.stack = null;
+        }
+    }
+
+    @Override
+    public void visitIntInsn(final int opcode, final int operand) {
+        if (mv != null) {
+            mv.visitIntInsn(opcode, operand);
+        }
+        execute(opcode, operand, null);
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        if (mv != null) {
+            mv.visitVarInsn(opcode, var);
+        }
+        execute(opcode, var, null);
+    }
+
+    @Override
+    public void visitTypeInsn(final int opcode, final String type) {
+        if (opcode == Opcodes.NEW) {
+            if (labels == null) {
+                Label l = new Label();
+                labels = new ArrayList<Label>(3);
+                labels.add(l);
+                if (mv != null) {
+                    mv.visitLabel(l);
+                }
+            }
+            for (int i = 0; i < labels.size(); ++i) {
+                uninitializedTypes.put(labels.get(i), type);
+            }
+        }
+        if (mv != null) {
+            mv.visitTypeInsn(opcode, type);
+        }
+        execute(opcode, 0, type);
+    }
+
+    @Override
+    public void visitFieldInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        if (mv != null) {
+            mv.visitFieldInsn(opcode, owner, name, desc);
+        }
+        execute(opcode, 0, desc);
+    }
+
+    @Override
+    public void visitMethodInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        if (mv != null) {
+            mv.visitMethodInsn(opcode, owner, name, desc);
+        }
+        if (this.locals == null) {
+            labels = null;
+            return;
+        }
+        pop(desc);
+        if (opcode != Opcodes.INVOKESTATIC) {
+            Object t = pop();
+            if (opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<') {
+                Object u;
+                if (t == Opcodes.UNINITIALIZED_THIS) {
+                    u = this.owner;
+                } else {
+                    u = uninitializedTypes.get(t);
+                }
+                for (int i = 0; i < locals.size(); ++i) {
+                    if (locals.get(i) == t) {
+                        locals.set(i, u);
+                    }
+                }
+                for (int i = 0; i < stack.size(); ++i) {
+                    if (stack.get(i) == t) {
+                        stack.set(i, u);
+                    }
+                }
+            }
+        }
+        pushDesc(desc);
+        labels = null;
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
+        if (mv != null) {
+            mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+        }
+        if (this.locals == null) {
+            labels = null;
+            return;
+        }
+        pop(desc);
+        pushDesc(desc);
+        labels = null;
+    }
+
+    @Override
+    public void visitJumpInsn(final int opcode, final Label label) {
+        if (mv != null) {
+            mv.visitJumpInsn(opcode, label);
+        }
+        execute(opcode, 0, null);
+        if (opcode == Opcodes.GOTO) {
+            this.locals = null;
+            this.stack = null;
+        }
+    }
+
+    @Override
+    public void visitLabel(final Label label) {
+        if (mv != null) {
+            mv.visitLabel(label);
+        }
+        if (labels == null) {
+            labels = new ArrayList<Label>(3);
+        }
+        labels.add(label);
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        if (mv != null) {
+            mv.visitLdcInsn(cst);
+        }
+        if (this.locals == null) {
+            labels = null;
+            return;
+        }
+        if (cst instanceof Integer) {
+            push(Opcodes.INTEGER);
+        } else if (cst instanceof Long) {
+            push(Opcodes.LONG);
+            push(Opcodes.TOP);
+        } else if (cst instanceof Float) {
+            push(Opcodes.FLOAT);
+        } else if (cst instanceof Double) {
+            push(Opcodes.DOUBLE);
+            push(Opcodes.TOP);
+        } else if (cst instanceof String) {
+            push("java/lang/String");
+        } else if (cst instanceof Type) {
+            int sort = ((Type) cst).getSort();
+            if (sort == Type.OBJECT || sort == Type.ARRAY) {
+                push("java/lang/Class");
+            } else if (sort == Type.METHOD) {
+                push("java/lang/invoke/MethodType");
+            } else {
+                throw new IllegalArgumentException();
+            }
+        } else if (cst instanceof Handle) {
+            push("java/lang/invoke/MethodHandle");
+        } else {
+            throw new IllegalArgumentException();
+        }
+        labels = null;
+    }
+
+    @Override
+    public void visitIincInsn(final int var, final int increment) {
+        if (mv != null) {
+            mv.visitIincInsn(var, increment);
+        }
+        execute(Opcodes.IINC, var, null);
+    }
+
+    @Override
+    public void visitTableSwitchInsn(final int min, final int max,
+            final Label dflt, final Label... labels) {
+        if (mv != null) {
+            mv.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+        execute(Opcodes.TABLESWITCH, 0, null);
+        this.locals = null;
+        this.stack = null;
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
+            final Label[] labels) {
+        if (mv != null) {
+            mv.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+        execute(Opcodes.LOOKUPSWITCH, 0, null);
+        this.locals = null;
+        this.stack = null;
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        if (mv != null) {
+            mv.visitMultiANewArrayInsn(desc, dims);
+        }
+        execute(Opcodes.MULTIANEWARRAY, dims, desc);
+    }
+
+    @Override
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        if (mv != null) {
+            this.maxStack = Math.max(this.maxStack, maxStack);
+            this.maxLocals = Math.max(this.maxLocals, maxLocals);
+            mv.visitMaxs(this.maxStack, this.maxLocals);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+
+    private Object get(final int local) {
+        maxLocals = Math.max(maxLocals, local);
+        return local < locals.size() ? locals.get(local) : Opcodes.TOP;
+    }
+
+    private void set(final int local, final Object type) {
+        maxLocals = Math.max(maxLocals, local);
+        while (local >= locals.size()) {
+            locals.add(Opcodes.TOP);
+        }
+        locals.set(local, type);
+    }
+
+    private void push(final Object type) {
+        stack.add(type);
+        maxStack = Math.max(maxStack, stack.size());
+    }
+
+    private void pushDesc(final String desc) {
+        int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0;
+        switch (desc.charAt(index)) {
+        case 'V':
+            return;
+        case 'Z':
+        case 'C':
+        case 'B':
+        case 'S':
+        case 'I':
+            push(Opcodes.INTEGER);
+            return;
+        case 'F':
+            push(Opcodes.FLOAT);
+            return;
+        case 'J':
+            push(Opcodes.LONG);
+            push(Opcodes.TOP);
+            return;
+        case 'D':
+            push(Opcodes.DOUBLE);
+            push(Opcodes.TOP);
+            return;
+        case '[':
+            if (index == 0) {
+                push(desc);
+            } else {
+                push(desc.substring(index, desc.length()));
+            }
+            break;
+        // case 'L':
+        default:
+            if (index == 0) {
+                push(desc.substring(1, desc.length() - 1));
+            } else {
+                push(desc.substring(index + 1, desc.length() - 1));
+            }
+        }
+    }
+
+    private Object pop() {
+        return stack.remove(stack.size() - 1);
+    }
+
+    private void pop(final int n) {
+        int size = stack.size();
+        int end = size - n;
+        for (int i = size - 1; i >= end; --i) {
+            stack.remove(i);
+        }
+    }
+
+    private void pop(final String desc) {
+        char c = desc.charAt(0);
+        if (c == '(') {
+            int n = 0;
+            Type[] types = Type.getArgumentTypes(desc);
+            for (int i = 0; i < types.length; ++i) {
+                n += types[i].getSize();
+            }
+            pop(n);
+        } else if (c == 'J' || c == 'D') {
+            pop(2);
+        } else {
+            pop(1);
+        }
+    }
+
+    private void execute(final int opcode, final int iarg, final String sarg) {
+        if (this.locals == null) {
+            labels = null;
+            return;
+        }
+        Object t1, t2, t3, t4;
+        switch (opcode) {
+        case Opcodes.NOP:
+        case Opcodes.INEG:
+        case Opcodes.LNEG:
+        case Opcodes.FNEG:
+        case Opcodes.DNEG:
+        case Opcodes.I2B:
+        case Opcodes.I2C:
+        case Opcodes.I2S:
+        case Opcodes.GOTO:
+        case Opcodes.RETURN:
+            break;
+        case Opcodes.ACONST_NULL:
+            push(Opcodes.NULL);
+            break;
+        case Opcodes.ICONST_M1:
+        case Opcodes.ICONST_0:
+        case Opcodes.ICONST_1:
+        case Opcodes.ICONST_2:
+        case Opcodes.ICONST_3:
+        case Opcodes.ICONST_4:
+        case Opcodes.ICONST_5:
+        case Opcodes.BIPUSH:
+        case Opcodes.SIPUSH:
+            push(Opcodes.INTEGER);
+            break;
+        case Opcodes.LCONST_0:
+        case Opcodes.LCONST_1:
+            push(Opcodes.LONG);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.FCONST_0:
+        case Opcodes.FCONST_1:
+        case Opcodes.FCONST_2:
+            push(Opcodes.FLOAT);
+            break;
+        case Opcodes.DCONST_0:
+        case Opcodes.DCONST_1:
+            push(Opcodes.DOUBLE);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.ILOAD:
+        case Opcodes.FLOAD:
+        case Opcodes.ALOAD:
+            push(get(iarg));
+            break;
+        case Opcodes.LLOAD:
+        case Opcodes.DLOAD:
+            push(get(iarg));
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.IALOAD:
+        case Opcodes.BALOAD:
+        case Opcodes.CALOAD:
+        case Opcodes.SALOAD:
+            pop(2);
+            push(Opcodes.INTEGER);
+            break;
+        case Opcodes.LALOAD:
+        case Opcodes.D2L:
+            pop(2);
+            push(Opcodes.LONG);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.FALOAD:
+            pop(2);
+            push(Opcodes.FLOAT);
+            break;
+        case Opcodes.DALOAD:
+        case Opcodes.L2D:
+            pop(2);
+            push(Opcodes.DOUBLE);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.AALOAD:
+            pop(1);
+            t1 = pop();
+            if (t1 instanceof String) {
+                pushDesc(((String) t1).substring(1));
+            } else {
+                push("java/lang/Object");
+            }
+            break;
+        case Opcodes.ISTORE:
+        case Opcodes.FSTORE:
+        case Opcodes.ASTORE:
+            t1 = pop();
+            set(iarg, t1);
+            if (iarg > 0) {
+                t2 = get(iarg - 1);
+                if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
+                    set(iarg - 1, Opcodes.TOP);
+                }
+            }
+            break;
+        case Opcodes.LSTORE:
+        case Opcodes.DSTORE:
+            pop(1);
+            t1 = pop();
+            set(iarg, t1);
+            set(iarg + 1, Opcodes.TOP);
+            if (iarg > 0) {
+                t2 = get(iarg - 1);
+                if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) {
+                    set(iarg - 1, Opcodes.TOP);
+                }
+            }
+            break;
+        case Opcodes.IASTORE:
+        case Opcodes.BASTORE:
+        case Opcodes.CASTORE:
+        case Opcodes.SASTORE:
+        case Opcodes.FASTORE:
+        case Opcodes.AASTORE:
+            pop(3);
+            break;
+        case Opcodes.LASTORE:
+        case Opcodes.DASTORE:
+            pop(4);
+            break;
+        case Opcodes.POP:
+        case Opcodes.IFEQ:
+        case Opcodes.IFNE:
+        case Opcodes.IFLT:
+        case Opcodes.IFGE:
+        case Opcodes.IFGT:
+        case Opcodes.IFLE:
+        case Opcodes.IRETURN:
+        case Opcodes.FRETURN:
+        case Opcodes.ARETURN:
+        case Opcodes.TABLESWITCH:
+        case Opcodes.LOOKUPSWITCH:
+        case Opcodes.ATHROW:
+        case Opcodes.MONITORENTER:
+        case Opcodes.MONITOREXIT:
+        case Opcodes.IFNULL:
+        case Opcodes.IFNONNULL:
+            pop(1);
+            break;
+        case Opcodes.POP2:
+        case Opcodes.IF_ICMPEQ:
+        case Opcodes.IF_ICMPNE:
+        case Opcodes.IF_ICMPLT:
+        case Opcodes.IF_ICMPGE:
+        case Opcodes.IF_ICMPGT:
+        case Opcodes.IF_ICMPLE:
+        case Opcodes.IF_ACMPEQ:
+        case Opcodes.IF_ACMPNE:
+        case Opcodes.LRETURN:
+        case Opcodes.DRETURN:
+            pop(2);
+            break;
+        case Opcodes.DUP:
+            t1 = pop();
+            push(t1);
+            push(t1);
+            break;
+        case Opcodes.DUP_X1:
+            t1 = pop();
+            t2 = pop();
+            push(t1);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP_X2:
+            t1 = pop();
+            t2 = pop();
+            t3 = pop();
+            push(t1);
+            push(t3);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP2:
+            t1 = pop();
+            t2 = pop();
+            push(t2);
+            push(t1);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP2_X1:
+            t1 = pop();
+            t2 = pop();
+            t3 = pop();
+            push(t2);
+            push(t1);
+            push(t3);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.DUP2_X2:
+            t1 = pop();
+            t2 = pop();
+            t3 = pop();
+            t4 = pop();
+            push(t2);
+            push(t1);
+            push(t4);
+            push(t3);
+            push(t2);
+            push(t1);
+            break;
+        case Opcodes.SWAP:
+            t1 = pop();
+            t2 = pop();
+            push(t1);
+            push(t2);
+            break;
+        case Opcodes.IADD:
+        case Opcodes.ISUB:
+        case Opcodes.IMUL:
+        case Opcodes.IDIV:
+        case Opcodes.IREM:
+        case Opcodes.IAND:
+        case Opcodes.IOR:
+        case Opcodes.IXOR:
+        case Opcodes.ISHL:
+        case Opcodes.ISHR:
+        case Opcodes.IUSHR:
+        case Opcodes.L2I:
+        case Opcodes.D2I:
+        case Opcodes.FCMPL:
+        case Opcodes.FCMPG:
+            pop(2);
+            push(Opcodes.INTEGER);
+            break;
+        case Opcodes.LADD:
+        case Opcodes.LSUB:
+        case Opcodes.LMUL:
+        case Opcodes.LDIV:
+        case Opcodes.LREM:
+        case Opcodes.LAND:
+        case Opcodes.LOR:
+        case Opcodes.LXOR:
+            pop(4);
+            push(Opcodes.LONG);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.FADD:
+        case Opcodes.FSUB:
+        case Opcodes.FMUL:
+        case Opcodes.FDIV:
+        case Opcodes.FREM:
+        case Opcodes.L2F:
+        case Opcodes.D2F:
+            pop(2);
+            push(Opcodes.FLOAT);
+            break;
+        case Opcodes.DADD:
+        case Opcodes.DSUB:
+        case Opcodes.DMUL:
+        case Opcodes.DDIV:
+        case Opcodes.DREM:
+            pop(4);
+            push(Opcodes.DOUBLE);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.LSHL:
+        case Opcodes.LSHR:
+        case Opcodes.LUSHR:
+            pop(3);
+            push(Opcodes.LONG);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.IINC:
+            set(iarg, Opcodes.INTEGER);
+            break;
+        case Opcodes.I2L:
+        case Opcodes.F2L:
+            pop(1);
+            push(Opcodes.LONG);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.I2F:
+            pop(1);
+            push(Opcodes.FLOAT);
+            break;
+        case Opcodes.I2D:
+        case Opcodes.F2D:
+            pop(1);
+            push(Opcodes.DOUBLE);
+            push(Opcodes.TOP);
+            break;
+        case Opcodes.F2I:
+        case Opcodes.ARRAYLENGTH:
+        case Opcodes.INSTANCEOF:
+            pop(1);
+            push(Opcodes.INTEGER);
+            break;
+        case Opcodes.LCMP:
+        case Opcodes.DCMPL:
+        case Opcodes.DCMPG:
+            pop(4);
+            push(Opcodes.INTEGER);
+            break;
+        case Opcodes.JSR:
+        case Opcodes.RET:
+            throw new RuntimeException("JSR/RET are not supported");
+        case Opcodes.GETSTATIC:
+            pushDesc(sarg);
+            break;
+        case Opcodes.PUTSTATIC:
+            pop(sarg);
+            break;
+        case Opcodes.GETFIELD:
+            pop(1);
+            pushDesc(sarg);
+            break;
+        case Opcodes.PUTFIELD:
+            pop(sarg);
+            pop();
+            break;
+        case Opcodes.NEW:
+            push(labels.get(0));
+            break;
+        case Opcodes.NEWARRAY:
+            pop();
+            switch (iarg) {
+            case Opcodes.T_BOOLEAN:
+                pushDesc("[Z");
+                break;
+            case Opcodes.T_CHAR:
+                pushDesc("[C");
+                break;
+            case Opcodes.T_BYTE:
+                pushDesc("[B");
+                break;
+            case Opcodes.T_SHORT:
+                pushDesc("[S");
+                break;
+            case Opcodes.T_INT:
+                pushDesc("[I");
+                break;
+            case Opcodes.T_FLOAT:
+                pushDesc("[F");
+                break;
+            case Opcodes.T_DOUBLE:
+                pushDesc("[D");
+                break;
+            // case Opcodes.T_LONG:
+            default:
+                pushDesc("[J");
+                break;
+            }
+            break;
+        case Opcodes.ANEWARRAY:
+            pop();
+            pushDesc("[" + Type.getObjectType(sarg));
+            break;
+        case Opcodes.CHECKCAST:
+            pop();
+            pushDesc(Type.getObjectType(sarg).getDescriptor());
+            break;
+        // case Opcodes.MULTIANEWARRAY:
+        default:
+            pop(iarg);
+            pushDesc(sarg);
+            break;
+        }
+        labels = null;
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java b/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java
new file mode 100644
index 0000000..5a35bbf
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/CodeSizeEvaluator.java
@@ -0,0 +1,217 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import clojure.asm.Handle;
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+
+/**
+ * A {@link MethodVisitor} that can be used to approximate method size.
+ *
+ * @author Eugene Kuleshov
+ */
+public class CodeSizeEvaluator extends MethodVisitor implements Opcodes {
+
+    private int minSize;
+
+    private int maxSize;
+
+    public CodeSizeEvaluator(final MethodVisitor mv) {
+        this(Opcodes.ASM4, mv);
+    }
+
+    protected CodeSizeEvaluator(final int api, final MethodVisitor mv) {
+        super(api, mv);
+    }
+
+    public int getMinSize() {
+        return this.minSize;
+    }
+
+    public int getMaxSize() {
+        return this.maxSize;
+    }
+
+    @Override
+    public void visitInsn(final int opcode) {
+        minSize += 1;
+        maxSize += 1;
+        if (mv != null) {
+            mv.visitInsn(opcode);
+        }
+    }
+
+    @Override
+    public void visitIntInsn(final int opcode, final int operand) {
+        if (opcode == SIPUSH) {
+            minSize += 3;
+            maxSize += 3;
+        } else {
+            minSize += 2;
+            maxSize += 2;
+        }
+        if (mv != null) {
+            mv.visitIntInsn(opcode, operand);
+        }
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        if (var < 4 && opcode != RET) {
+            minSize += 1;
+            maxSize += 1;
+        } else if (var >= 256) {
+            minSize += 4;
+            maxSize += 4;
+        } else {
+            minSize += 2;
+            maxSize += 2;
+        }
+        if (mv != null) {
+            mv.visitVarInsn(opcode, var);
+        }
+    }
+
+    @Override
+    public void visitTypeInsn(final int opcode, final String type) {
+        minSize += 3;
+        maxSize += 3;
+        if (mv != null) {
+            mv.visitTypeInsn(opcode, type);
+        }
+    }
+
+    @Override
+    public void visitFieldInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        minSize += 3;
+        maxSize += 3;
+        if (mv != null) {
+            mv.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitMethodInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        if (opcode == INVOKEINTERFACE) {
+            minSize += 5;
+            maxSize += 5;
+        } else {
+            minSize += 3;
+            maxSize += 3;
+        }
+        if (mv != null) {
+            mv.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
+        minSize += 5;
+        maxSize += 5;
+        if (mv != null) {
+            mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+        }
+    }
+
+    @Override
+    public void visitJumpInsn(final int opcode, final Label label) {
+        minSize += 3;
+        if (opcode == GOTO || opcode == JSR) {
+            maxSize += 5;
+        } else {
+            maxSize += 8;
+        }
+        if (mv != null) {
+            mv.visitJumpInsn(opcode, label);
+        }
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        if (cst instanceof Long || cst instanceof Double) {
+            minSize += 3;
+            maxSize += 3;
+        } else {
+            minSize += 2;
+            maxSize += 3;
+        }
+        if (mv != null) {
+            mv.visitLdcInsn(cst);
+        }
+    }
+
+    @Override
+    public void visitIincInsn(final int var, final int increment) {
+        if (var > 255 || increment > 127 || increment < -128) {
+            minSize += 6;
+            maxSize += 6;
+        } else {
+            minSize += 3;
+            maxSize += 3;
+        }
+        if (mv != null) {
+            mv.visitIincInsn(var, increment);
+        }
+    }
+
+    @Override
+    public void visitTableSwitchInsn(final int min, final int max,
+            final Label dflt, final Label... labels) {
+        minSize += 13 + labels.length * 4;
+        maxSize += 16 + labels.length * 4;
+        if (mv != null) {
+            mv.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
+            final Label[] labels) {
+        minSize += 9 + keys.length * 8;
+        maxSize += 12 + keys.length * 8;
+        if (mv != null) {
+            mv.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        minSize += 4;
+        maxSize += 4;
+        if (mv != null) {
+            mv.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/GeneratorAdapter.java b/src/jvm/clojure/asm/commons/GeneratorAdapter.java
new file mode 100644
index 0000000..8727478
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/GeneratorAdapter.java
@@ -0,0 +1,1623 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import clojure.asm.ClassVisitor;
+import clojure.asm.Handle;
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link clojure.asm.MethodVisitor} with convenient methods to generate
+ * code. For example, using this adapter, the class below
+ *
+ * <pre>
+ * public class Example {
+ *     public static void main(String[] args) {
+ *         System.out.println("Hello world!");
+ *     }
+ * }
+ * </pre>
+ *
+ * can be generated as follows:
+ *
+ * <pre>
+ * ClassWriter cw = new ClassWriter(true);
+ * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null);
+ *
+ * Method m = Method.getMethod("void <init> ()");
+ * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw);
+ * mg.loadThis();
+ * mg.invokeConstructor(Type.getType(Object.class), m);
+ * mg.returnValue();
+ * mg.endMethod();
+ *
+ * m = Method.getMethod("void main (String[])");
+ * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
+ * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class));
+ * mg.push("Hello world!");
+ * mg.invokeVirtual(Type.getType(PrintStream.class),
+ *         Method.getMethod("void println (String)"));
+ * mg.returnValue();
+ * mg.endMethod();
+ *
+ * cw.visitEnd();
+ * </pre>
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ * @author Prashant Deva
+ */
+public class GeneratorAdapter extends LocalVariablesSorter {
+
+    private static final String CLDESC = "Ljava/lang/Class;";
+
+    private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
+
+    private static final Type BOOLEAN_TYPE = Type
+            .getObjectType("java/lang/Boolean");
+
+    private static final Type SHORT_TYPE = Type
+            .getObjectType("java/lang/Short");
+
+    private static final Type CHARACTER_TYPE = Type
+            .getObjectType("java/lang/Character");
+
+    private static final Type INTEGER_TYPE = Type
+            .getObjectType("java/lang/Integer");
+
+    private static final Type FLOAT_TYPE = Type
+            .getObjectType("java/lang/Float");
+
+    private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long");
+
+    private static final Type DOUBLE_TYPE = Type
+            .getObjectType("java/lang/Double");
+
+    private static final Type NUMBER_TYPE = Type
+            .getObjectType("java/lang/Number");
+
+    private static final Type OBJECT_TYPE = Type
+            .getObjectType("java/lang/Object");
+
+    private static final Method BOOLEAN_VALUE = Method
+            .getMethod("boolean booleanValue()");
+
+    private static final Method CHAR_VALUE = Method
+            .getMethod("char charValue()");
+
+    private static final Method INT_VALUE = Method.getMethod("int intValue()");
+
+    private static final Method FLOAT_VALUE = Method
+            .getMethod("float floatValue()");
+
+    private static final Method LONG_VALUE = Method
+            .getMethod("long longValue()");
+
+    private static final Method DOUBLE_VALUE = Method
+            .getMethod("double doubleValue()");
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int ADD = Opcodes.IADD;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int SUB = Opcodes.ISUB;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int MUL = Opcodes.IMUL;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int DIV = Opcodes.IDIV;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int REM = Opcodes.IREM;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int NEG = Opcodes.INEG;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int SHL = Opcodes.ISHL;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int SHR = Opcodes.ISHR;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int USHR = Opcodes.IUSHR;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int AND = Opcodes.IAND;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int OR = Opcodes.IOR;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public static final int XOR = Opcodes.IXOR;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public static final int EQ = Opcodes.IFEQ;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public static final int NE = Opcodes.IFNE;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public static final int LT = Opcodes.IFLT;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public static final int GE = Opcodes.IFGE;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public static final int GT = Opcodes.IFGT;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public static final int LE = Opcodes.IFLE;
+
+    /**
+     * Access flags of the method visited by this adapter.
+     */
+    private final int access;
+
+    /**
+     * Return type of the method visited by this adapter.
+     */
+    private final Type returnType;
+
+    /**
+     * Argument types of the method visited by this adapter.
+     */
+    private final Type[] argumentTypes;
+
+    /**
+     * Types of the local variables of the method visited by this adapter.
+     */
+    private final List<Type> localTypes = new ArrayList<Type>();
+
+    /**
+     * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this
+     * constructor</i>. Instead, they must use the
+     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
+     * version.
+     *
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     * @param access
+     *            the method's access flags (see {@link Opcodes}).
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     */
+    public GeneratorAdapter(final MethodVisitor mv, final int access,
+            final String name, final String desc) {
+        this(Opcodes.ASM4, mv, access, name, desc);
+    }
+
+    /**
+     * Creates a new {@link GeneratorAdapter}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     * @param access
+     *            the method's access flags (see {@link Opcodes}).
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     */
+    protected GeneratorAdapter(final int api, final MethodVisitor mv,
+            final int access, final String name, final String desc) {
+        super(api, access, desc, mv);
+        this.access = access;
+        this.returnType = Type.getReturnType(desc);
+        this.argumentTypes = Type.getArgumentTypes(desc);
+    }
+
+    /**
+     * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this
+     * constructor</i>. Instead, they must use the
+     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
+     * version.
+     *
+     * @param access
+     *            access flags of the adapted method.
+     * @param method
+     *            the adapted method.
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     */
+    public GeneratorAdapter(final int access, final Method method,
+            final MethodVisitor mv) {
+        this(mv, access, null, method.getDescriptor());
+    }
+
+    /**
+     * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this
+     * constructor</i>. Instead, they must use the
+     * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)}
+     * version.
+     *
+     * @param access
+     *            access flags of the adapted method.
+     * @param method
+     *            the adapted method.
+     * @param signature
+     *            the signature of the adapted method (may be <tt>null</tt>).
+     * @param exceptions
+     *            the exceptions thrown by the adapted method (may be
+     *            <tt>null</tt>).
+     * @param cv
+     *            the class visitor to which this adapter delegates calls.
+     */
+    public GeneratorAdapter(final int access, final Method method,
+            final String signature, final Type[] exceptions,
+            final ClassVisitor cv) {
+        this(access, method, cv
+                .visitMethod(access, method.getName(), method.getDescriptor(),
+                        signature, getInternalNames(exceptions)));
+    }
+
+    /**
+     * Returns the internal names of the given types.
+     *
+     * @param types
+     *            a set of types.
+     * @return the internal names of the given types.
+     */
+    private static String[] getInternalNames(final Type[] types) {
+        if (types == null) {
+            return null;
+        }
+        String[] names = new String[types.length];
+        for (int i = 0; i < names.length; ++i) {
+            names[i] = types[i].getInternalName();
+        }
+        return names;
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to push constants on the stack
+    // ------------------------------------------------------------------------
+
+    /**
+     * Generates the instruction to push the given value on the stack.
+     *
+     * @param value
+     *            the value to be pushed on the stack.
+     */
+    public void push(final boolean value) {
+        push(value ? 1 : 0);
+    }
+
+    /**
+     * Generates the instruction to push the given value on the stack.
+     *
+     * @param value
+     *            the value to be pushed on the stack.
+     */
+    public void push(final int value) {
+        if (value >= -1 && value <= 5) {
+            mv.visitInsn(Opcodes.ICONST_0 + value);
+        } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
+            mv.visitIntInsn(Opcodes.BIPUSH, value);
+        } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
+            mv.visitIntInsn(Opcodes.SIPUSH, value);
+        } else {
+            mv.visitLdcInsn(new Integer(value));
+        }
+    }
+
+    /**
+     * Generates the instruction to push the given value on the stack.
+     *
+     * @param value
+     *            the value to be pushed on the stack.
+     */
+    public void push(final long value) {
+        if (value == 0L || value == 1L) {
+            mv.visitInsn(Opcodes.LCONST_0 + (int) value);
+        } else {
+            mv.visitLdcInsn(new Long(value));
+        }
+    }
+
+    /**
+     * Generates the instruction to push the given value on the stack.
+     *
+     * @param value
+     *            the value to be pushed on the stack.
+     */
+    public void push(final float value) {
+        int bits = Float.floatToIntBits(value);
+        if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
+            mv.visitInsn(Opcodes.FCONST_0 + (int) value);
+        } else {
+            mv.visitLdcInsn(new Float(value));
+        }
+    }
+
+    /**
+     * Generates the instruction to push the given value on the stack.
+     *
+     * @param value
+     *            the value to be pushed on the stack.
+     */
+    public void push(final double value) {
+        long bits = Double.doubleToLongBits(value);
+        if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
+            mv.visitInsn(Opcodes.DCONST_0 + (int) value);
+        } else {
+            mv.visitLdcInsn(new Double(value));
+        }
+    }
+
+    /**
+     * Generates the instruction to push the given value on the stack.
+     *
+     * @param value
+     *            the value to be pushed on the stack. May be <tt>null</tt>.
+     */
+    public void push(final String value) {
+        if (value == null) {
+            mv.visitInsn(Opcodes.ACONST_NULL);
+        } else {
+            mv.visitLdcInsn(value);
+        }
+    }
+
+    /**
+     * Generates the instruction to push the given value on the stack.
+     *
+     * @param value
+     *            the value to be pushed on the stack.
+     */
+    public void push(final Type value) {
+        if (value == null) {
+            mv.visitInsn(Opcodes.ACONST_NULL);
+        } else {
+            switch (value.getSort()) {
+            case Type.BOOLEAN:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean",
+                        "TYPE", CLDESC);
+                break;
+            case Type.CHAR:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character",
+                        "TYPE", CLDESC);
+                break;
+            case Type.BYTE:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE",
+                        CLDESC);
+                break;
+            case Type.SHORT:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE",
+                        CLDESC);
+                break;
+            case Type.INT:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer",
+                        "TYPE", CLDESC);
+                break;
+            case Type.FLOAT:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE",
+                        CLDESC);
+                break;
+            case Type.LONG:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE",
+                        CLDESC);
+                break;
+            case Type.DOUBLE:
+                mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double",
+                        "TYPE", CLDESC);
+                break;
+            default:
+                mv.visitLdcInsn(value);
+            }
+        }
+    }
+
+    /**
+     * Generates the instruction to push a handle on the stack.
+     *
+     * @param handle
+     *            the handle to be pushed on the stack.
+     */
+    public void push(final Handle handle) {
+        mv.visitLdcInsn(handle);
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to load and store method arguments
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the index of the given method argument in the frame's local
+     * variables array.
+     *
+     * @param arg
+     *            the index of a method argument.
+     * @return the index of the given method argument in the frame's local
+     *         variables array.
+     */
+    private int getArgIndex(final int arg) {
+        int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0;
+        for (int i = 0; i < arg; i++) {
+            index += argumentTypes[i].getSize();
+        }
+        return index;
+    }
+
+    /**
+     * Generates the instruction to push a local variable on the stack.
+     *
+     * @param type
+     *            the type of the local variable to be loaded.
+     * @param index
+     *            an index in the frame's local variables array.
+     */
+    private void loadInsn(final Type type, final int index) {
+        mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index);
+    }
+
+    /**
+     * Generates the instruction to store the top stack value in a local
+     * variable.
+     *
+     * @param type
+     *            the type of the local variable to be stored.
+     * @param index
+     *            an index in the frame's local variables array.
+     */
+    private void storeInsn(final Type type, final int index) {
+        mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index);
+    }
+
+    /**
+     * Generates the instruction to load 'this' on the stack.
+     */
+    public void loadThis() {
+        if ((access & Opcodes.ACC_STATIC) != 0) {
+            throw new IllegalStateException(
+                    "no 'this' pointer within static method");
+        }
+        mv.visitVarInsn(Opcodes.ALOAD, 0);
+    }
+
+    /**
+     * Generates the instruction to load the given method argument on the stack.
+     *
+     * @param arg
+     *            the index of a method argument.
+     */
+    public void loadArg(final int arg) {
+        loadInsn(argumentTypes[arg], getArgIndex(arg));
+    }
+
+    /**
+     * Generates the instructions to load the given method arguments on the
+     * stack.
+     *
+     * @param arg
+     *            the index of the first method argument to be loaded.
+     * @param count
+     *            the number of method arguments to be loaded.
+     */
+    public void loadArgs(final int arg, final int count) {
+        int index = getArgIndex(arg);
+        for (int i = 0; i < count; ++i) {
+            Type t = argumentTypes[arg + i];
+            loadInsn(t, index);
+            index += t.getSize();
+        }
+    }
+
+    /**
+     * Generates the instructions to load all the method arguments on the stack.
+     */
+    public void loadArgs() {
+        loadArgs(0, argumentTypes.length);
+    }
+
+    /**
+     * Generates the instructions to load all the method arguments on the stack,
+     * as a single object array.
+     */
+    public void loadArgArray() {
+        push(argumentTypes.length);
+        newArray(OBJECT_TYPE);
+        for (int i = 0; i < argumentTypes.length; i++) {
+            dup();
+            push(i);
+            loadArg(i);
+            box(argumentTypes[i]);
+            arrayStore(OBJECT_TYPE);
+        }
+    }
+
+    /**
+     * Generates the instruction to store the top stack value in the given
+     * method argument.
+     *
+     * @param arg
+     *            the index of a method argument.
+     */
+    public void storeArg(final int arg) {
+        storeInsn(argumentTypes[arg], getArgIndex(arg));
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to load and store local variables
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the type of the given local variable.
+     *
+     * @param local
+     *            a local variable identifier, as returned by
+     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+     * @return the type of the given local variable.
+     */
+    public Type getLocalType(final int local) {
+        return localTypes.get(local - firstLocal);
+    }
+
+    @Override
+    protected void setLocalType(final int local, final Type type) {
+        int index = local - firstLocal;
+        while (localTypes.size() < index + 1) {
+            localTypes.add(null);
+        }
+        localTypes.set(index, type);
+    }
+
+    /**
+     * Generates the instruction to load the given local variable on the stack.
+     *
+     * @param local
+     *            a local variable identifier, as returned by
+     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+     */
+    public void loadLocal(final int local) {
+        loadInsn(getLocalType(local), local);
+    }
+
+    /**
+     * Generates the instruction to load the given local variable on the stack.
+     *
+     * @param local
+     *            a local variable identifier, as returned by
+     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+     * @param type
+     *            the type of this local variable.
+     */
+    public void loadLocal(final int local, final Type type) {
+        setLocalType(local, type);
+        loadInsn(type, local);
+    }
+
+    /**
+     * Generates the instruction to store the top stack value in the given local
+     * variable.
+     *
+     * @param local
+     *            a local variable identifier, as returned by
+     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+     */
+    public void storeLocal(final int local) {
+        storeInsn(getLocalType(local), local);
+    }
+
+    /**
+     * Generates the instruction to store the top stack value in the given local
+     * variable.
+     *
+     * @param local
+     *            a local variable identifier, as returned by
+     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+     * @param type
+     *            the type of this local variable.
+     */
+    public void storeLocal(final int local, final Type type) {
+        setLocalType(local, type);
+        storeInsn(type, local);
+    }
+
+    /**
+     * Generates the instruction to load an element from an array.
+     *
+     * @param type
+     *            the type of the array element to be loaded.
+     */
+    public void arrayLoad(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
+    }
+
+    /**
+     * Generates the instruction to store an element in an array.
+     *
+     * @param type
+     *            the type of the array element to be stored.
+     */
+    public void arrayStore(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to manage the stack
+    // ------------------------------------------------------------------------
+
+    /**
+     * Generates a POP instruction.
+     */
+    public void pop() {
+        mv.visitInsn(Opcodes.POP);
+    }
+
+    /**
+     * Generates a POP2 instruction.
+     */
+    public void pop2() {
+        mv.visitInsn(Opcodes.POP2);
+    }
+
+    /**
+     * Generates a DUP instruction.
+     */
+    public void dup() {
+        mv.visitInsn(Opcodes.DUP);
+    }
+
+    /**
+     * Generates a DUP2 instruction.
+     */
+    public void dup2() {
+        mv.visitInsn(Opcodes.DUP2);
+    }
+
+    /**
+     * Generates a DUP_X1 instruction.
+     */
+    public void dupX1() {
+        mv.visitInsn(Opcodes.DUP_X1);
+    }
+
+    /**
+     * Generates a DUP_X2 instruction.
+     */
+    public void dupX2() {
+        mv.visitInsn(Opcodes.DUP_X2);
+    }
+
+    /**
+     * Generates a DUP2_X1 instruction.
+     */
+    public void dup2X1() {
+        mv.visitInsn(Opcodes.DUP2_X1);
+    }
+
+    /**
+     * Generates a DUP2_X2 instruction.
+     */
+    public void dup2X2() {
+        mv.visitInsn(Opcodes.DUP2_X2);
+    }
+
+    /**
+     * Generates a SWAP instruction.
+     */
+    public void swap() {
+        mv.visitInsn(Opcodes.SWAP);
+    }
+
+    /**
+     * Generates the instructions to swap the top two stack values.
+     *
+     * @param prev
+     *            type of the top - 1 stack value.
+     * @param type
+     *            type of the top stack value.
+     */
+    public void swap(final Type prev, final Type type) {
+        if (type.getSize() == 1) {
+            if (prev.getSize() == 1) {
+                swap(); // same as dupX1(), pop();
+            } else {
+                dupX2();
+                pop();
+            }
+        } else {
+            if (prev.getSize() == 1) {
+                dup2X1();
+                pop2();
+            } else {
+                dup2X2();
+                pop2();
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to do mathematical and logical operations
+    // ------------------------------------------------------------------------
+
+    /**
+     * Generates the instruction to do the specified mathematical or logical
+     * operation.
+     *
+     * @param op
+     *            a mathematical or logical operation. Must be one of ADD, SUB,
+     *            MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR.
+     * @param type
+     *            the type of the operand(s) for this operation.
+     */
+    public void math(final int op, final Type type) {
+        mv.visitInsn(type.getOpcode(op));
+    }
+
+    /**
+     * Generates the instructions to compute the bitwise negation of the top
+     * stack value.
+     */
+    public void not() {
+        mv.visitInsn(Opcodes.ICONST_1);
+        mv.visitInsn(Opcodes.IXOR);
+    }
+
+    /**
+     * Generates the instruction to increment the given local variable.
+     *
+     * @param local
+     *            the local variable to be incremented.
+     * @param amount
+     *            the amount by which the local variable must be incremented.
+     */
+    public void iinc(final int local, final int amount) {
+        mv.visitIincInsn(local, amount);
+    }
+
+    /**
+     * Generates the instructions to cast a numerical value from one type to
+     * another.
+     *
+     * @param from
+     *            the type of the top stack value
+     * @param to
+     *            the type into which this value must be cast.
+     */
+    public void cast(final Type from, final Type to) {
+        if (from != to) {
+            if (from == Type.DOUBLE_TYPE) {
+                if (to == Type.FLOAT_TYPE) {
+                    mv.visitInsn(Opcodes.D2F);
+                } else if (to == Type.LONG_TYPE) {
+                    mv.visitInsn(Opcodes.D2L);
+                } else {
+                    mv.visitInsn(Opcodes.D2I);
+                    cast(Type.INT_TYPE, to);
+                }
+            } else if (from == Type.FLOAT_TYPE) {
+                if (to == Type.DOUBLE_TYPE) {
+                    mv.visitInsn(Opcodes.F2D);
+                } else if (to == Type.LONG_TYPE) {
+                    mv.visitInsn(Opcodes.F2L);
+                } else {
+                    mv.visitInsn(Opcodes.F2I);
+                    cast(Type.INT_TYPE, to);
+                }
+            } else if (from == Type.LONG_TYPE) {
+                if (to == Type.DOUBLE_TYPE) {
+                    mv.visitInsn(Opcodes.L2D);
+                } else if (to == Type.FLOAT_TYPE) {
+                    mv.visitInsn(Opcodes.L2F);
+                } else {
+                    mv.visitInsn(Opcodes.L2I);
+                    cast(Type.INT_TYPE, to);
+                }
+            } else {
+                if (to == Type.BYTE_TYPE) {
+                    mv.visitInsn(Opcodes.I2B);
+                } else if (to == Type.CHAR_TYPE) {
+                    mv.visitInsn(Opcodes.I2C);
+                } else if (to == Type.DOUBLE_TYPE) {
+                    mv.visitInsn(Opcodes.I2D);
+                } else if (to == Type.FLOAT_TYPE) {
+                    mv.visitInsn(Opcodes.I2F);
+                } else if (to == Type.LONG_TYPE) {
+                    mv.visitInsn(Opcodes.I2L);
+                } else if (to == Type.SHORT_TYPE) {
+                    mv.visitInsn(Opcodes.I2S);
+                }
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to do boxing and unboxing operations
+    // ------------------------------------------------------------------------
+
+    private static Type getBoxedType(final Type type) {
+        switch (type.getSort()) {
+        case Type.BYTE:
+            return BYTE_TYPE;
+        case Type.BOOLEAN:
+            return BOOLEAN_TYPE;
+        case Type.SHORT:
+            return SHORT_TYPE;
+        case Type.CHAR:
+            return CHARACTER_TYPE;
+        case Type.INT:
+            return INTEGER_TYPE;
+        case Type.FLOAT:
+            return FLOAT_TYPE;
+        case Type.LONG:
+            return LONG_TYPE;
+        case Type.DOUBLE:
+            return DOUBLE_TYPE;
+        }
+        return type;
+    }
+
+    /**
+     * Generates the instructions to box the top stack value. This value is
+     * replaced by its boxed equivalent on top of the stack.
+     *
+     * @param type
+     *            the type of the top stack value.
+     */
+    public void box(final Type type) {
+        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
+            return;
+        }
+        if (type == Type.VOID_TYPE) {
+            push((String) null);
+        } else {
+            Type boxed = getBoxedType(type);
+            newInstance(boxed);
+            if (type.getSize() == 2) {
+                // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
+                dupX2();
+                dupX2();
+                pop();
+            } else {
+                // p -> po -> opo -> oop -> o
+                dupX1();
+                swap();
+            }
+            invokeConstructor(boxed, new Method("<init>", Type.VOID_TYPE,
+                    new Type[] { type }));
+        }
+    }
+
+    /**
+     * Generates the instructions to box the top stack value using Java 5's
+     * valueOf() method. This value is replaced by its boxed equivalent on top
+     * of the stack.
+     *
+     * @param type
+     *            the type of the top stack value.
+     */
+    public void valueOf(final Type type) {
+        if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
+            return;
+        }
+        if (type == Type.VOID_TYPE) {
+            push((String) null);
+        } else {
+            Type boxed = getBoxedType(type);
+            invokeStatic(boxed, new Method("valueOf", boxed,
+                    new Type[] { type }));
+        }
+    }
+
+    /**
+     * Generates the instructions to unbox the top stack value. This value is
+     * replaced by its unboxed equivalent on top of the stack.
+     *
+     * @param type
+     *            the type of the top stack value.
+     */
+    public void unbox(final Type type) {
+        Type t = NUMBER_TYPE;
+        Method sig = null;
+        switch (type.getSort()) {
+        case Type.VOID:
+            return;
+        case Type.CHAR:
+            t = CHARACTER_TYPE;
+            sig = CHAR_VALUE;
+            break;
+        case Type.BOOLEAN:
+            t = BOOLEAN_TYPE;
+            sig = BOOLEAN_VALUE;
+            break;
+        case Type.DOUBLE:
+            sig = DOUBLE_VALUE;
+            break;
+        case Type.FLOAT:
+            sig = FLOAT_VALUE;
+            break;
+        case Type.LONG:
+            sig = LONG_VALUE;
+            break;
+        case Type.INT:
+        case Type.SHORT:
+        case Type.BYTE:
+            sig = INT_VALUE;
+        }
+        if (sig == null) {
+            checkCast(type);
+        } else {
+            checkCast(t);
+            invokeVirtual(t, sig);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to jump to other instructions
+    // ------------------------------------------------------------------------
+
+    /**
+     * Creates a new {@link Label}.
+     *
+     * @return a new {@link Label}.
+     */
+    public Label newLabel() {
+        return new Label();
+    }
+
+    /**
+     * Marks the current code position with the given label.
+     *
+     * @param label
+     *            a label.
+     */
+    public void mark(final Label label) {
+        mv.visitLabel(label);
+    }
+
+    /**
+     * Marks the current code position with a new label.
+     *
+     * @return the label that was created to mark the current code position.
+     */
+    public Label mark() {
+        Label label = new Label();
+        mv.visitLabel(label);
+        return label;
+    }
+
+    /**
+     * Generates the instructions to jump to a label based on the comparison of
+     * the top two stack values.
+     *
+     * @param type
+     *            the type of the top two stack values.
+     * @param mode
+     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
+     *            LE.
+     * @param label
+     *            where to jump if the comparison result is <tt>true</tt>.
+     */
+    public void ifCmp(final Type type, final int mode, final Label label) {
+        switch (type.getSort()) {
+        case Type.LONG:
+            mv.visitInsn(Opcodes.LCMP);
+            break;
+        case Type.DOUBLE:
+            mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL
+                    : Opcodes.DCMPG);
+            break;
+        case Type.FLOAT:
+            mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL
+                    : Opcodes.FCMPG);
+            break;
+        case Type.ARRAY:
+        case Type.OBJECT:
+            switch (mode) {
+            case EQ:
+                mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
+                return;
+            case NE:
+                mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
+                return;
+            }
+            throw new IllegalArgumentException("Bad comparison for type "
+                    + type);
+        default:
+            int intOp = -1;
+            switch (mode) {
+            case EQ:
+                intOp = Opcodes.IF_ICMPEQ;
+                break;
+            case NE:
+                intOp = Opcodes.IF_ICMPNE;
+                break;
+            case GE:
+                intOp = Opcodes.IF_ICMPGE;
+                break;
+            case LT:
+                intOp = Opcodes.IF_ICMPLT;
+                break;
+            case LE:
+                intOp = Opcodes.IF_ICMPLE;
+                break;
+            case GT:
+                intOp = Opcodes.IF_ICMPGT;
+                break;
+            }
+            mv.visitJumpInsn(intOp, label);
+            return;
+        }
+        mv.visitJumpInsn(mode, label);
+    }
+
+    /**
+     * Generates the instructions to jump to a label based on the comparison of
+     * the top two integer stack values.
+     *
+     * @param mode
+     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
+     *            LE.
+     * @param label
+     *            where to jump if the comparison result is <tt>true</tt>.
+     */
+    public void ifICmp(final int mode, final Label label) {
+        ifCmp(Type.INT_TYPE, mode, label);
+    }
+
+    /**
+     * Generates the instructions to jump to a label based on the comparison of
+     * the top integer stack value with zero.
+     *
+     * @param mode
+     *            how these values must be compared. One of EQ, NE, LT, GE, GT,
+     *            LE.
+     * @param label
+     *            where to jump if the comparison result is <tt>true</tt>.
+     */
+    public void ifZCmp(final int mode, final Label label) {
+        mv.visitJumpInsn(mode, label);
+    }
+
+    /**
+     * Generates the instruction to jump to the given label if the top stack
+     * value is null.
+     *
+     * @param label
+     *            where to jump if the condition is <tt>true</tt>.
+     */
+    public void ifNull(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFNULL, label);
+    }
+
+    /**
+     * Generates the instruction to jump to the given label if the top stack
+     * value is not null.
+     *
+     * @param label
+     *            where to jump if the condition is <tt>true</tt>.
+     */
+    public void ifNonNull(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFNONNULL, label);
+    }
+
+    /**
+     * Generates the instruction to jump to the given label.
+     *
+     * @param label
+     *            where to jump if the condition is <tt>true</tt>.
+     */
+    public void goTo(final Label label) {
+        mv.visitJumpInsn(Opcodes.GOTO, label);
+    }
+
+    /**
+     * Generates a RET instruction.
+     *
+     * @param local
+     *            a local variable identifier, as returned by
+     *            {@link LocalVariablesSorter#newLocal(Type) newLocal()}.
+     */
+    public void ret(final int local) {
+        mv.visitVarInsn(Opcodes.RET, local);
+    }
+
+    /**
+     * Generates the instructions for a switch statement.
+     *
+     * @param keys
+     *            the switch case keys.
+     * @param generator
+     *            a generator to generate the code for the switch cases.
+     */
+    public void tableSwitch(final int[] keys,
+            final TableSwitchGenerator generator) {
+        float density;
+        if (keys.length == 0) {
+            density = 0;
+        } else {
+            density = (float) keys.length
+                    / (keys[keys.length - 1] - keys[0] + 1);
+        }
+        tableSwitch(keys, generator, density >= 0.5f);
+    }
+
+    /**
+     * Generates the instructions for a switch statement.
+     *
+     * @param keys
+     *            the switch case keys.
+     * @param generator
+     *            a generator to generate the code for the switch cases.
+     * @param useTable
+     *            <tt>true</tt> to use a TABLESWITCH instruction, or
+     *            <tt>false</tt> to use a LOOKUPSWITCH instruction.
+     */
+    public void tableSwitch(final int[] keys,
+            final TableSwitchGenerator generator, final boolean useTable) {
+        for (int i = 1; i < keys.length; ++i) {
+            if (keys[i] < keys[i - 1]) {
+                throw new IllegalArgumentException(
+                        "keys must be sorted ascending");
+            }
+        }
+        Label def = newLabel();
+        Label end = newLabel();
+        if (keys.length > 0) {
+            int len = keys.length;
+            int min = keys[0];
+            int max = keys[len - 1];
+            int range = max - min + 1;
+            if (useTable) {
+                Label[] labels = new Label[range];
+                Arrays.fill(labels, def);
+                for (int i = 0; i < len; ++i) {
+                    labels[keys[i] - min] = newLabel();
+                }
+                mv.visitTableSwitchInsn(min, max, def, labels);
+                for (int i = 0; i < range; ++i) {
+                    Label label = labels[i];
+                    if (label != def) {
+                        mark(label);
+                        generator.generateCase(i + min, end);
+                    }
+                }
+            } else {
+                Label[] labels = new Label[len];
+                for (int i = 0; i < len; ++i) {
+                    labels[i] = newLabel();
+                }
+                mv.visitLookupSwitchInsn(def, keys, labels);
+                for (int i = 0; i < len; ++i) {
+                    mark(labels[i]);
+                    generator.generateCase(keys[i], end);
+                }
+            }
+        }
+        mark(def);
+        generator.generateDefault();
+        mark(end);
+    }
+
+    /**
+     * Generates the instruction to return the top stack value to the caller.
+     */
+    public void returnValue() {
+        mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN));
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to load and store fields
+    // ------------------------------------------------------------------------
+
+    /**
+     * Generates a get field or set field instruction.
+     *
+     * @param opcode
+     *            the instruction's opcode.
+     * @param ownerType
+     *            the class in which the field is defined.
+     * @param name
+     *            the name of the field.
+     * @param fieldType
+     *            the type of the field.
+     */
+    private void fieldInsn(final int opcode, final Type ownerType,
+            final String name, final Type fieldType) {
+        mv.visitFieldInsn(opcode, ownerType.getInternalName(), name,
+                fieldType.getDescriptor());
+    }
+
+    /**
+     * Generates the instruction to push the value of a static field on the
+     * stack.
+     *
+     * @param owner
+     *            the class in which the field is defined.
+     * @param name
+     *            the name of the field.
+     * @param type
+     *            the type of the field.
+     */
+    public void getStatic(final Type owner, final String name, final Type type) {
+        fieldInsn(Opcodes.GETSTATIC, owner, name, type);
+    }
+
+    /**
+     * Generates the instruction to store the top stack value in a static field.
+     *
+     * @param owner
+     *            the class in which the field is defined.
+     * @param name
+     *            the name of the field.
+     * @param type
+     *            the type of the field.
+     */
+    public void putStatic(final Type owner, final String name, final Type type) {
+        fieldInsn(Opcodes.PUTSTATIC, owner, name, type);
+    }
+
+    /**
+     * Generates the instruction to push the value of a non static field on the
+     * stack.
+     *
+     * @param owner
+     *            the class in which the field is defined.
+     * @param name
+     *            the name of the field.
+     * @param type
+     *            the type of the field.
+     */
+    public void getField(final Type owner, final String name, final Type type) {
+        fieldInsn(Opcodes.GETFIELD, owner, name, type);
+    }
+
+    /**
+     * Generates the instruction to store the top stack value in a non static
+     * field.
+     *
+     * @param owner
+     *            the class in which the field is defined.
+     * @param name
+     *            the name of the field.
+     * @param type
+     *            the type of the field.
+     */
+    public void putField(final Type owner, final String name, final Type type) {
+        fieldInsn(Opcodes.PUTFIELD, owner, name, type);
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to invoke methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Generates an invoke method instruction.
+     *
+     * @param opcode
+     *            the instruction's opcode.
+     * @param type
+     *            the class in which the method is defined.
+     * @param method
+     *            the method to be invoked.
+     */
+    private void invokeInsn(final int opcode, final Type type,
+            final Method method) {
+        String owner = type.getSort() == Type.ARRAY ? type.getDescriptor()
+                : type.getInternalName();
+        mv.visitMethodInsn(opcode, owner, method.getName(),
+                method.getDescriptor());
+    }
+
+    /**
+     * Generates the instruction to invoke a normal method.
+     *
+     * @param owner
+     *            the class in which the method is defined.
+     * @param method
+     *            the method to be invoked.
+     */
+    public void invokeVirtual(final Type owner, final Method method) {
+        invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method);
+    }
+
+    /**
+     * Generates the instruction to invoke a constructor.
+     *
+     * @param type
+     *            the class in which the constructor is defined.
+     * @param method
+     *            the constructor to be invoked.
+     */
+    public void invokeConstructor(final Type type, final Method method) {
+        invokeInsn(Opcodes.INVOKESPECIAL, type, method);
+    }
+
+    /**
+     * Generates the instruction to invoke a static method.
+     *
+     * @param owner
+     *            the class in which the method is defined.
+     * @param method
+     *            the method to be invoked.
+     */
+    public void invokeStatic(final Type owner, final Method method) {
+        invokeInsn(Opcodes.INVOKESTATIC, owner, method);
+    }
+
+    /**
+     * Generates the instruction to invoke an interface method.
+     *
+     * @param owner
+     *            the class in which the method is defined.
+     * @param method
+     *            the method to be invoked.
+     */
+    public void invokeInterface(final Type owner, final Method method) {
+        invokeInsn(Opcodes.INVOKEINTERFACE, owner, method);
+    }
+
+    /**
+     * Generates an invokedynamic instruction.
+     *
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     * @param bsm
+     *            the bootstrap method.
+     * @param bsmArgs
+     *            the bootstrap method constant arguments. Each argument must be
+     *            an {@link Integer}, {@link Float}, {@link Long},
+     *            {@link Double}, {@link String}, {@link Type} or {@link Handle}
+     *            value. This method is allowed to modify the content of the
+     *            array so a caller should expect that this array may change.
+     */
+    public void invokeDynamic(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
+        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+    }
+
+    // ------------------------------------------------------------------------
+    // Instructions to create objects and arrays
+    // ------------------------------------------------------------------------
+
+    /**
+     * Generates a type dependent instruction.
+     *
+     * @param opcode
+     *            the instruction's opcode.
+     * @param type
+     *            the instruction's operand.
+     */
+    private void typeInsn(final int opcode, final Type type) {
+        mv.visitTypeInsn(opcode, type.getInternalName());
+    }
+
+    /**
+     * Generates the instruction to create a new object.
+     *
+     * @param type
+     *            the class of the object to be created.
+     */
+    public void newInstance(final Type type) {
+        typeInsn(Opcodes.NEW, type);
+    }
+
+    /**
+     * Generates the instruction to create a new array.
+     *
+     * @param type
+     *            the type of the array elements.
+     */
+    public void newArray(final Type type) {
+        int typ;
+        switch (type.getSort()) {
+        case Type.BOOLEAN:
+            typ = Opcodes.T_BOOLEAN;
+            break;
+        case Type.CHAR:
+            typ = Opcodes.T_CHAR;
+            break;
+        case Type.BYTE:
+            typ = Opcodes.T_BYTE;
+            break;
+        case Type.SHORT:
+            typ = Opcodes.T_SHORT;
+            break;
+        case Type.INT:
+            typ = Opcodes.T_INT;
+            break;
+        case Type.FLOAT:
+            typ = Opcodes.T_FLOAT;
+            break;
+        case Type.LONG:
+            typ = Opcodes.T_LONG;
+            break;
+        case Type.DOUBLE:
+            typ = Opcodes.T_DOUBLE;
+            break;
+        default:
+            typeInsn(Opcodes.ANEWARRAY, type);
+            return;
+        }
+        mv.visitIntInsn(Opcodes.NEWARRAY, typ);
+    }
+
+    // ------------------------------------------------------------------------
+    // Miscelaneous instructions
+    // ------------------------------------------------------------------------
+
+    /**
+     * Generates the instruction to compute the length of an array.
+     */
+    public void arrayLength() {
+        mv.visitInsn(Opcodes.ARRAYLENGTH);
+    }
+
+    /**
+     * Generates the instruction to throw an exception.
+     */
+    public void throwException() {
+        mv.visitInsn(Opcodes.ATHROW);
+    }
+
+    /**
+     * Generates the instructions to create and throw an exception. The
+     * exception class must have a constructor with a single String argument.
+     *
+     * @param type
+     *            the class of the exception to be thrown.
+     * @param msg
+     *            the detailed message of the exception.
+     */
+    public void throwException(final Type type, final String msg) {
+        newInstance(type);
+        dup();
+        push(msg);
+        invokeConstructor(type, Method.getMethod("void <init> (String)"));
+        throwException();
+    }
+
+    /**
+     * Generates the instruction to check that the top stack value is of the
+     * given type.
+     *
+     * @param type
+     *            a class or interface type.
+     */
+    public void checkCast(final Type type) {
+        if (!type.equals(OBJECT_TYPE)) {
+            typeInsn(Opcodes.CHECKCAST, type);
+        }
+    }
+
+    /**
+     * Generates the instruction to test if the top stack value is of the given
+     * type.
+     *
+     * @param type
+     *            a class or interface type.
+     */
+    public void instanceOf(final Type type) {
+        typeInsn(Opcodes.INSTANCEOF, type);
+    }
+
+    /**
+     * Generates the instruction to get the monitor of the top stack value.
+     */
+    public void monitorEnter() {
+        mv.visitInsn(Opcodes.MONITORENTER);
+    }
+
+    /**
+     * Generates the instruction to release the monitor of the top stack value.
+     */
+    public void monitorExit() {
+        mv.visitInsn(Opcodes.MONITOREXIT);
+    }
+
+    // ------------------------------------------------------------------------
+    // Non instructions
+    // ------------------------------------------------------------------------
+
+    /**
+     * Marks the end of the visited method.
+     */
+    public void endMethod() {
+        if ((access & Opcodes.ACC_ABSTRACT) == 0) {
+            mv.visitMaxs(0, 0);
+        }
+        mv.visitEnd();
+    }
+
+    /**
+     * Marks the start of an exception handler.
+     *
+     * @param start
+     *            beginning of the exception handler's scope (inclusive).
+     * @param end
+     *            end of the exception handler's scope (exclusive).
+     * @param exception
+     *            internal name of the type of exceptions handled by the
+     *            handler.
+     */
+    public void catchException(final Label start, final Label end,
+            final Type exception) {
+        if (exception == null) {
+            mv.visitTryCatchBlock(start, end, mark(), null);
+        } else {
+            mv.visitTryCatchBlock(start, end, mark(),
+                    exception.getInternalName());
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/InstructionAdapter.java b/src/jvm/clojure/asm/commons/InstructionAdapter.java
new file mode 100644
index 0000000..e61c109
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/InstructionAdapter.java
@@ -0,0 +1,1090 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package clojure.asm.commons;
+
+import clojure.asm.Handle;
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link MethodVisitor} providing a more detailed API to generate and
+ * transform instructions.
+ *
+ * @author Eric Bruneton
+ */
+public class InstructionAdapter extends MethodVisitor {
+
+    public final static Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;");
+
+    /**
+     * Creates a new {@link InstructionAdapter}. <i>Subclasses must not use this
+     * constructor</i>. Instead, they must use the
+     * {@link #InstructionAdapter(int, MethodVisitor)} version.
+     *
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     */
+    public InstructionAdapter(final MethodVisitor mv) {
+        this(Opcodes.ASM4, mv);
+    }
+
+    /**
+     * Creates a new {@link InstructionAdapter}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     */
+    protected InstructionAdapter(final int api, final MethodVisitor mv) {
+        super(api, mv);
+    }
+
+    @Override
+    public void visitInsn(final int opcode) {
+        switch (opcode) {
+        case Opcodes.NOP:
+            nop();
+            break;
+        case Opcodes.ACONST_NULL:
+            aconst(null);
+            break;
+        case Opcodes.ICONST_M1:
+        case Opcodes.ICONST_0:
+        case Opcodes.ICONST_1:
+        case Opcodes.ICONST_2:
+        case Opcodes.ICONST_3:
+        case Opcodes.ICONST_4:
+        case Opcodes.ICONST_5:
+            iconst(opcode - Opcodes.ICONST_0);
+            break;
+        case Opcodes.LCONST_0:
+        case Opcodes.LCONST_1:
+            lconst(opcode - Opcodes.LCONST_0);
+            break;
+        case Opcodes.FCONST_0:
+        case Opcodes.FCONST_1:
+        case Opcodes.FCONST_2:
+            fconst(opcode - Opcodes.FCONST_0);
+            break;
+        case Opcodes.DCONST_0:
+        case Opcodes.DCONST_1:
+            dconst(opcode - Opcodes.DCONST_0);
+            break;
+        case Opcodes.IALOAD:
+            aload(Type.INT_TYPE);
+            break;
+        case Opcodes.LALOAD:
+            aload(Type.LONG_TYPE);
+            break;
+        case Opcodes.FALOAD:
+            aload(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DALOAD:
+            aload(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.AALOAD:
+            aload(OBJECT_TYPE);
+            break;
+        case Opcodes.BALOAD:
+            aload(Type.BYTE_TYPE);
+            break;
+        case Opcodes.CALOAD:
+            aload(Type.CHAR_TYPE);
+            break;
+        case Opcodes.SALOAD:
+            aload(Type.SHORT_TYPE);
+            break;
+        case Opcodes.IASTORE:
+            astore(Type.INT_TYPE);
+            break;
+        case Opcodes.LASTORE:
+            astore(Type.LONG_TYPE);
+            break;
+        case Opcodes.FASTORE:
+            astore(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DASTORE:
+            astore(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.AASTORE:
+            astore(OBJECT_TYPE);
+            break;
+        case Opcodes.BASTORE:
+            astore(Type.BYTE_TYPE);
+            break;
+        case Opcodes.CASTORE:
+            astore(Type.CHAR_TYPE);
+            break;
+        case Opcodes.SASTORE:
+            astore(Type.SHORT_TYPE);
+            break;
+        case Opcodes.POP:
+            pop();
+            break;
+        case Opcodes.POP2:
+            pop2();
+            break;
+        case Opcodes.DUP:
+            dup();
+            break;
+        case Opcodes.DUP_X1:
+            dupX1();
+            break;
+        case Opcodes.DUP_X2:
+            dupX2();
+            break;
+        case Opcodes.DUP2:
+            dup2();
+            break;
+        case Opcodes.DUP2_X1:
+            dup2X1();
+            break;
+        case Opcodes.DUP2_X2:
+            dup2X2();
+            break;
+        case Opcodes.SWAP:
+            swap();
+            break;
+        case Opcodes.IADD:
+            add(Type.INT_TYPE);
+            break;
+        case Opcodes.LADD:
+            add(Type.LONG_TYPE);
+            break;
+        case Opcodes.FADD:
+            add(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DADD:
+            add(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.ISUB:
+            sub(Type.INT_TYPE);
+            break;
+        case Opcodes.LSUB:
+            sub(Type.LONG_TYPE);
+            break;
+        case Opcodes.FSUB:
+            sub(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DSUB:
+            sub(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.IMUL:
+            mul(Type.INT_TYPE);
+            break;
+        case Opcodes.LMUL:
+            mul(Type.LONG_TYPE);
+            break;
+        case Opcodes.FMUL:
+            mul(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DMUL:
+            mul(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.IDIV:
+            div(Type.INT_TYPE);
+            break;
+        case Opcodes.LDIV:
+            div(Type.LONG_TYPE);
+            break;
+        case Opcodes.FDIV:
+            div(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DDIV:
+            div(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.IREM:
+            rem(Type.INT_TYPE);
+            break;
+        case Opcodes.LREM:
+            rem(Type.LONG_TYPE);
+            break;
+        case Opcodes.FREM:
+            rem(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DREM:
+            rem(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.INEG:
+            neg(Type.INT_TYPE);
+            break;
+        case Opcodes.LNEG:
+            neg(Type.LONG_TYPE);
+            break;
+        case Opcodes.FNEG:
+            neg(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DNEG:
+            neg(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.ISHL:
+            shl(Type.INT_TYPE);
+            break;
+        case Opcodes.LSHL:
+            shl(Type.LONG_TYPE);
+            break;
+        case Opcodes.ISHR:
+            shr(Type.INT_TYPE);
+            break;
+        case Opcodes.LSHR:
+            shr(Type.LONG_TYPE);
+            break;
+        case Opcodes.IUSHR:
+            ushr(Type.INT_TYPE);
+            break;
+        case Opcodes.LUSHR:
+            ushr(Type.LONG_TYPE);
+            break;
+        case Opcodes.IAND:
+            and(Type.INT_TYPE);
+            break;
+        case Opcodes.LAND:
+            and(Type.LONG_TYPE);
+            break;
+        case Opcodes.IOR:
+            or(Type.INT_TYPE);
+            break;
+        case Opcodes.LOR:
+            or(Type.LONG_TYPE);
+            break;
+        case Opcodes.IXOR:
+            xor(Type.INT_TYPE);
+            break;
+        case Opcodes.LXOR:
+            xor(Type.LONG_TYPE);
+            break;
+        case Opcodes.I2L:
+            cast(Type.INT_TYPE, Type.LONG_TYPE);
+            break;
+        case Opcodes.I2F:
+            cast(Type.INT_TYPE, Type.FLOAT_TYPE);
+            break;
+        case Opcodes.I2D:
+            cast(Type.INT_TYPE, Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.L2I:
+            cast(Type.LONG_TYPE, Type.INT_TYPE);
+            break;
+        case Opcodes.L2F:
+            cast(Type.LONG_TYPE, Type.FLOAT_TYPE);
+            break;
+        case Opcodes.L2D:
+            cast(Type.LONG_TYPE, Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.F2I:
+            cast(Type.FLOAT_TYPE, Type.INT_TYPE);
+            break;
+        case Opcodes.F2L:
+            cast(Type.FLOAT_TYPE, Type.LONG_TYPE);
+            break;
+        case Opcodes.F2D:
+            cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.D2I:
+            cast(Type.DOUBLE_TYPE, Type.INT_TYPE);
+            break;
+        case Opcodes.D2L:
+            cast(Type.DOUBLE_TYPE, Type.LONG_TYPE);
+            break;
+        case Opcodes.D2F:
+            cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE);
+            break;
+        case Opcodes.I2B:
+            cast(Type.INT_TYPE, Type.BYTE_TYPE);
+            break;
+        case Opcodes.I2C:
+            cast(Type.INT_TYPE, Type.CHAR_TYPE);
+            break;
+        case Opcodes.I2S:
+            cast(Type.INT_TYPE, Type.SHORT_TYPE);
+            break;
+        case Opcodes.LCMP:
+            lcmp();
+            break;
+        case Opcodes.FCMPL:
+            cmpl(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.FCMPG:
+            cmpg(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DCMPL:
+            cmpl(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.DCMPG:
+            cmpg(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.IRETURN:
+            areturn(Type.INT_TYPE);
+            break;
+        case Opcodes.LRETURN:
+            areturn(Type.LONG_TYPE);
+            break;
+        case Opcodes.FRETURN:
+            areturn(Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DRETURN:
+            areturn(Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.ARETURN:
+            areturn(OBJECT_TYPE);
+            break;
+        case Opcodes.RETURN:
+            areturn(Type.VOID_TYPE);
+            break;
+        case Opcodes.ARRAYLENGTH:
+            arraylength();
+            break;
+        case Opcodes.ATHROW:
+            athrow();
+            break;
+        case Opcodes.MONITORENTER:
+            monitorenter();
+            break;
+        case Opcodes.MONITOREXIT:
+            monitorexit();
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitIntInsn(final int opcode, final int operand) {
+        switch (opcode) {
+        case Opcodes.BIPUSH:
+            iconst(operand);
+            break;
+        case Opcodes.SIPUSH:
+            iconst(operand);
+            break;
+        case Opcodes.NEWARRAY:
+            switch (operand) {
+            case Opcodes.T_BOOLEAN:
+                newarray(Type.BOOLEAN_TYPE);
+                break;
+            case Opcodes.T_CHAR:
+                newarray(Type.CHAR_TYPE);
+                break;
+            case Opcodes.T_BYTE:
+                newarray(Type.BYTE_TYPE);
+                break;
+            case Opcodes.T_SHORT:
+                newarray(Type.SHORT_TYPE);
+                break;
+            case Opcodes.T_INT:
+                newarray(Type.INT_TYPE);
+                break;
+            case Opcodes.T_FLOAT:
+                newarray(Type.FLOAT_TYPE);
+                break;
+            case Opcodes.T_LONG:
+                newarray(Type.LONG_TYPE);
+                break;
+            case Opcodes.T_DOUBLE:
+                newarray(Type.DOUBLE_TYPE);
+                break;
+            default:
+                throw new IllegalArgumentException();
+            }
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        switch (opcode) {
+        case Opcodes.ILOAD:
+            load(var, Type.INT_TYPE);
+            break;
+        case Opcodes.LLOAD:
+            load(var, Type.LONG_TYPE);
+            break;
+        case Opcodes.FLOAD:
+            load(var, Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DLOAD:
+            load(var, Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.ALOAD:
+            load(var, OBJECT_TYPE);
+            break;
+        case Opcodes.ISTORE:
+            store(var, Type.INT_TYPE);
+            break;
+        case Opcodes.LSTORE:
+            store(var, Type.LONG_TYPE);
+            break;
+        case Opcodes.FSTORE:
+            store(var, Type.FLOAT_TYPE);
+            break;
+        case Opcodes.DSTORE:
+            store(var, Type.DOUBLE_TYPE);
+            break;
+        case Opcodes.ASTORE:
+            store(var, OBJECT_TYPE);
+            break;
+        case Opcodes.RET:
+            ret(var);
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitTypeInsn(final int opcode, final String type) {
+        Type t = Type.getObjectType(type);
+        switch (opcode) {
+        case Opcodes.NEW:
+            anew(t);
+            break;
+        case Opcodes.ANEWARRAY:
+            newarray(t);
+            break;
+        case Opcodes.CHECKCAST:
+            checkcast(t);
+            break;
+        case Opcodes.INSTANCEOF:
+            instanceOf(t);
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitFieldInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        switch (opcode) {
+        case Opcodes.GETSTATIC:
+            getstatic(owner, name, desc);
+            break;
+        case Opcodes.PUTSTATIC:
+            putstatic(owner, name, desc);
+            break;
+        case Opcodes.GETFIELD:
+            getfield(owner, name, desc);
+            break;
+        case Opcodes.PUTFIELD:
+            putfield(owner, name, desc);
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitMethodInsn(final int opcode, final String owner,
+            final String name, final String desc) {
+        switch (opcode) {
+        case Opcodes.INVOKESPECIAL:
+            invokespecial(owner, name, desc);
+            break;
+        case Opcodes.INVOKEVIRTUAL:
+            invokevirtual(owner, name, desc);
+            break;
+        case Opcodes.INVOKESTATIC:
+            invokestatic(owner, name, desc);
+            break;
+        case Opcodes.INVOKEINTERFACE:
+            invokeinterface(owner, name, desc);
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
+        invokedynamic(name, desc, bsm, bsmArgs);
+    }
+
+    @Override
+    public void visitJumpInsn(final int opcode, final Label label) {
+        switch (opcode) {
+        case Opcodes.IFEQ:
+            ifeq(label);
+            break;
+        case Opcodes.IFNE:
+            ifne(label);
+            break;
+        case Opcodes.IFLT:
+            iflt(label);
+            break;
+        case Opcodes.IFGE:
+            ifge(label);
+            break;
+        case Opcodes.IFGT:
+            ifgt(label);
+            break;
+        case Opcodes.IFLE:
+            ifle(label);
+            break;
+        case Opcodes.IF_ICMPEQ:
+            ificmpeq(label);
+            break;
+        case Opcodes.IF_ICMPNE:
+            ificmpne(label);
+            break;
+        case Opcodes.IF_ICMPLT:
+            ificmplt(label);
+            break;
+        case Opcodes.IF_ICMPGE:
+            ificmpge(label);
+            break;
+        case Opcodes.IF_ICMPGT:
+            ificmpgt(label);
+            break;
+        case Opcodes.IF_ICMPLE:
+            ificmple(label);
+            break;
+        case Opcodes.IF_ACMPEQ:
+            ifacmpeq(label);
+            break;
+        case Opcodes.IF_ACMPNE:
+            ifacmpne(label);
+            break;
+        case Opcodes.GOTO:
+            goTo(label);
+            break;
+        case Opcodes.JSR:
+            jsr(label);
+            break;
+        case Opcodes.IFNULL:
+            ifnull(label);
+            break;
+        case Opcodes.IFNONNULL:
+            ifnonnull(label);
+            break;
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitLabel(final Label label) {
+        mark(label);
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        if (cst instanceof Integer) {
+            int val = ((Integer) cst).intValue();
+            iconst(val);
+        } else if (cst instanceof Byte) {
+            int val = ((Byte) cst).intValue();
+            iconst(val);
+        } else if (cst instanceof Character) {
+            int val = ((Character) cst).charValue();
+            iconst(val);
+        } else if (cst instanceof Short) {
+            int val = ((Short) cst).intValue();
+            iconst(val);
+        } else if (cst instanceof Boolean) {
+            int val = ((Boolean) cst).booleanValue() ? 1 : 0;
+            iconst(val);
+        } else if (cst instanceof Float) {
+            float val = ((Float) cst).floatValue();
+            fconst(val);
+        } else if (cst instanceof Long) {
+            long val = ((Long) cst).longValue();
+            lconst(val);
+        } else if (cst instanceof Double) {
+            double val = ((Double) cst).doubleValue();
+            dconst(val);
+        } else if (cst instanceof String) {
+            aconst(cst);
+        } else if (cst instanceof Type) {
+            tconst((Type) cst);
+        } else if (cst instanceof Handle) {
+            hconst((Handle) cst);
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void visitIincInsn(final int var, final int increment) {
+        iinc(var, increment);
+    }
+
+    @Override
+    public void visitTableSwitchInsn(final int min, final int max,
+            final Label dflt, final Label... labels) {
+        tableswitch(min, max, dflt, labels);
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(final Label dflt, final int[] keys,
+            final Label[] labels) {
+        lookupswitch(dflt, keys, labels);
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        multianewarray(desc, dims);
+    }
+
+    // -----------------------------------------------------------------------
+
+    public void nop() {
+        mv.visitInsn(Opcodes.NOP);
+    }
+
+    public void aconst(final Object cst) {
+        if (cst == null) {
+            mv.visitInsn(Opcodes.ACONST_NULL);
+        } else {
+            mv.visitLdcInsn(cst);
+        }
+    }
+
+    public void iconst(final int cst) {
+        if (cst >= -1 && cst <= 5) {
+            mv.visitInsn(Opcodes.ICONST_0 + cst);
+        } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) {
+            mv.visitIntInsn(Opcodes.BIPUSH, cst);
+        } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) {
+            mv.visitIntInsn(Opcodes.SIPUSH, cst);
+        } else {
+            mv.visitLdcInsn(new Integer(cst));
+        }
+    }
+
+    public void lconst(final long cst) {
+        if (cst == 0L || cst == 1L) {
+            mv.visitInsn(Opcodes.LCONST_0 + (int) cst);
+        } else {
+            mv.visitLdcInsn(new Long(cst));
+        }
+    }
+
+    public void fconst(final float cst) {
+        int bits = Float.floatToIntBits(cst);
+        if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2
+            mv.visitInsn(Opcodes.FCONST_0 + (int) cst);
+        } else {
+            mv.visitLdcInsn(new Float(cst));
+        }
+    }
+
+    public void dconst(final double cst) {
+        long bits = Double.doubleToLongBits(cst);
+        if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d
+            mv.visitInsn(Opcodes.DCONST_0 + (int) cst);
+        } else {
+            mv.visitLdcInsn(new Double(cst));
+        }
+    }
+
+    public void tconst(final Type type) {
+        mv.visitLdcInsn(type);
+    }
+
+    public void hconst(final Handle handle) {
+        mv.visitLdcInsn(handle);
+    }
+
+    public void load(final int var, final Type type) {
+        mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var);
+    }
+
+    public void aload(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IALOAD));
+    }
+
+    public void store(final int var, final Type type) {
+        mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), var);
+    }
+
+    public void astore(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IASTORE));
+    }
+
+    public void pop() {
+        mv.visitInsn(Opcodes.POP);
+    }
+
+    public void pop2() {
+        mv.visitInsn(Opcodes.POP2);
+    }
+
+    public void dup() {
+        mv.visitInsn(Opcodes.DUP);
+    }
+
+    public void dup2() {
+        mv.visitInsn(Opcodes.DUP2);
+    }
+
+    public void dupX1() {
+        mv.visitInsn(Opcodes.DUP_X1);
+    }
+
+    public void dupX2() {
+        mv.visitInsn(Opcodes.DUP_X2);
+    }
+
+    public void dup2X1() {
+        mv.visitInsn(Opcodes.DUP2_X1);
+    }
+
+    public void dup2X2() {
+        mv.visitInsn(Opcodes.DUP2_X2);
+    }
+
+    public void swap() {
+        mv.visitInsn(Opcodes.SWAP);
+    }
+
+    public void add(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IADD));
+    }
+
+    public void sub(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.ISUB));
+    }
+
+    public void mul(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IMUL));
+    }
+
+    public void div(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IDIV));
+    }
+
+    public void rem(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IREM));
+    }
+
+    public void neg(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.INEG));
+    }
+
+    public void shl(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.ISHL));
+    }
+
+    public void shr(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.ISHR));
+    }
+
+    public void ushr(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IUSHR));
+    }
+
+    public void and(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IAND));
+    }
+
+    public void or(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IOR));
+    }
+
+    public void xor(final Type type) {
+        mv.visitInsn(type.getOpcode(Opcodes.IXOR));
+    }
+
+    public void iinc(final int var, final int increment) {
+        mv.visitIincInsn(var, increment);
+    }
+
+    public void cast(final Type from, final Type to) {
+        if (from != to) {
+            if (from == Type.DOUBLE_TYPE) {
+                if (to == Type.FLOAT_TYPE) {
+                    mv.visitInsn(Opcodes.D2F);
+                } else if (to == Type.LONG_TYPE) {
+                    mv.visitInsn(Opcodes.D2L);
+                } else {
+                    mv.visitInsn(Opcodes.D2I);
+                    cast(Type.INT_TYPE, to);
+                }
+            } else if (from == Type.FLOAT_TYPE) {
+                if (to == Type.DOUBLE_TYPE) {
+                    mv.visitInsn(Opcodes.F2D);
+                } else if (to == Type.LONG_TYPE) {
+                    mv.visitInsn(Opcodes.F2L);
+                } else {
+                    mv.visitInsn(Opcodes.F2I);
+                    cast(Type.INT_TYPE, to);
+                }
+            } else if (from == Type.LONG_TYPE) {
+                if (to == Type.DOUBLE_TYPE) {
+                    mv.visitInsn(Opcodes.L2D);
+                } else if (to == Type.FLOAT_TYPE) {
+                    mv.visitInsn(Opcodes.L2F);
+                } else {
+                    mv.visitInsn(Opcodes.L2I);
+                    cast(Type.INT_TYPE, to);
+                }
+            } else {
+                if (to == Type.BYTE_TYPE) {
+                    mv.visitInsn(Opcodes.I2B);
+                } else if (to == Type.CHAR_TYPE) {
+                    mv.visitInsn(Opcodes.I2C);
+                } else if (to == Type.DOUBLE_TYPE) {
+                    mv.visitInsn(Opcodes.I2D);
+                } else if (to == Type.FLOAT_TYPE) {
+                    mv.visitInsn(Opcodes.I2F);
+                } else if (to == Type.LONG_TYPE) {
+                    mv.visitInsn(Opcodes.I2L);
+                } else if (to == Type.SHORT_TYPE) {
+                    mv.visitInsn(Opcodes.I2S);
+                }
+            }
+        }
+    }
+
+    public void lcmp() {
+        mv.visitInsn(Opcodes.LCMP);
+    }
+
+    public void cmpl(final Type type) {
+        mv.visitInsn(type == Type.FLOAT_TYPE ? Opcodes.FCMPL : Opcodes.DCMPL);
+    }
+
+    public void cmpg(final Type type) {
+        mv.visitInsn(type == Type.FLOAT_TYPE ? Opcodes.FCMPG : Opcodes.DCMPG);
+    }
+
+    public void ifeq(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFEQ, label);
+    }
+
+    public void ifne(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFNE, label);
+    }
+
+    public void iflt(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFLT, label);
+    }
+
+    public void ifge(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFGE, label);
+    }
+
+    public void ifgt(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFGT, label);
+    }
+
+    public void ifle(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFLE, label);
+    }
+
+    public void ificmpeq(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ICMPEQ, label);
+    }
+
+    public void ificmpne(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ICMPNE, label);
+    }
+
+    public void ificmplt(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ICMPLT, label);
+    }
+
+    public void ificmpge(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ICMPGE, label);
+    }
+
+    public void ificmpgt(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ICMPGT, label);
+    }
+
+    public void ificmple(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ICMPLE, label);
+    }
+
+    public void ifacmpeq(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label);
+    }
+
+    public void ifacmpne(final Label label) {
+        mv.visitJumpInsn(Opcodes.IF_ACMPNE, label);
+    }
+
+    public void goTo(final Label label) {
+        mv.visitJumpInsn(Opcodes.GOTO, label);
+    }
+
+    public void jsr(final Label label) {
+        mv.visitJumpInsn(Opcodes.JSR, label);
+    }
+
+    public void ret(final int var) {
+        mv.visitVarInsn(Opcodes.RET, var);
+    }
+
+    public void tableswitch(final int min, final int max, final Label dflt,
+            final Label... labels) {
+        mv.visitTableSwitchInsn(min, max, dflt, labels);
+    }
+
+    public void lookupswitch(final Label dflt, final int[] keys,
+            final Label[] labels) {
+        mv.visitLookupSwitchInsn(dflt, keys, labels);
+    }
+
+    public void areturn(final Type t) {
+        mv.visitInsn(t.getOpcode(Opcodes.IRETURN));
+    }
+
+    public void getstatic(final String owner, final String name,
+            final String desc) {
+        mv.visitFieldInsn(Opcodes.GETSTATIC, owner, name, desc);
+    }
+
+    public void putstatic(final String owner, final String name,
+            final String desc) {
+        mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, name, desc);
+    }
+
+    public void getfield(final String owner, final String name,
+            final String desc) {
+        mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc);
+    }
+
+    public void putfield(final String owner, final String name,
+            final String desc) {
+        mv.visitFieldInsn(Opcodes.PUTFIELD, owner, name, desc);
+    }
+
+    public void invokevirtual(final String owner, final String name,
+            final String desc) {
+        mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc);
+    }
+
+    public void invokespecial(final String owner, final String name,
+            final String desc) {
+        mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc);
+    }
+
+    public void invokestatic(final String owner, final String name,
+            final String desc) {
+        mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc);
+    }
+
+    public void invokeinterface(final String owner, final String name,
+            final String desc) {
+        mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc);
+    }
+
+    public void invokedynamic(String name, String desc, Handle bsm,
+            Object[] bsmArgs) {
+        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+    }
+
+    public void anew(final Type type) {
+        mv.visitTypeInsn(Opcodes.NEW, type.getInternalName());
+    }
+
+    public void newarray(final Type type) {
+        int typ;
+        switch (type.getSort()) {
+        case Type.BOOLEAN:
+            typ = Opcodes.T_BOOLEAN;
+            break;
+        case Type.CHAR:
+            typ = Opcodes.T_CHAR;
+            break;
+        case Type.BYTE:
+            typ = Opcodes.T_BYTE;
+            break;
+        case Type.SHORT:
+            typ = Opcodes.T_SHORT;
+            break;
+        case Type.INT:
+            typ = Opcodes.T_INT;
+            break;
+        case Type.FLOAT:
+            typ = Opcodes.T_FLOAT;
+            break;
+        case Type.LONG:
+            typ = Opcodes.T_LONG;
+            break;
+        case Type.DOUBLE:
+            typ = Opcodes.T_DOUBLE;
+            break;
+        default:
+            mv.visitTypeInsn(Opcodes.ANEWARRAY, type.getInternalName());
+            return;
+        }
+        mv.visitIntInsn(Opcodes.NEWARRAY, typ);
+    }
+
+    public void arraylength() {
+        mv.visitInsn(Opcodes.ARRAYLENGTH);
+    }
+
+    public void athrow() {
+        mv.visitInsn(Opcodes.ATHROW);
+    }
+
+    public void checkcast(final Type type) {
+        mv.visitTypeInsn(Opcodes.CHECKCAST, type.getInternalName());
+    }
+
+    public void instanceOf(final Type type) {
+        mv.visitTypeInsn(Opcodes.INSTANCEOF, type.getInternalName());
+    }
+
+    public void monitorenter() {
+        mv.visitInsn(Opcodes.MONITORENTER);
+    }
+
+    public void monitorexit() {
+        mv.visitInsn(Opcodes.MONITOREXIT);
+    }
+
+    public void multianewarray(final String desc, final int dims) {
+        mv.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    public void ifnull(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFNULL, label);
+    }
+
+    public void ifnonnull(final Label label) {
+        mv.visitJumpInsn(Opcodes.IFNONNULL, label);
+    }
+
+    public void mark(final Label label) {
+        mv.visitLabel(label);
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/LocalVariablesSorter.java b/src/jvm/clojure/asm/commons/LocalVariablesSorter.java
new file mode 100644
index 0000000..85f23ba
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/LocalVariablesSorter.java
@@ -0,0 +1,360 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import clojure.asm.Label;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+import clojure.asm.Type;
+
+/**
+ * A {@link MethodVisitor} that renumbers local variables in their order of
+ * appearance. This adapter allows one to easily add new local variables to a
+ * method. It may be used by inheriting from this class, but the preferred way
+ * of using it is via delegation: the next visitor in the chain can indeed add
+ * new locals when needed by calling {@link #newLocal} on this adapter (this
+ * requires a reference back to this {@link LocalVariablesSorter}).
+ *
+ * @author Chris Nokleberg
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public class LocalVariablesSorter extends MethodVisitor {
+
+    private static final Type OBJECT_TYPE = Type
+            .getObjectType("java/lang/Object");
+
+    /**
+     * Mapping from old to new local variable indexes. A local variable at index
+     * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
+     * index i of size 2 is remapped to 'mapping[2*i+1]'.
+     */
+    private int[] mapping = new int[40];
+
+    /**
+     * Array used to store stack map local variable types after remapping.
+     */
+    private Object[] newLocals = new Object[20];
+
+    /**
+     * Index of the first local variable, after formal parameters.
+     */
+    protected final int firstLocal;
+
+    /**
+     * Index of the next local variable to be created by {@link #newLocal}.
+     */
+    protected int nextLocal;
+
+    /**
+     * Indicates if at least one local variable has moved due to remapping.
+     */
+    private boolean changed;
+
+    /**
+     * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use
+     * this constructor</i>. Instead, they must use the
+     * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version.
+     *
+     * @param access
+     *            access flags of the adapted method.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     */
+    public LocalVariablesSorter(final int access, final String desc,
+            final MethodVisitor mv) {
+        this(Opcodes.ASM4, access, desc, mv);
+    }
+
+    /**
+     * Creates a new {@link LocalVariablesSorter}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param access
+     *            access flags of the adapted method.
+     * @param desc
+     *            the method's descriptor (see {@link Type Type}).
+     * @param mv
+     *            the method visitor to which this adapter delegates calls.
+     */
+    protected LocalVariablesSorter(final int api, final int access,
+            final String desc, final MethodVisitor mv) {
+        super(api, mv);
+        Type[] args = Type.getArgumentTypes(desc);
+        nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
+        for (int i = 0; i < args.length; i++) {
+            nextLocal += args[i].getSize();
+        }
+        firstLocal = nextLocal;
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        Type type;
+        switch (opcode) {
+        case Opcodes.LLOAD:
+        case Opcodes.LSTORE:
+            type = Type.LONG_TYPE;
+            break;
+
+        case Opcodes.DLOAD:
+        case Opcodes.DSTORE:
+            type = Type.DOUBLE_TYPE;
+            break;
+
+        case Opcodes.FLOAD:
+        case Opcodes.FSTORE:
+            type = Type.FLOAT_TYPE;
+            break;
+
+        case Opcodes.ILOAD:
+        case Opcodes.ISTORE:
+            type = Type.INT_TYPE;
+            break;
+
+        default:
+            // case Opcodes.ALOAD:
+            // case Opcodes.ASTORE:
+            // case RET:
+            type = OBJECT_TYPE;
+            break;
+        }
+        mv.visitVarInsn(opcode, remap(var, type));
+    }
+
+    @Override
+    public void visitIincInsn(final int var, final int increment) {
+        mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
+    }
+
+    @Override
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        mv.visitMaxs(maxStack, nextLocal);
+    }
+
+    @Override
+    public void visitLocalVariable(final String name, final String desc,
+            final String signature, final Label start, final Label end,
+            final int index) {
+        int newIndex = remap(index, Type.getType(desc));
+        mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
+    }
+
+    @Override
+    public void visitFrame(final int type, final int nLocal,
+            final Object[] local, final int nStack, final Object[] stack) {
+        if (type != Opcodes.F_NEW) { // uncompressed frame
+            throw new IllegalStateException(
+                    "ClassReader.accept() should be called with EXPAND_FRAMES flag");
+        }
+
+        if (!changed) { // optimization for the case where mapping = identity
+            mv.visitFrame(type, nLocal, local, nStack, stack);
+            return;
+        }
+
+        // creates a copy of newLocals
+        Object[] oldLocals = new Object[newLocals.length];
+        System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
+
+        updateNewLocals(newLocals);
+
+        // copies types from 'local' to 'newLocals'
+        // 'newLocals' already contains the variables added with 'newLocal'
+
+        int index = 0; // old local variable index
+        int number = 0; // old local variable number
+        for (; number < nLocal; ++number) {
+            Object t = local[number];
+            int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
+            if (t != Opcodes.TOP) {
+                Type typ = OBJECT_TYPE;
+                if (t == Opcodes.INTEGER) {
+                    typ = Type.INT_TYPE;
+                } else if (t == Opcodes.FLOAT) {
+                    typ = Type.FLOAT_TYPE;
+                } else if (t == Opcodes.LONG) {
+                    typ = Type.LONG_TYPE;
+                } else if (t == Opcodes.DOUBLE) {
+                    typ = Type.DOUBLE_TYPE;
+                } else if (t instanceof String) {
+                    typ = Type.getObjectType((String) t);
+                }
+                setFrameLocal(remap(index, typ), t);
+            }
+            index += size;
+        }
+
+        // removes TOP after long and double types as well as trailing TOPs
+
+        index = 0;
+        number = 0;
+        for (int i = 0; index < newLocals.length; ++i) {
+            Object t = newLocals[index++];
+            if (t != null && t != Opcodes.TOP) {
+                newLocals[i] = t;
+                number = i + 1;
+                if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {
+                    index += 1;
+                }
+            } else {
+                newLocals[i] = Opcodes.TOP;
+            }
+        }
+
+        // visits remapped frame
+        mv.visitFrame(type, number, newLocals, nStack, stack);
+
+        // restores original value of 'newLocals'
+        newLocals = oldLocals;
+    }
+
+    // -------------
+
+    /**
+     * Creates a new local variable of the given type.
+     *
+     * @param type
+     *            the type of the local variable to be created.
+     * @return the identifier of the newly created local variable.
+     */
+    public int newLocal(final Type type) {
+        Object t;
+        switch (type.getSort()) {
+        case Type.BOOLEAN:
+        case Type.CHAR:
+        case Type.BYTE:
+        case Type.SHORT:
+        case Type.INT:
+            t = Opcodes.INTEGER;
+            break;
+        case Type.FLOAT:
+            t = Opcodes.FLOAT;
+            break;
+        case Type.LONG:
+            t = Opcodes.LONG;
+            break;
+        case Type.DOUBLE:
+            t = Opcodes.DOUBLE;
+            break;
+        case Type.ARRAY:
+            t = type.getDescriptor();
+            break;
+        // case Type.OBJECT:
+        default:
+            t = type.getInternalName();
+            break;
+        }
+        int local = newLocalMapping(type);
+        setLocalType(local, type);
+        setFrameLocal(local, t);
+        return local;
+    }
+
+    /**
+     * Notifies subclasses that a new stack map frame is being visited. The
+     * array argument contains the stack map frame types corresponding to the
+     * local variables added with {@link #newLocal}. This method can update
+     * these types in place for the stack map frame being visited. The default
+     * implementation of this method does nothing, i.e. a local variable added
+     * with {@link #newLocal} will have the same type in all stack map frames.
+     * But this behavior is not always the desired one, for instance if a local
+     * variable is added in the middle of a try/catch block: the frame for the
+     * exception handler should have a TOP type for this new local.
+     *
+     * @param newLocals
+     *            the stack map frame types corresponding to the local variables
+     *            added with {@link #newLocal} (and null for the others). The
+     *            format of this array is the same as in
+     *            {@link MethodVisitor#visitFrame}, except that long and double
+     *            types use two slots. The types for the current stack map frame
+     *            must be updated in place in this array.
+     */
+    protected void updateNewLocals(Object[] newLocals) {
+    }
+
+    /**
+     * Notifies subclasses that a local variable has been added or remapped. The
+     * default implementation of this method does nothing.
+     *
+     * @param local
+     *            a local variable identifier, as returned by {@link #newLocal
+     *            newLocal()}.
+     * @param type
+     *            the type of the value being stored in the local variable.
+     */
+    protected void setLocalType(final int local, final Type type) {
+    }
+
+    private void setFrameLocal(final int local, final Object type) {
+        int l = newLocals.length;
+        if (local >= l) {
+            Object[] a = new Object[Math.max(2 * l, local + 1)];
+            System.arraycopy(newLocals, 0, a, 0, l);
+            newLocals = a;
+        }
+        newLocals[local] = type;
+    }
+
+    private int remap(final int var, final Type type) {
+        if (var + type.getSize() <= firstLocal) {
+            return var;
+        }
+        int key = 2 * var + type.getSize() - 1;
+        int size = mapping.length;
+        if (key >= size) {
+            int[] newMapping = new int[Math.max(2 * size, key + 1)];
+            System.arraycopy(mapping, 0, newMapping, 0, size);
+            mapping = newMapping;
+        }
+        int value = mapping[key];
+        if (value == 0) {
+            value = newLocalMapping(type);
+            setLocalType(value, type);
+            mapping[key] = value + 1;
+        } else {
+            value--;
+        }
+        if (value != var) {
+            changed = true;
+        }
+        return value;
+    }
+
+    protected int newLocalMapping(final Type type) {
+        int local = nextLocal;
+        nextLocal += type.getSize();
+        return local;
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/Method.java b/src/jvm/clojure/asm/commons/Method.java
new file mode 100644
index 0000000..ee16c93
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/Method.java
@@ -0,0 +1,282 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import clojure.asm.Type;
+
+/**
+ * A named method descriptor.
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class Method {
+
+    /**
+     * The method name.
+     */
+    private final String name;
+
+    /**
+     * The method descriptor.
+     */
+    private final String desc;
+
+    /**
+     * Maps primitive Java type names to their descriptors.
+     */
+    private static final Map<String, String> DESCRIPTORS;
+
+    static {
+        DESCRIPTORS = new HashMap<String, String>();
+        DESCRIPTORS.put("void", "V");
+        DESCRIPTORS.put("byte", "B");
+        DESCRIPTORS.put("char", "C");
+        DESCRIPTORS.put("double", "D");
+        DESCRIPTORS.put("float", "F");
+        DESCRIPTORS.put("int", "I");
+        DESCRIPTORS.put("long", "J");
+        DESCRIPTORS.put("short", "S");
+        DESCRIPTORS.put("boolean", "Z");
+    }
+
+    /**
+     * Creates a new {@link Method}.
+     *
+     * @param name
+     *            the method's name.
+     * @param desc
+     *            the method's descriptor.
+     */
+    public Method(final String name, final String desc) {
+        this.name = name;
+        this.desc = desc;
+    }
+
+    /**
+     * Creates a new {@link Method}.
+     *
+     * @param name
+     *            the method's name.
+     * @param returnType
+     *            the method's return type.
+     * @param argumentTypes
+     *            the method's argument types.
+     */
+    public Method(final String name, final Type returnType,
+            final Type[] argumentTypes) {
+        this(name, Type.getMethodDescriptor(returnType, argumentTypes));
+    }
+
+    /**
+     * Creates a new {@link Method}.
+     *
+     * @param m
+     *            a java.lang.reflect method descriptor
+     * @return a {@link Method} corresponding to the given Java method
+     *         declaration.
+     */
+    public static Method getMethod(java.lang.reflect.Method m) {
+        return new Method(m.getName(), Type.getMethodDescriptor(m));
+    }
+
+    /**
+     * Creates a new {@link Method}.
+     *
+     * @param c
+     *            a java.lang.reflect constructor descriptor
+     * @return a {@link Method} corresponding to the given Java constructor
+     *         declaration.
+     */
+    public static Method getMethod(java.lang.reflect.Constructor<?> c) {
+        return new Method("<init>", Type.getConstructorDescriptor(c));
+    }
+
+    /**
+     * Returns a {@link Method} corresponding to the given Java method
+     * declaration.
+     *
+     * @param method
+     *            a Java method declaration, without argument names, of the form
+     *            "returnType name (argumentType1, ... argumentTypeN)", where
+     *            the types are in plain Java (e.g. "int", "float",
+     *            "java.util.List", ...). Classes of the java.lang package can
+     *            be specified by their unqualified name; all other classes
+     *            names must be fully qualified.
+     * @return a {@link Method} corresponding to the given Java method
+     *         declaration.
+     * @throws IllegalArgumentException
+     *             if <code>method</code> could not get parsed.
+     */
+    public static Method getMethod(final String method)
+            throws IllegalArgumentException {
+        return getMethod(method, false);
+    }
+
+    /**
+     * Returns a {@link Method} corresponding to the given Java method
+     * declaration.
+     *
+     * @param method
+     *            a Java method declaration, without argument names, of the form
+     *            "returnType name (argumentType1, ... argumentTypeN)", where
+     *            the types are in plain Java (e.g. "int", "float",
+     *            "java.util.List", ...). Classes of the java.lang package may
+     *            be specified by their unqualified name, depending on the
+     *            defaultPackage argument; all other classes names must be fully
+     *            qualified.
+     * @param defaultPackage
+     *            true if unqualified class names belong to the default package,
+     *            or false if they correspond to java.lang classes. For instance
+     *            "Object" means "Object" if this option is true, or
+     *            "java.lang.Object" otherwise.
+     * @return a {@link Method} corresponding to the given Java method
+     *         declaration.
+     * @throws IllegalArgumentException
+     *             if <code>method</code> could not get parsed.
+     */
+    public static Method getMethod(final String method,
+            final boolean defaultPackage) throws IllegalArgumentException {
+        int space = method.indexOf(' ');
+        int start = method.indexOf('(', space) + 1;
+        int end = method.indexOf(')', start);
+        if (space == -1 || start == -1 || end == -1) {
+            throw new IllegalArgumentException();
+        }
+        String returnType = method.substring(0, space);
+        String methodName = method.substring(space + 1, start - 1).trim();
+        StringBuffer sb = new StringBuffer();
+        sb.append('(');
+        int p;
+        do {
+            String s;
+            p = method.indexOf(',', start);
+            if (p == -1) {
+                s = map(method.substring(start, end).trim(), defaultPackage);
+            } else {
+                s = map(method.substring(start, p).trim(), defaultPackage);
+                start = p + 1;
+            }
+            sb.append(s);
+        } while (p != -1);
+        sb.append(')');
+        sb.append(map(returnType, defaultPackage));
+        return new Method(methodName, sb.toString());
+    }
+
+    private static String map(final String type, final boolean defaultPackage) {
+        if ("".equals(type)) {
+            return type;
+        }
+
+        StringBuffer sb = new StringBuffer();
+        int index = 0;
+        while ((index = type.indexOf("[]", index) + 1) > 0) {
+            sb.append('[');
+        }
+
+        String t = type.substring(0, type.length() - sb.length() * 2);
+        String desc = DESCRIPTORS.get(t);
+        if (desc != null) {
+            sb.append(desc);
+        } else {
+            sb.append('L');
+            if (t.indexOf('.') < 0) {
+                if (!defaultPackage) {
+                    sb.append("java/lang/");
+                }
+                sb.append(t);
+            } else {
+                sb.append(t.replace('.', '/'));
+            }
+            sb.append(';');
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Returns the name of the method described by this object.
+     *
+     * @return the name of the method described by this object.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Returns the descriptor of the method described by this object.
+     *
+     * @return the descriptor of the method described by this object.
+     */
+    public String getDescriptor() {
+        return desc;
+    }
+
+    /**
+     * Returns the return type of the method described by this object.
+     *
+     * @return the return type of the method described by this object.
+     */
+    public Type getReturnType() {
+        return Type.getReturnType(desc);
+    }
+
+    /**
+     * Returns the argument types of the method described by this object.
+     *
+     * @return the argument types of the method described by this object.
+     */
+    public Type[] getArgumentTypes() {
+        return Type.getArgumentTypes(desc);
+    }
+
+    @Override
+    public String toString() {
+        return name + desc;
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (!(o instanceof Method)) {
+            return false;
+        }
+        Method other = (Method) o;
+        return name.equals(other.name) && desc.equals(other.desc);
+    }
+
+    @Override
+    public int hashCode() {
+        return name.hashCode() ^ desc.hashCode();
+    }
+}
\ No newline at end of file
diff --git a/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java b/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java
new file mode 100644
index 0000000..65dd583
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/SerialVersionUIDAdder.java
@@ -0,0 +1,533 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import clojure.asm.ClassVisitor;
+import clojure.asm.FieldVisitor;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+
+/**
+ * A {@link ClassVisitor} that adds a serial version unique identifier to a
+ * class if missing. Here is typical usage of this class:
+ *
+ * <pre>
+ *   ClassWriter cw = new ClassWriter(...);
+ *   ClassVisitor sv = new SerialVersionUIDAdder(cw);
+ *   ClassVisitor ca = new MyClassAdapter(sv);
+ *   new ClassReader(orginalClass).accept(ca, false);
+ * </pre>
+ *
+ * The SVUID algorithm can be found <a href=
+ * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html"
+ * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>:
+ *
+ * <pre>
+ * The serialVersionUID is computed using the signature of a stream of bytes
+ * that reflect the class definition. The National Institute of Standards and
+ * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a
+ * signature for the stream. The first two 32-bit quantities are used to form a
+ * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data
+ * types to a sequence of bytes. The values input to the stream are defined by
+ * the Java Virtual Machine (VM) specification for classes.
+ *
+ * The sequence of items in the stream is as follows:
+ *
+ * 1. The class name written using UTF encoding.
+ * 2. The class modifiers written as a 32-bit integer.
+ * 3. The name of each interface sorted by name written using UTF encoding.
+ * 4. For each field of the class sorted by field name (except private static
+ * and private transient fields):
+ * 1. The name of the field in UTF encoding.
+ * 2. The modifiers of the field written as a 32-bit integer.
+ * 3. The descriptor of the field in UTF encoding
+ * 5. If a class initializer exists, write out the following:
+ * 1. The name of the method, <clinit>, in UTF encoding.
+ * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC,
+ * written as a 32-bit integer.
+ * 3. The descriptor of the method, ()V, in UTF encoding.
+ * 6. For each non-private constructor sorted by method name and signature:
+ * 1. The name of the method, <init>, in UTF encoding.
+ * 2. The modifiers of the method written as a 32-bit integer.
+ * 3. The descriptor of the method in UTF encoding.
+ * 7. For each non-private method sorted by method name and signature:
+ * 1. The name of the method in UTF encoding.
+ * 2. The modifiers of the method written as a 32-bit integer.
+ * 3. The descriptor of the method in UTF encoding.
+ * 8. The SHA-1 algorithm is executed on the stream of bytes produced by
+ * DataOutputStream and produces five 32-bit values sha[0..4].
+ *
+ * 9. The hash value is assembled from the first and second 32-bit values of
+ * the SHA-1 message digest. If the result of the message digest, the five
+ * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named
+ * sha, the hash value would be computed as follows:
+ *
+ * long hash = ((sha[0] >>> 24) & 0xFF) |
+ * ((sha[0] >>> 16) & 0xFF) << 8 |
+ * ((sha[0] >>> 8) & 0xFF) << 16 |
+ * ((sha[0] >>> 0) & 0xFF) << 24 |
+ * ((sha[1] >>> 24) & 0xFF) << 32 |
+ * ((sha[1] >>> 16) & 0xFF) << 40 |
+ * ((sha[1] >>> 8) & 0xFF) << 48 |
+ * ((sha[1] >>> 0) & 0xFF) << 56;
+ * </pre>
+ *
+ * @author Rajendra Inamdar, Vishal Vishnoi
+ */
+public class SerialVersionUIDAdder extends ClassVisitor {
+
+    /**
+     * Flag that indicates if we need to compute SVUID.
+     */
+    private boolean computeSVUID;
+
+    /**
+     * Set to true if the class already has SVUID.
+     */
+    private boolean hasSVUID;
+
+    /**
+     * Classes access flags.
+     */
+    private int access;
+
+    /**
+     * Internal name of the class
+     */
+    private String name;
+
+    /**
+     * Interfaces implemented by the class.
+     */
+    private String[] interfaces;
+
+    /**
+     * Collection of fields. (except private static and private transient
+     * fields)
+     */
+    private Collection<Item> svuidFields;
+
+    /**
+     * Set to true if the class has static initializer.
+     */
+    private boolean hasStaticInitializer;
+
+    /**
+     * Collection of non-private constructors.
+     */
+    private Collection<Item> svuidConstructors;
+
+    /**
+     * Collection of non-private methods.
+     */
+    private Collection<Item> svuidMethods;
+
+    /**
+     * Creates a new {@link SerialVersionUIDAdder}. <i>Subclasses must not use
+     * this constructor</i>. Instead, they must use the
+     * {@link #SerialVersionUIDAdder(int, ClassVisitor)} version.
+     *
+     * @param cv
+     *            a {@link ClassVisitor} to which this visitor will delegate
+     *            calls.
+     */
+    public SerialVersionUIDAdder(final ClassVisitor cv) {
+        this(Opcodes.ASM4, cv);
+    }
+
+    /**
+     * Creates a new {@link SerialVersionUIDAdder}.
+     *
+     * @param api
+     *            the ASM API version implemented by this visitor. Must be one
+     *            of {@link Opcodes#ASM4}.
+     * @param cv
+     *            a {@link ClassVisitor} to which this visitor will delegate
+     *            calls.
+     */
+    protected SerialVersionUIDAdder(final int api, final ClassVisitor cv) {
+        super(api, cv);
+        svuidFields = new ArrayList<Item>();
+        svuidConstructors = new ArrayList<Item>();
+        svuidMethods = new ArrayList<Item>();
+    }
+
+    // ------------------------------------------------------------------------
+    // Overriden methods
+    // ------------------------------------------------------------------------
+
+    /*
+     * Visit class header and get class name, access , and interfaces
+     * information (step 1,2, and 3) for SVUID computation.
+     */
+    @Override
+    public void visit(final int version, final int access, final String name,
+            final String signature, final String superName,
+            final String[] interfaces) {
+        computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0;
+
+        if (computeSVUID) {
+            this.name = name;
+            this.access = access;
+            this.interfaces = interfaces;
+        }
+
+        super.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    /*
+     * Visit the methods and get constructor and method information (step 5 and
+     * 7). Also determine if there is a class initializer (step 6).
+     */
+    @Override
+    public MethodVisitor visitMethod(final int access, final String name,
+            final String desc, final String signature, final String[] exceptions) {
+        if (computeSVUID) {
+            if ("<clinit>".equals(name)) {
+                hasStaticInitializer = true;
+            }
+            /*
+             * Remembers non private constructors and methods for SVUID
+             * computation For constructor and method modifiers, only the
+             * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL,
+             * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags
+             * are used.
+             */
+            int mods = access
+                    & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
+                            | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
+                            | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED
+                            | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT);
+
+            // all non private methods
+            if ((access & Opcodes.ACC_PRIVATE) == 0) {
+                if ("<init>".equals(name)) {
+                    svuidConstructors.add(new Item(name, mods, desc));
+                } else if (!"<clinit>".equals(name)) {
+                    svuidMethods.add(new Item(name, mods, desc));
+                }
+            }
+        }
+
+        return super.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    /*
+     * Gets class field information for step 4 of the algorithm. Also determines
+     * if the class already has a SVUID.
+     */
+    @Override
+    public FieldVisitor visitField(final int access, final String name,
+            final String desc, final String signature, final Object value) {
+        if (computeSVUID) {
+            if ("serialVersionUID".equals(name)) {
+                // since the class already has SVUID, we won't be computing it.
+                computeSVUID = false;
+                hasSVUID = true;
+            }
+            /*
+             * Remember field for SVUID computation For field modifiers, only
+             * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC,
+             * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when
+             * computing serialVersionUID values.
+             */
+            if ((access & Opcodes.ACC_PRIVATE) == 0
+                    || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) {
+                int mods = access
+                        & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
+                                | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
+                                | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
+                svuidFields.add(new Item(name, mods, desc));
+            }
+        }
+
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+    /**
+     * Handle a bizarre special case. Nested classes (static classes declared
+     * inside another class) that are protected have their access bit set to
+     * public in their class files to deal with some odd reflection situation.
+     * Our SVUID computation must do as the JVM does and ignore access bits in
+     * the class file in favor of the access bits InnerClass attribute.
+     */
+    @Override
+    public void visitInnerClass(final String aname, final String outerName,
+            final String innerName, final int attr_access) {
+        if ((name != null) && name.equals(aname)) {
+            this.access = attr_access;
+        }
+        super.visitInnerClass(aname, outerName, innerName, attr_access);
+    }
+
+    /*
+     * Add the SVUID if class doesn't have one
+     */
+    @Override
+    public void visitEnd() {
+        // compute SVUID and add it to the class
+        if (computeSVUID && !hasSVUID) {
+            try {
+                addSVUID(computeSVUID());
+            } catch (Throwable e) {
+                throw new RuntimeException("Error while computing SVUID for "
+                        + name, e);
+            }
+        }
+
+        super.visitEnd();
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns true if the class already has a SVUID field. The result of this
+     * method is only valid when visitEnd is or has been called.
+     *
+     * @return true if the class already has a SVUID field.
+     */
+    public boolean hasSVUID() {
+        return hasSVUID;
+    }
+
+    protected void addSVUID(long svuid) {
+        FieldVisitor fv = super.visitField(Opcodes.ACC_FINAL
+                + Opcodes.ACC_STATIC, "serialVersionUID", "J", null, new Long(
+                svuid));
+        if (fv != null) {
+            fv.visitEnd();
+        }
+    }
+
+    /**
+     * Computes and returns the value of SVUID.
+     *
+     * @return Returns the serial version UID
+     * @throws IOException
+     *             if an I/O error occurs
+     */
+    protected long computeSVUID() throws IOException {
+        ByteArrayOutputStream bos;
+        DataOutputStream dos = null;
+        long svuid = 0;
+
+        try {
+            bos = new ByteArrayOutputStream();
+            dos = new DataOutputStream(bos);
+
+            /*
+             * 1. The class name written using UTF encoding.
+             */
+            dos.writeUTF(name.replace('/', '.'));
+
+            /*
+             * 2. The class modifiers written as a 32-bit integer.
+             */
+            dos.writeInt(access
+                    & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
+                            | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT));
+
+            /*
+             * 3. The name of each interface sorted by name written using UTF
+             * encoding.
+             */
+            Arrays.sort(interfaces);
+            for (int i = 0; i < interfaces.length; i++) {
+                dos.writeUTF(interfaces[i].replace('/', '.'));
+            }
+
+            /*
+             * 4. For each field of the class sorted by field name (except
+             * private static and private transient fields):
+             *
+             * 1. The name of the field in UTF encoding. 2. The modifiers of the
+             * field written as a 32-bit integer. 3. The descriptor of the field
+             * in UTF encoding
+             *
+             * Note that field signatures are not dot separated. Method and
+             * constructor signatures are dot separated. Go figure...
+             */
+            writeItems(svuidFields, dos, false);
+
+            /*
+             * 5. If a class initializer exists, write out the following: 1. The
+             * name of the method, <clinit>, in UTF encoding. 2. The modifier of
+             * the method, java.lang.reflect.Modifier.STATIC, written as a
+             * 32-bit integer. 3. The descriptor of the method, ()V, in UTF
+             * encoding.
+             */
+            if (hasStaticInitializer) {
+                dos.writeUTF("<clinit>");
+                dos.writeInt(Opcodes.ACC_STATIC);
+                dos.writeUTF("()V");
+            } // if..
+
+            /*
+             * 6. For each non-private constructor sorted by method name and
+             * signature: 1. The name of the method, <init>, in UTF encoding. 2.
+             * The modifiers of the method written as a 32-bit integer. 3. The
+             * descriptor of the method in UTF encoding.
+             */
+            writeItems(svuidConstructors, dos, true);
+
+            /*
+             * 7. For each non-private method sorted by method name and
+             * signature: 1. The name of the method in UTF encoding. 2. The
+             * modifiers of the method written as a 32-bit integer. 3. The
+             * descriptor of the method in UTF encoding.
+             */
+            writeItems(svuidMethods, dos, true);
+
+            dos.flush();
+
+            /*
+             * 8. The SHA-1 algorithm is executed on the stream of bytes
+             * produced by DataOutputStream and produces five 32-bit values
+             * sha[0..4].
+             */
+            byte[] hashBytes = computeSHAdigest(bos.toByteArray());
+
+            /*
+             * 9. The hash value is assembled from the first and second 32-bit
+             * values of the SHA-1 message digest. If the result of the message
+             * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of
+             * five int values named sha, the hash value would be computed as
+             * follows:
+             *
+             * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF)
+             * << 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) <<
+             * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) <<
+             * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) <<
+             * 56;
+             */
+            for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) {
+                svuid = (svuid << 8) | (hashBytes[i] & 0xFF);
+            }
+        } finally {
+            // close the stream (if open)
+            if (dos != null) {
+                dos.close();
+            }
+        }
+
+        return svuid;
+    }
+
+    /**
+     * Returns the SHA-1 message digest of the given value.
+     *
+     * @param value
+     *            the value whose SHA message digest must be computed.
+     * @return the SHA-1 message digest of the given value.
+     */
+    protected byte[] computeSHAdigest(final byte[] value) {
+        try {
+            return MessageDigest.getInstance("SHA").digest(value);
+        } catch (Exception e) {
+            throw new UnsupportedOperationException(e.toString());
+        }
+    }
+
+    /**
+     * Sorts the items in the collection and writes it to the data output stream
+     *
+     * @param itemCollection
+     *            collection of items
+     * @param dos
+     *            a <code>DataOutputStream</code> value
+     * @param dotted
+     *            a <code>boolean</code> value
+     * @exception IOException
+     *                if an error occurs
+     */
+    private static void writeItems(final Collection<Item> itemCollection,
+            final DataOutput dos, final boolean dotted) throws IOException {
+        int size = itemCollection.size();
+        Item[] items = itemCollection.toArray(new Item[size]);
+        Arrays.sort(items);
+        for (int i = 0; i < size; i++) {
+            dos.writeUTF(items[i].name);
+            dos.writeInt(items[i].access);
+            dos.writeUTF(dotted ? items[i].desc.replace('/', '.')
+                    : items[i].desc);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Inner classes
+    // ------------------------------------------------------------------------
+
+    private static class Item implements Comparable<Item> {
+
+        final String name;
+
+        final int access;
+
+        final String desc;
+
+        Item(final String name, final int access, final String desc) {
+            this.name = name;
+            this.access = access;
+            this.desc = desc;
+        }
+
+        public int compareTo(final Item other) {
+            int retVal = name.compareTo(other.name);
+            if (retVal == 0) {
+                retVal = desc.compareTo(other.desc);
+            }
+            return retVal;
+        }
+
+        @Override
+        public boolean equals(final Object o) {
+            if (o instanceof Item) {
+                return compareTo((Item) o) == 0;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return (name + desc).hashCode();
+        }
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/StaticInitMerger.java b/src/jvm/clojure/asm/commons/StaticInitMerger.java
new file mode 100644
index 0000000..1be4dcd
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/StaticInitMerger.java
@@ -0,0 +1,96 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import clojure.asm.ClassVisitor;
+import clojure.asm.MethodVisitor;
+import clojure.asm.Opcodes;
+
+/**
+ * A {@link ClassVisitor} that merges clinit methods into a single one.
+ *
+ * @author Eric Bruneton
+ */
+public class StaticInitMerger extends ClassVisitor {
+
+    private String name;
+
+    private MethodVisitor clinit;
+
+    private final String prefix;
+
+    private int counter;
+
+    public StaticInitMerger(final String prefix, final ClassVisitor cv) {
+        this(Opcodes.ASM4, prefix, cv);
+    }
+
+    protected StaticInitMerger(final int api, final String prefix,
+            final ClassVisitor cv) {
+        super(api, cv);
+        this.prefix = prefix;
+    }
+
+    @Override
+    public void visit(final int version, final int access, final String name,
+            final String signature, final String superName,
+            final String[] interfaces) {
+        cv.visit(version, access, name, signature, superName, interfaces);
+        this.name = name;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(final int access, final String name,
+            final String desc, final String signature, final String[] exceptions) {
+        MethodVisitor mv;
+        if ("<clinit>".equals(name)) {
+            int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC;
+            String n = prefix + counter++;
+            mv = cv.visitMethod(a, n, desc, signature, exceptions);
+
+            if (clinit == null) {
+                clinit = cv.visitMethod(a, name, desc, null, null);
+            }
+            clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc);
+        } else {
+            mv = cv.visitMethod(access, name, desc, signature, exceptions);
+        }
+        return mv;
+    }
+
+    @Override
+    public void visitEnd() {
+        if (clinit != null) {
+            clinit.visitInsn(Opcodes.RETURN);
+            clinit.visitMaxs(0, 0);
+        }
+        cv.visitEnd();
+    }
+}
diff --git a/src/jvm/clojure/asm/commons/TableSwitchGenerator.java b/src/jvm/clojure/asm/commons/TableSwitchGenerator.java
new file mode 100644
index 0000000..7c1cf6e
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/TableSwitchGenerator.java
@@ -0,0 +1,57 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package clojure.asm.commons;
+
+import clojure.asm.Label;
+
+/**
+ * A code generator for switch statements.
+ *
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public interface TableSwitchGenerator {
+
+    /**
+     * Generates the code for a switch case.
+     *
+     * @param key
+     *            the switch case key.
+     * @param end
+     *            a label that corresponds to the end of the switch statement.
+     */
+    void generateCase(int key, Label end);
+
+    /**
+     * Generates the code for the default switch case.
+     */
+    void generateDefault();
+}
diff --git a/src/jvm/clojure/asm/commons/package.html b/src/jvm/clojure/asm/commons/package.html
new file mode 100644
index 0000000..4ce0db8
--- /dev/null
+++ b/src/jvm/clojure/asm/commons/package.html
@@ -0,0 +1,48 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+Provides some useful class and method adapters. <i>The preferred way of using
+these adapters is by chaining them together and to custom adapters (instead of
+inheriting from them)</i>. Indeed this approach provides more combination
+possibilities than inheritance. For instance, suppose you want to implement an
+adapter MyAdapter than needs sorted local variables and intermediate stack map
+frame values taking into account the local variables sort. By using inheritance,
+this would require MyAdapter to extend AnalyzerAdapter, itself extending
+LocalVariablesSorter. But AnalyzerAdapter is not a subclass of
+LocalVariablesSorter, so this is not possible. On the contrary, by using
+delegation, you can make LocalVariablesSorter delegate to AnalyzerAdapter,
+itself delegating to MyAdapter. In this case AnalyzerAdapter computes
+intermediate frames based on the output of LocalVariablesSorter, and MyAdapter
+can add new locals by calling the newLocal method on LocalVariablesSorter, and
+can get the stack map frame state before each instruction by reading the locals
+and stack fields in AnalyzerAdapter (this requires references from MyAdapter
+back to LocalVariablesSorter and AnalyzerAdapter).
+</body>
\ No newline at end of file
diff --git a/src/jvm/clojure/asm/package.html b/src/jvm/clojure/asm/package.html
new file mode 100644
index 0000000..82c1ff9
--- /dev/null
+++ b/src/jvm/clojure/asm/package.html
@@ -0,0 +1,87 @@
+<html>
+<!--
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2011 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+-->
+<body>
+Provides a small and fast bytecode manipulation framework.
+
+<p>
+The <a href="http://www.objectweb.org/asm">ASM</a> framework is organized
+around the {@link clojure.asm.ClassVisitor ClassVisitor},
+{@link clojure.asm.FieldVisitor FieldVisitor},
+{@link clojure.asm.MethodVisitor MethodVisitor} and
+{@link clojure.asm.AnnotationVisitor AnnotationVisitor} abstract classes,
+which allow one to visit the fields, methods and annotations of a class,
+including the bytecode instructions of each method.
+
+<p>
+In addition to these main abstract classes, ASM provides a {@link
+clojure.asm.ClassReader ClassReader} class, that can parse an
+existing class and make a given visitor visit it. ASM also provides
+a {@link clojure.asm.ClassWriter ClassWriter} class, which is
+a visitor that generates Java class files.
+
+<p>
+In order to generate a class from scratch, only the {@link
+clojure.asm.ClassWriter ClassWriter} class is necessary. Indeed,
+in order to generate a class, one must just call its visit<i>Xxx</i>
+methods with the appropriate arguments to generate the desired fields
+and methods. See the "helloworld" example in the ASM distribution for
+more details about class generation.
+
+<p>
+In order to modify existing classes, one must use a {@link
+clojure.asm.ClassReader ClassReader} class to analyze
+the original class, a class modifier, and a {@link clojure.asm.ClassWriter
+ClassWriter} to construct the modified class. The class modifier
+is just a {@link clojure.asm.ClassVisitor ClassVisitor}
+that delegates most of the work to another {@link clojure.asm.ClassVisitor
+ClassVisitor}, but that sometimes changes some parameter values,
+or call additional methods, in order to implement the desired
+modification process. In order to make it easier to implement such
+class modifiers, the {@link clojure.asm.ClassVisitor
+ClassVisitor} and {@link clojure.asm.MethodVisitor MethodVisitor}
+classes delegate by default all the method calls they receive to an
+optional visitor. See the "adapt" example in the ASM
+distribution for more details about class modification.
+
+<p>
+The size of the core ASM library, <tt>asm.jar</tt>, is only 45KB, which is much
+smaller than the size of the
+<a href="http://jakarta.apache.org/bcel">BCEL</a> library (504KB), and than the
+size of the
+<a href="http://serp.sourceforge.net">SERP</a> library (150KB). ASM is also
+much faster than these tools. Indeed the overhead of a load time class
+transformation process is of the order of 60% with ASM, 700% or more with BCEL,
+and 1100% or more with SERP (see the <tt>test/perf</tt> directory in the ASM
+distribution)!
+
+ at since ASM 1.3
+</body>
+</html>
diff --git a/src/jvm/clojure/java/api/Clojure.java b/src/jvm/clojure/java/api/Clojure.java
new file mode 100644
index 0000000..d3ea0e1
--- /dev/null
+++ b/src/jvm/clojure/java/api/Clojure.java
@@ -0,0 +1,100 @@
+/**
+ *   Copyright (c) Rich Hickey and Contributors. All rights reserved.
+ *   The use and distribution terms for this software are covered by the
+ *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ *   which can be found in the file epl-v10.html at the root of this distribution.
+ *   By using this software in any fashion, you are agreeing to be bound by
+ * 	 the terms of this license.
+ *   You must not remove this notice, or any other, from this software.
+ **/
+
+package clojure.java.api;
+
+import clojure.lang.IFn;
+import clojure.lang.Symbol;
+import clojure.lang.Var;
+
+/**
+ * <p>The Clojure class provides a minimal interface to bootstrap Clojure access
+ * from other JVM languages. It provides:</p>
+ *
+ * <ol>
+ * <li>The ability to use Clojure's namespaces to locate an arbitrary
+ * <a href="http://clojure.org/vars">var</a>, returning the
+ * var's {@link clojure.lang.IFn} interface.</li>
+ * <li>A convenience method <code>read</code> for reading data using
+ * Clojure's edn reader</li>
+ * </ol>
+ *
+ * <p>To lookup and call a Clojure function:</p>
+ *
+ * <pre>
+ * IFn plus = Clojure.var("clojure.core", "+");
+ * plus.invoke(1, 2);</pre>
+ *
+ * <p>Functions in <code>clojure.core</code> are automatically loaded. Other
+ * namespaces can be loaded via <code>require</code>:</p>
+ *
+ * <pre>
+ * IFn require = Clojure.var("clojure.core", "require");
+ * require.invoke(Clojure.read("clojure.set"));</pre>
+ *
+ * <p><code>IFn</code>s can be passed to higher order functions, e.g. the
+ * example below passes <code>plus</code> to <code>read</code>:</p>
+ *
+ * <pre>
+ * IFn map = Clojure.var("clojure.core", "map");
+ * IFn inc = Clojure.var("clojure.core", "inc");
+ * map.invoke(inc, Clojure.read("[1 2 3]"));</pre>
+ */
+public class Clojure {
+    private Clojure() {}
+
+    private static Symbol asSym(Object o) {
+        Symbol s;
+        if (o instanceof String) {
+            s = Symbol.intern((String) o);
+        }  else {
+            s = (Symbol) o;
+        }
+        return s;
+    }
+
+    /**
+     * Returns the var associated with qualifiedName.
+     *
+     * @param qualifiedName  a String or clojure.lang.Symbol
+     * @return               a clojure.lang.IFn
+     */
+    public static IFn var(Object qualifiedName) {
+        Symbol s = asSym(qualifiedName);
+        return var(s.getNamespace(), s.getName());
+    }
+
+    /**
+     * Returns an IFn associated with the namespace and name.
+     *
+     * @param ns        a String or clojure.lang.Symbol
+     * @param name      a String or clojure.lang.Symbol
+     * @return          a clojure.lang.IFn
+     */
+    public static IFn var(Object ns, Object name) {
+        return Var.intern(asSym(ns), asSym(name));
+    }
+
+    /**
+     * Read one object from the String s.  Reads data in the
+     * <a href="http://edn-format.org">edn format</a>.
+     * @param s   a String
+     * @return    an Object, or nil.
+     */
+    public static Object read(String s) {
+        return EDN_READ_STRING.invoke(s);
+    }
+
+    static {
+        Symbol edn = (Symbol) var("clojure.core", "symbol").invoke("clojure.edn");
+        var("clojure.core", "require").invoke(edn);
+    }
+    private static final IFn EDN_READ_STRING = var("clojure.edn", "read-string");
+}
diff --git a/src/jvm/clojure/java/api/package.html b/src/jvm/clojure/java/api/package.html
new file mode 100644
index 0000000..6536c33
--- /dev/null
+++ b/src/jvm/clojure/java/api/package.html
@@ -0,0 +1,79 @@
+<html>
+
+<!--
+Copyright (c) Rich Hickey and Contributors. All rights reserved.
+The use and distribution terms for this software are covered by the
+Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+which can be found in the file epl-v10.html at the root of this distribution.
+By using this software in any fashion, you are agreeing to be bound by
+the terms of this license.
+You must not remove this notice, or any other, from this software.
+-->
+
+<body>Clojure interop from Java.
+
+<p>The clojure.java.api package provides a minimal interface to bootstrap
+  Clojure access from other JVM languages.  It does this by providing:
+</p>
+
+<ol>
+  <li>The ability to use Clojure's namespaces to locate an arbitrary
+  <a href="http://clojure.org/vars">var</a>, returning the
+  var's <code>clojure.lang.IFn</code> interface.</li>
+  <li>A convenience method <code>read</code> for reading data using
+  Clojure's edn reader</li>
+</ol>
+
+<p><code>IFn</code>s provide complete access to
+  Clojure's <a href="http://clojure.github.io/clojure/">API</a>s.
+  You can also access any other library written in Clojure, after adding
+  either its source or compiled form to the classpath.</p>
+
+<p>The public Java API for Clojure consists of the following classes
+  and interfaces:
+</p>
+
+<ol>
+<li>clojure.java.api.Clojure</li>
+<li>clojure.lang.IFn</li>
+</ol>
+
+<p>All other Java classes should be treated as implementation details,
+  and applications should avoid relying on them.</p>
+
+<p>To lookup and call a Clojure function:
+<pre>
+IFn plus = Clojure.var("clojure.core", "+");
+plus.invoke(1, 2);
+</pre>
+</p>
+
+<p>Functions in <code>clojure.core</code> are automatically loaded. Other
+  namespaces can be loaded via <code>require</code>:
+<pre>
+IFn require = Clojure.var("clojure.core", "require");
+require.invoke(Clojure.read("clojure.set"));
+</pre>
+</p>
+
+<p><code>IFn</code>s can be passed to higher order functions, e.g. the
+  example below passes <code>plus</code> to <code>read</code>:
+<pre>
+IFn map = Clojure.var("clojure.core", "map");
+IFn inc = Clojure.var("clojure.core", "inc");
+map.invoke(inc, Clojure.read("[1 2 3]"));
+</pre>
+</p>
+
+<p>Most IFns in Clojure refer to functions. A few, however, refer to
+  non-function data values. To access these, use <code>deref</code>
+  instead of <code>fn</code>:</p>
+
+<pre>
+IFn printLength = Clojure.var("clojure.core", "*print-length*");
+IFn deref = Clojure.var("clojure.core", "deref");
+deref.invoke(printLength);
+</pre>
+
+</body>
+</html>
diff --git a/src/jvm/clojure/lang/AFn.java b/src/jvm/clojure/lang/AFn.java
index ca4d39d..68b3538 100644
--- a/src/jvm/clojure/lang/AFn.java
+++ b/src/jvm/clojure/lang/AFn.java
@@ -19,14 +19,7 @@ public Object call() {
 }
 
 public void run(){
-	try
-		{
-		invoke();
-		}
-	catch(Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
+        invoke();
 }
 
 
@@ -433,7 +426,6 @@ static public Object applyToHelper(IFn ifn, ISeq arglist) {
 
 public Object throwArity(int n){
 	String name = getClass().getSimpleName();
-	int suffix = name.lastIndexOf("__");
-	throw new ArityException(n, (suffix == -1 ? name : name.substring(0, suffix)).replace('_', '-'));
+	throw new ArityException(n, Compiler.demunge(name));
 }
 }
diff --git a/src/jvm/clojure/lang/AFunction.java b/src/jvm/clojure/lang/AFunction.java
index 2963d0e..4f69ec4 100644
--- a/src/jvm/clojure/lang/AFunction.java
+++ b/src/jvm/clojure/lang/AFunction.java
@@ -44,23 +44,16 @@ public IObj withMeta(final IPersistentMap meta){
 }
 
 public int compare(Object o1, Object o2){
-	try
-		{
-		Object o = invoke(o1, o2);
+        Object o = invoke(o1, o2);
 
-		if(o instanceof Boolean)
-			{
-			if(RT.booleanCast(o))
-				return -1;
-			return RT.booleanCast(invoke(o2,o1))? 1 : 0;
-			}
+        if(o instanceof Boolean)
+                {
+                if(RT.booleanCast(o))
+                        return -1;
+                return RT.booleanCast(invoke(o2,o1))? 1 : 0;
+                }
 
-		Number n = (Number) o;
-		return n.intValue();
-		}
-	catch(Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
+        Number n = (Number) o;
+        return n.intValue();
 }
 }
diff --git a/src/jvm/clojure/lang/APersistentMap.java b/src/jvm/clojure/lang/APersistentMap.java
index 205b867..f270faa 100644
--- a/src/jvm/clojure/lang/APersistentMap.java
+++ b/src/jvm/clojure/lang/APersistentMap.java
@@ -15,6 +15,7 @@ import java.util.*;
 
 public abstract class APersistentMap extends AFn implements IPersistentMap, Map, Iterable, Serializable, MapEquivalence, IHashEq {
 int _hash = -1;
+int _hasheq = -1;
 
 public String toString(){
 	return RT.printString(this);
@@ -111,14 +112,24 @@ static public int mapHash(IPersistentMap m){
 }
 
 public int hasheq(){
-	int hash = 0;
-	for(ISeq s = this.seq(); s != null; s = s.next())
+	if(_hasheq == -1)
 		{
-		Map.Entry e = (Map.Entry) s.first();
-		hash += Util.hasheq(e.getKey()) ^
-				Util.hasheq(e.getValue());
+		//this._hasheq = mapHasheq(this);
+		_hasheq = Murmur3.hashUnordered(this);
 		}
-	return hash;
+	return _hasheq;
+}
+
+static public int mapHasheq(IPersistentMap m) {
+	return Murmur3.hashUnordered(m);
+//	int hash = 0;
+//	for(ISeq s = m.seq(); s != null; s = s.next())
+//		{
+//		Map.Entry e = (Map.Entry) s.first();
+//		hash += Util.hasheq(e.getKey()) ^
+//				Util.hasheq(e.getValue());
+//		}
+//	return hash;
 }
 
 static public class KeySeq extends ASeq{
diff --git a/src/jvm/clojure/lang/APersistentSet.java b/src/jvm/clojure/lang/APersistentSet.java
index 8d5ab0e..5870998 100644
--- a/src/jvm/clojure/lang/APersistentSet.java
+++ b/src/jvm/clojure/lang/APersistentSet.java
@@ -19,6 +19,7 @@ import java.util.Set;
 
 public abstract class APersistentSet extends AFn implements IPersistentSet, Collection, Set, Serializable, IHashEq {
 int _hash = -1;
+int _hasheq = -1;
 final IPersistentMap impl;
 
 protected APersistentSet(IPersistentMap impl){
@@ -50,30 +51,43 @@ public Object invoke(Object arg1) {
 }
 
 public boolean equals(Object obj){
-	if(this == obj) return true;
-	if(!(obj instanceof Set))
-		return false;
-	Set m = (Set) obj;
+    return setEquals(this, obj);
+}
 
-	if(m.size() != count() || m.hashCode() != hashCode())
-		return false;
+static public boolean setEquals(IPersistentSet s1, Object obj) {
+    if(s1 == obj) return true;
+    if(!(obj instanceof Set))
+        return false;
+    Set m = (Set) obj;
 
-	for(Object aM : m)
-		{
-		if(!contains(aM))
-			return false;
-		}
-//	for(ISeq s = seq(); s != null; s = s.rest())
-//		{
-//		if(!m.contains(s.first()))
-//			return false;
-//		}
+    if(m.size() != s1.count())
+        return false;
 
-	return true;
+    for(Object aM : m)
+    {
+        if(!s1.contains(aM))
+            return false;
+    }
+
+    return true;
 }
 
-public boolean equiv(Object o){
-	return equals(o);
+public boolean equiv(Object obj){
+	if (!(obj instanceof Set))
+        return false;
+
+    Set m = (Set) obj;
+
+    if (m.size() != size())
+        return false;
+
+    for(Object aM : m)
+    {
+        if(!contains(aM))
+            return false;
+    }
+
+    return true;
 }
 
 public int hashCode(){
@@ -93,13 +107,17 @@ public int hashCode(){
 }
 
 public int hasheq(){
-	int hash = 0;
-	for(ISeq s = seq(); s != null; s = s.next())
-		{
-		Object e = s.first();
-		hash +=  Util.hasheq(e);
-		}
-	return hash;
+	if(_hasheq == -1){
+//		int hash = 0;
+//		for(ISeq s = seq(); s != null; s = s.next())
+//			{
+//			Object e = s.first();
+//			hash +=  Util.hasheq(e);
+//			}
+//		this._hasheq = hash;
+		_hasheq = Murmur3.hashUnordered(this);
+	}
+	return _hasheq;		
 }
 
 public Object[] toArray(){
diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java
index 2b37379..a9f1828 100644
--- a/src/jvm/clojure/lang/APersistentVector.java
+++ b/src/jvm/clojure/lang/APersistentVector.java
@@ -20,6 +20,7 @@ public abstract class APersistentVector extends AFn implements IPersistentVector
                                                                RandomAccess, Comparable,
                                                                Serializable, IHashEq {
 int _hash = -1;
+int _hasheq = -1;
 
 public String toString(){
 	return RT.printString(this);
@@ -153,14 +154,18 @@ public int hashCode(){
 }
 
 public int hasheq(){
-	int hash = 1;
-	Iterator i = iterator();
-	while(i.hasNext())
-		{
-		Object obj = i.next();
-		hash = 31 * hash + Util.hasheq(obj);
-		}
-	return hash;
+	if(_hasheq == -1) {
+//	int hash = 1;
+//	Iterator i = iterator();
+//	while(i.hasNext())
+//		{
+//		Object obj = i.next();
+//		hash = 31 * hash + Util.hasheq(obj);
+//		}
+//	_hasheq = hash;
+	_hasheq  = Murmur3.hashOrdered(this);
+	}
+	return _hasheq;
 }
 
 public Object get(int index){
@@ -237,6 +242,24 @@ public ListIterator listIterator(final int index){
 	};
 }
 
+Iterator rangedIterator(final int start, final int end){
+	return new Iterator(){
+		int i = start;
+
+		public boolean hasNext(){
+			return i < end;
+		}
+
+		public Object next(){
+			return nth(i++);
+		}
+
+		public void remove(){
+			throw new UnsupportedOperationException();
+		}
+	};
+}
+
 public List subList(int fromIndex, int toIndex){
 	return (List) RT.subvec(this, fromIndex, toIndex);
 }
@@ -497,10 +520,10 @@ public static class RSeq extends ASeq implements IndexedSeq, Counted{
 	}
 }
 
-static class SubVector extends APersistentVector implements IObj{
-	final IPersistentVector v;
-	final int start;
-	final int end;
+public static class SubVector extends APersistentVector implements IObj{
+	public final IPersistentVector v;
+	public final int start;
+	public final int end;
 	final IPersistentMap _meta;
 
 
@@ -520,8 +543,15 @@ static class SubVector extends APersistentVector implements IObj{
 		this.end = end;
 	}
 
+	public Iterator iterator(){
+		if (v instanceof APersistentVector) {
+			return ((APersistentVector)v).rangedIterator(start,end);
+		}
+		return super.iterator();
+	}
+
 	public Object nth(int i){
-		if(start + i >= end)
+		if((start + i >= end) || (i < 0))
 			throw new IndexOutOfBoundsException();
 		return v.nth(start + i);
 	}
diff --git a/src/jvm/clojure/lang/ARef.java b/src/jvm/clojure/lang/ARef.java
index 44dc2ad..e10aca3 100644
--- a/src/jvm/clojure/lang/ARef.java
+++ b/src/jvm/clojure/lang/ARef.java
@@ -47,14 +47,7 @@ void validate(Object val){
 }
 
 public void setValidator(IFn vf){
-	try
-		{
-		validate(vf, deref());
-		}
-	catch(Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
+	validate(vf, deref());
 	validator = vf;
 }
 
@@ -72,15 +65,7 @@ synchronized public IRef addWatch(Object key, IFn callback){
 }
 
 synchronized public IRef removeWatch(Object key){
-	try
-		{
-		watches = watches.without(key);
-		}
-	catch(Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
-
+	watches = watches.without(key);
 	return this;
 }
 
@@ -92,15 +77,8 @@ public void notifyWatches(Object oldval, Object newval){
 			{
 			Map.Entry e = (Map.Entry) s.first();
 			IFn fn = (IFn) e.getValue();
-			try
-				{
-				if(fn != null)
-					fn.invoke(e.getKey(), this, oldval, newval);
-				}
-			catch(Exception e1)
-				{
-				throw Util.sneakyThrow(e1);
-				}
+			if(fn != null)
+                                fn.invoke(e.getKey(), this, oldval, newval);
 			}
 		}
 }
diff --git a/src/jvm/clojure/lang/ASeq.java b/src/jvm/clojure/lang/ASeq.java
index 5597f5f..4f5c1a8 100644
--- a/src/jvm/clojure/lang/ASeq.java
+++ b/src/jvm/clojure/lang/ASeq.java
@@ -15,6 +15,7 @@ import java.util.*;
 
 public abstract class ASeq extends Obj implements ISeq, Sequential, List, Serializable, IHashEq {
 transient int _hash = -1;
+transient int _hasheq = -1;
 
 public String toString(){
 	return RT.printString(this);
@@ -74,12 +75,17 @@ public int hashCode(){
 }
 
 public int hasheq(){
-	int hash = 1;
-	for(ISeq s = seq(); s != null; s = s.next())
+	if(_hasheq == -1)
 		{
-		hash = 31 * hash + Util.hasheq(s.first());
+//		int hash = 1;
+//		for(ISeq s = seq(); s != null; s = s.next())
+//			{
+//			hash = 31 * hash + Util.hasheq(s.first());
+//			}
+//		this._hasheq = hash;
+		_hasheq  = Murmur3.hashOrdered(this);
 		}
-	return hash;
+	return _hasheq;
 }
 
 
diff --git a/src/jvm/clojure/lang/Agent.java b/src/jvm/clojure/lang/Agent.java
index e63d060..0f508eb 100644
--- a/src/jvm/clojure/lang/Agent.java
+++ b/src/jvm/clojure/lang/Agent.java
@@ -12,6 +12,7 @@
 
 package clojure.lang;
 
+import java.util.concurrent.Executor;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
@@ -45,11 +46,11 @@ final private static AtomicLong sendThreadPoolCounter = new AtomicLong(0);
 
 final private static AtomicLong sendOffThreadPoolCounter = new AtomicLong(0);
 
-final public static ExecutorService pooledExecutor =
+volatile public static ExecutorService pooledExecutor =
 	Executors.newFixedThreadPool(2 + Runtime.getRuntime().availableProcessors(), 
 		createThreadFactory("clojure-agent-send-pool-%d", sendThreadPoolCounter));
 
-final public static ExecutorService soloExecutor = Executors.newCachedThreadPool(
+volatile public static ExecutorService soloExecutor = Executors.newCachedThreadPool(
 	createThreadFactory("clojure-agent-send-off-pool-%d", sendOffThreadPoolCounter));
 
 final static ThreadLocal<IPersistentVector> nested = new ThreadLocal<IPersistentVector>();
@@ -73,23 +74,20 @@ static class Action implements Runnable{
 	final Agent agent;
 	final IFn fn;
 	final ISeq args;
-	final boolean solo;
+	final Executor exec;
 
 
-	public Action(Agent agent, IFn fn, ISeq args, boolean solo){
+	public Action(Agent agent, IFn fn, ISeq args, Executor exec){
 		this.agent = agent;
 		this.args = args;
 		this.fn = fn;
-		this.solo = solo;
+		this.exec = exec;
 	}
 
 	void execute(){
 		try
 			{
-			if(solo)
-				soloExecutor.execute(this);
-			else
-				pooledExecutor.execute(this);
+			exec.execute(this);
 			}
 		catch(Throwable error)
 			{
@@ -233,13 +231,13 @@ synchronized public Object restart(Object newState, boolean clearActions){
 	return newState;
 }
 
-public Object dispatch(IFn fn, ISeq args, boolean solo) {
+public Object dispatch(IFn fn, ISeq args, Executor exec) {
 	Throwable error = getError();
 	if(error != null)
 		{
 		throw Util.runtimeException("Agent is failed, needs restart", error);
 		}
-	Action action = new Action(this, fn, args, solo);
+	Action action = new Action(this, fn, args, exec);
 	dispatchAction(action);
 
 	return this;
diff --git a/src/jvm/clojure/lang/ArrayChunk.java b/src/jvm/clojure/lang/ArrayChunk.java
index 76e818c..9743083 100644
--- a/src/jvm/clojure/lang/ArrayChunk.java
+++ b/src/jvm/clojure/lang/ArrayChunk.java
@@ -56,8 +56,14 @@ public IChunk dropFirst(){
 
 public Object reduce(IFn f, Object start) {
 		Object ret = f.invoke(start, array[off]);
+		if(RT.isReduced(ret))
+			return ret;
 		for(int x = off + 1; x < end; x++)
+			{
 			ret = f.invoke(ret, array[x]);
+			if(RT.isReduced(ret))
+				return ret;
+			}
 		return ret;
 }
 }
diff --git a/src/jvm/clojure/lang/ArraySeq.java b/src/jvm/clojure/lang/ArraySeq.java
index f6f0dd7..b7f1ea1 100644
--- a/src/jvm/clojure/lang/ArraySeq.java
+++ b/src/jvm/clojure/lang/ArraySeq.java
@@ -15,10 +15,8 @@ package clojure.lang;
 import java.lang.reflect.Array;
 
 public class ArraySeq extends ASeq implements IndexedSeq, IReduce{
-public final Object array;
+public final Object[] array;
 final int i;
-final Object[] oa;
-final Class ct;
 //ISeq _rest;
 
 static public ArraySeq create(){
@@ -47,51 +45,41 @@ static ISeq createFromObject(Object array){
 		return new ArraySeq_byte(null, (byte[]) array, 0);
 	if(aclass == char[].class)
 		return new ArraySeq_char(null, (char[]) array, 0);
+        if(aclass == short[].class)
+            return new ArraySeq_short(null, (short[]) array, 0);
 	if(aclass == boolean[].class)
 		return new ArraySeq_boolean(null, (boolean[]) array, 0);
 	return new ArraySeq(array, 0);
 }
 
 ArraySeq(Object array, int i){
-	this.array = array;
-	this.ct = array.getClass().getComponentType();
 	this.i = i;
-	this.oa = (Object[]) (array instanceof Object[] ? array : null);
+	this.array = (Object[]) array;
 //    this._rest = this;
 }
 
 ArraySeq(IPersistentMap meta, Object array, int i){
 	super(meta);
-	this.array = array;
-	this.ct = array.getClass().getComponentType();
 	this.i = i;
-	this.oa = (Object[]) (array instanceof Object[] ? array : null);
+	this.array = (Object[]) array;
 }
 
 public Object first(){
-	if(oa != null)
-		return oa[i];
-	return Reflector.prepRet(ct, Array.get(array, i));
+	if(array != null)
+		return array[i];
+	return null;
 }
 
 public ISeq next(){
-	if(oa != null)
-		{
-		if(i + 1 < oa.length)
-			return new ArraySeq(array, i + 1);
-		}
-	else
-		{
-		if(i + 1 < Array.getLength(array))
-			return new ArraySeq(array, i + 1);
-		}
+	if(array != null && i + 1 < array.length)
+		return new ArraySeq(array, i + 1);
 	return null;
 }
 
 public int count(){
-	if(oa != null)
-		return oa.length - i;
-	return Array.getLength(array) - i;
+	if(array != null)
+		return array.length - i;
+	return 0;
 }
 
 public int index(){
@@ -103,62 +91,40 @@ public ArraySeq withMeta(IPersistentMap meta){
 }
 
 public Object reduce(IFn f) {
-	if(oa != null)
-		{
-		Object ret = oa[i];
-		for(int x = i + 1; x < oa.length; x++)
-			ret = f.invoke(ret, oa[x]);
+	if(array != null) {
+		Object ret = array[i];
+		for(int x = i + 1; x < array.length; x++)
+			ret = f.invoke(ret, array[x]);
 		return ret;
-		}
-
-	Object ret = Reflector.prepRet(ct, Array.get(array, i));
-	for(int x = i + 1; x < Array.getLength(array); x++)
-		ret = f.invoke(ret, Reflector.prepRet(ct, Array.get(array, x)));
-	return ret;
+	}
+	return null;
 }
 
 public Object reduce(IFn f, Object start) {
-	if(oa != null)
-		{
-		Object ret = f.invoke(start, oa[i]);
-		for(int x = i + 1; x < oa.length; x++)
-			ret = f.invoke(ret, oa[x]);
+	if(array != null) {
+		Object ret = f.invoke(start, array[i]);
+		for(int x = i + 1; x < array.length; x++)
+			ret = f.invoke(ret, array[x]);
 		return ret;
-		}
-	Object ret = f.invoke(start, Reflector.prepRet(ct, Array.get(array, i)));
-	for(int x = i + 1; x < Array.getLength(array); x++)
-		ret = f.invoke(ret, Reflector.prepRet(ct, Array.get(array, x)));
-	return ret;
+	}
+	return null;
 }
 
 public int indexOf(Object o) {
-	if (oa != null) {
-		for (int j = i; j < oa.length; j++)
-			if (Util.equals(o, oa[j])) return j - i;
-	} else {
-		int n = Array.getLength(array); 
-		for (int j = i; j < n; j++)
-			if (Util.equals(o, Reflector.prepRet(ct, Array.get(array, j)))) return j - i;
-	}
+	if(array != null)
+		for (int j = i; j < array.length; j++)
+			if (Util.equals(o, array[j])) return j - i;
 	return -1;
 }
 
 public int lastIndexOf(Object o) {
-	if (oa != null) {
+	if (array != null) {
 		if (o == null) {
-			for (int j = oa.length - 1 ; j >= i; j--)
-				if (oa[j] == null) return j - i;
+			for (int j = array.length - 1 ; j >= i; j--)
+				if (array[j] == null) return j - i;
 		} else {
-			for (int j = oa.length - 1 ; j >= i; j--)
-				if (o.equals(oa[j])) return j - i;
-		}
-	} else {
-		if (o == null) {
-			for (int j = Array.getLength(array) - 1 ; j >= i; j--)
-				if (Reflector.prepRet(ct, Array.get(array, j)) == null) return j - i;
-		} else {
-			for (int j = Array.getLength(array) - 1 ; j >= i; j--)
-				if (o.equals(Reflector.prepRet(ct, Array.get(array, j)))) return j - i;
+			for (int j = array.length - 1 ; j >= i; j--)
+				if (o.equals(array[j])) return j - i;
 		}
 	}
 	return -1;
@@ -583,6 +549,81 @@ static public class ArraySeq_char extends ASeq implements IndexedSeq, IReduce{
 	}
 }
 
+static public class ArraySeq_short extends ASeq implements IndexedSeq, IReduce{
+	public final short[] array;
+	final int i;
+
+	ArraySeq_short(IPersistentMap meta, short[] array, int i){
+		super(meta);
+		this.array = array;
+		this.i = i;
+	}
+
+	public Object first(){
+		return array[i];
+	}
+
+	public ISeq next(){
+		if(i + 1 < array.length)
+			return new ArraySeq_short(meta(), array, i + 1);
+		return null;
+	}
+
+	public int count(){
+		return array.length - i;
+	}
+
+	public int index(){
+		return i;
+	}
+
+	public ArraySeq_short withMeta(IPersistentMap meta){
+		return new ArraySeq_short(meta, array, i);
+	}
+
+	public Object reduce(IFn f) {
+		Object ret = array[i];
+		for(int x = i + 1; x < array.length; x++)
+			ret = f.invoke(ret, array[x]);
+		return ret;
+	}
+
+	public Object reduce(IFn f, Object start) {
+		Object ret = f.invoke(start, array[i]);
+		for(int x = i + 1; x < array.length; x++)
+			ret = f.invoke(ret, array[x]);
+		return ret;
+	}
+
+	public int indexOf(Object o) {
+		if (o instanceof Short) {
+			short s = ((Short) o).shortValue();
+			for (int j = i; j < array.length; j++)
+				if (s == array[j]) return j - i;
+		}
+		if (o == null) {
+			return -1;
+		}
+		for (int j = i; j < array.length; j++)
+			if (o.equals(array[j])) return j - i;
+		return -1;
+	}
+
+	public int lastIndexOf(Object o) {
+		if (o instanceof Short) {
+			short s = ((Short) o).shortValue();
+			for (int j = array.length - 1; j >= i; j--)
+				if (s == array[j]) return j - i;
+		}
+		if (o == null) {
+			return -1;
+		}
+		for (int j = array.length - 1; j >= i; j--)
+			if (o.equals(array[j])) return j - i;
+		return -1;
+	}
+}
+
 static public class ArraySeq_boolean extends ASeq implements IndexedSeq, IReduce{
 	public final boolean[] array;
 	final int i;
diff --git a/src/jvm/clojure/lang/BigInt.java b/src/jvm/clojure/lang/BigInt.java
index aa82b7b..fb4bc00 100644
--- a/src/jvm/clojure/lang/BigInt.java
+++ b/src/jvm/clojure/lang/BigInt.java
@@ -13,8 +13,9 @@
 package clojure.lang;
 
 import java.math.BigInteger;
+import java.math.BigDecimal;
 
-public final class BigInt extends Number{
+public final class BigInt extends Number implements IHashEq{
 
 final public long lpart;
 final public BigInteger bipart;
@@ -30,6 +31,13 @@ public int hashCode(){
 	return bipart.hashCode();
 }
 
+public int hasheq(){
+	if(bipart == null)
+		return Murmur3.hashLong(lpart);
+	return bipart.hashCode();
+
+}
+
 public boolean equals(Object obj){
 	if(this == obj)
 		return true;
@@ -66,6 +74,13 @@ public BigInteger toBigInteger(){
 		return bipart;
 }
 
+public BigDecimal toBigDecimal(){
+	if(bipart == null)
+		return BigDecimal.valueOf(lpart);
+	else
+		return new BigDecimal(bipart);
+}
+
 ///// java.lang.Number:
 
 public int intValue(){
@@ -136,7 +151,8 @@ public BigInt add(BigInt y) {
 public BigInt multiply(BigInt y) {
     if ((bipart == null) && (y.bipart == null)) {
         long ret = lpart * y.lpart;
-            if (y.lpart == 0 || ret / y.lpart == lpart)
+            if (y.lpart == 0 ||
+                (ret / y.lpart == lpart && lpart != Long.MIN_VALUE))
                 return BigInt.valueOf(ret);
         }
     return BigInt.fromBigInteger(this.toBigInteger().multiply(y.toBigInteger()));
diff --git a/src/jvm/clojure/lang/Compile.java b/src/jvm/clojure/lang/Compile.java
index 6084bad..6e3f049 100644
--- a/src/jvm/clojure/lang/Compile.java
+++ b/src/jvm/clojure/lang/Compile.java
@@ -87,7 +87,6 @@ public static void main(String[] args) throws IOException{
 		try
 			{
 			out.flush();
-			out.close();
 			}
 		catch(IOException e)
 			{
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java
index 0898f07..2770ea0 100644
--- a/src/jvm/clojure/lang/Compiler.java
+++ b/src/jvm/clojure/lang/Compiler.java
@@ -23,6 +23,7 @@ import java.lang.reflect.Constructor;
 import java.lang.reflect.Modifier;
 import java.util.*;
 import java.util.regex.Pattern;
+import java.util.regex.Matcher;
 
 //*/
 /*
@@ -44,6 +45,7 @@ static final Symbol LET = Symbol.intern("let*");
 static final Symbol LETFN = Symbol.intern("letfn*");
 static final Symbol DO = Symbol.intern("do");
 static final Symbol FN = Symbol.intern("fn*");
+static final Symbol FNONCE = (Symbol) Symbol.intern("fn*").withMeta(RT.map(Keyword.intern(null, "once"), RT.T));
 static final Symbol QUOTE = Symbol.intern("quote");
 static final Symbol THE_VAR = Symbol.intern("var");
 static final Symbol DOT = Symbol.intern(".");
@@ -265,10 +267,21 @@ static Object elideMeta(Object m){
 
 //Integer
 static final public Var LINE = Var.create(0).setDynamic();
+static final public Var COLUMN = Var.create(0).setDynamic();
+
+static int lineDeref(){
+	return ((Number)LINE.deref()).intValue();
+}
+
+static int columnDeref(){
+	return ((Number)COLUMN.deref()).intValue();
+}
 
 //Integer
 static final public Var LINE_BEFORE = Var.create(0).setDynamic();
+static final public Var COLUMN_BEFORE = Var.create(0).setDynamic();
 static final public Var LINE_AFTER = Var.create(0).setDynamic();
+static final public Var COLUMN_AFTER = Var.create(0).setDynamic();
 
 //Integer
 static final public Var NEXT_LOCAL_NUM = Var.create(0).setDynamic();
@@ -297,6 +310,9 @@ static final public Var CLEAR_SITES = Var.create(null).setDynamic();
 	EVAL
 }
 
+private class Recur {};
+static final public Class RECUR_CLASS = Recur.class;
+    
 interface Expr{
 	Object eval() ;
 
@@ -359,15 +375,17 @@ static class DefExpr implements Expr{
 	public final boolean isDynamic;
 	public final String source;
 	public final int line;
+	public final int column;
 	final static Method bindRootMethod = Method.getMethod("void bindRoot(Object)");
 	final static Method setTagMethod = Method.getMethod("void setTag(clojure.lang.Symbol)");
 	final static Method setMetaMethod = Method.getMethod("void setMeta(clojure.lang.IPersistentMap)");
 	final static Method setDynamicMethod = Method.getMethod("clojure.lang.Var setDynamic(boolean)");
 	final static Method symintern = Method.getMethod("clojure.lang.Symbol intern(String, String)");
 
-	public DefExpr(String source, int line, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){
+	public DefExpr(String source, int line, int column, Var var, Expr init, Expr meta, boolean initProvided, boolean isDynamic){
 		this.source = source;
 		this.line = line;
+		this.column = column;
 		this.var = var;
 		this.init = init;
 		this.meta = meta;
@@ -381,7 +399,8 @@ static class DefExpr implements Expr{
                 Keyword k  = ((KeywordExpr) expr.keyvals.nth(i)).k;
                 if ((k != RT.FILE_KEY) &&
                     (k != RT.DECLARED_KEY) &&
-                    (k != RT.LINE_KEY))
+                    (k != RT.LINE_KEY) &&
+                    (k != RT.COLUMN_KEY))
                     return true;
             }
         return false;
@@ -408,7 +427,7 @@ static class DefExpr implements Expr{
 		catch(Throwable e)
 			{
 			if(!(e instanceof CompilerException))
-				throw new CompilerException(source, line, e);
+				throw new CompilerException(source, line, column, e);
 			else
 				throw (CompilerException) e;
 			}
@@ -486,7 +505,7 @@ static class DefExpr implements Expr{
 			boolean isDynamic = RT.booleanCast(RT.get(mm,dynamicKey));
 			if(isDynamic)
 			   v.setDynamic();
-            if(!isDynamic && sym.name.startsWith("*") && sym.name.endsWith("*") && sym.name.length() > 1)
+            if(!isDynamic && sym.name.startsWith("*") && sym.name.endsWith("*") && sym.name.length() > 2)
                 {
                 RT.errPrintWriter().format("Warning: %1$s not declared dynamic and thus is not dynamically rebindable, "
                                           +"but its name suggests otherwise. Please either indicate ^:dynamic %1$s or change the name. (%2$s:%3$d)\n",
@@ -502,20 +521,21 @@ static class DefExpr implements Expr{
 				}
             Object source_path = SOURCE_PATH.get();
             source_path = source_path == null ? "NO_SOURCE_FILE" : source_path;
-            mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.FILE_KEY, source_path);
+            mm = (IPersistentMap) RT.assoc(mm, RT.LINE_KEY, LINE.get()).assoc(RT.COLUMN_KEY, COLUMN.get()).assoc(RT.FILE_KEY, source_path);
 			if (docstring != null)
 			  mm = (IPersistentMap) RT.assoc(mm, RT.DOC_KEY, docstring);
 //			mm = mm.without(RT.DOC_KEY)
 //					.without(Keyword.intern(null, "arglists"))
 //					.without(RT.FILE_KEY)
 //					.without(RT.LINE_KEY)
+//					.without(RT.COLUMN_KEY)
 //					.without(Keyword.intern(null, "ns"))
 //					.without(Keyword.intern(null, "name"))
 //					.without(Keyword.intern(null, "added"))
 //					.without(Keyword.intern(null, "static"));
             mm = (IPersistentMap) elideMeta(mm);
 			Expr meta = mm.count()==0 ? null:analyze(context == C.EVAL ? context : C.EXPRESSION, mm);
-			return new DefExpr((String) SOURCE.deref(), (Integer) LINE.deref(),
+			return new DefExpr((String) SOURCE.deref(), lineDeref(), columnDeref(),
 			                   v, analyze(context == C.EVAL ? context : C.EXPRESSION, RT.third(form), v.sym.name),
 			                   meta, RT.count(form) == 3, isDynamic);
 		}
@@ -894,7 +914,8 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{
 				throw new IllegalArgumentException("Malformed member expression, expecting (. target member ...)");
 			//determine static or instance
 			//static target must be symbol, either fully.qualified.Classname or Classname that has been imported
-			int line = (Integer) LINE.deref();
+			int line = lineDeref();
+			int column = columnDeref();
 			String source = (String) SOURCE.deref();
 			Class c = maybeClass(RT.second(form), false);
 			//at this point c will be non-null if static
@@ -920,9 +941,9 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{
 						:(Symbol) RT.third(form);
 				Symbol tag = tagOf(form);
 				if(c != null) {
-					return new StaticFieldExpr(line, c, munge(sym.name), tag);
+					return new StaticFieldExpr(line, column, c, munge(sym.name), tag);
 				} else
-					return new InstanceFieldExpr(line, instance, munge(sym.name), tag);
+					return new InstanceFieldExpr(line, column, instance, munge(sym.name), tag, (((Symbol)RT.third(form)).name.charAt(0) == '-'));
 				}
 			else
 				{
@@ -935,9 +956,9 @@ static public abstract class HostExpr implements Expr, MaybePrimitiveExpr{
 				for(ISeq s = RT.next(call); s != null; s = s.next())
 					args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first()));
 				if(c != null)
-					return new StaticMethodExpr(source, line, tag, c, munge(sym.name), args);
+					return new StaticMethodExpr(source, line, column, tag, c, munge(sym.name), args);
 				else
-					return new InstanceMethodExpr(source, line, tag, instance, munge(sym.name), args);
+					return new InstanceMethodExpr(source, line, column, tag, instance, munge(sym.name), args);
 				}
 		}
 	}
@@ -1058,28 +1079,41 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
 	public final java.lang.reflect.Field field;
 	public final String fieldName;
 	public final int line;
+	public final int column;
 	public final Symbol tag;
-	final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String)");
+	public final boolean requireField;
+	final static Method invokeNoArgInstanceMember = Method.getMethod("Object invokeNoArgInstanceMember(Object,String,boolean)");
 	final static Method setInstanceFieldMethod = Method.getMethod("Object setInstanceField(Object,String,Object)");
 
 
-	public InstanceFieldExpr(int line, Expr target, String fieldName, Symbol tag) {
+	public InstanceFieldExpr(int line, int column, Expr target, String fieldName, Symbol tag, boolean requireField) {
 		this.target = target;
 		this.targetClass = target.hasJavaClass() ? target.getJavaClass() : null;
 		this.field = targetClass != null ? Reflector.getField(targetClass, fieldName, false) : null;
 		this.fieldName = fieldName;
 		this.line = line;
+		this.column = column;
 		this.tag = tag;
+		this.requireField = requireField;
 		if(field == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
 			{
-			RT.errPrintWriter()
-		      .format("Reflection warning, %s:%d - reference to field %s can't be resolved.\n",
-					  SOURCE_PATH.deref(), line, fieldName);
+			if(targetClass == null)
+				{
+				RT.errPrintWriter()
+					.format("Reflection warning, %s:%d:%d - reference to field %s can't be resolved.\n",
+									SOURCE_PATH.deref(), line, column, fieldName);
+				}
+			else
+				{
+				RT.errPrintWriter()
+					.format("Reflection warning, %s:%d:%d - reference to field %s on %s can't be resolved.\n",
+									SOURCE_PATH.deref(), line, column, fieldName, targetClass.getName());
+				}
 			}
 	}
 
 	public Object eval() {
-		return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName);
+		return Reflector.invokeNoArgInstanceMember(target.eval(), fieldName, requireField);
 	}
 
 	public boolean canEmitPrimitive(){
@@ -1117,6 +1151,7 @@ static class InstanceFieldExpr extends FieldExpr implements AssignableExpr{
 			{
 			target.emit(C.EXPRESSION, objx, gen);
 			gen.push(fieldName);
+			gen.push(requireField);
 			gen.invokeStatic(REFLECTOR_TYPE, invokeNoArgInstanceMember);
 			if(context == C.STATEMENT)
 				gen.pop();
@@ -1168,11 +1203,13 @@ static class StaticFieldExpr extends FieldExpr implements AssignableExpr{
 //	final static Method getStaticFieldMethod = Method.getMethod("Object getStaticField(String,String)");
 //	final static Method setStaticFieldMethod = Method.getMethod("Object setStaticField(String,String,Object)");
 	final int line;
+	final int column;
 
-	public StaticFieldExpr(int line, Class c, String fieldName, Symbol tag) {
+	public StaticFieldExpr(int line, int column, Class c, String fieldName, Symbol tag) {
 		//this.className = className;
 		this.fieldName = fieldName;
 		this.line = line;
+		this.column = column;
 		//c = Class.forName(className);
 		this.c = c;
 		try
@@ -1243,18 +1280,11 @@ static class StaticFieldExpr extends FieldExpr implements AssignableExpr{
 }
 
 static Class maybePrimitiveType(Expr e){
-	try
+	if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())
 		{
-		if(e instanceof MaybePrimitiveExpr && e.hasJavaClass() && ((MaybePrimitiveExpr)e).canEmitPrimitive())
-			{
-			Class c = e.getJavaClass();
-			if(Util.isPrimitive(c))
-				return c;
-			}
-		}
-	catch(Exception ex)
-		{
-		throw Util.sneakyThrow(ex);
+		Class c = e.getJavaClass();
+		if(Util.isPrimitive(c))
+			return c;
 		}
 	return null;
 }
@@ -1357,6 +1387,7 @@ static class InstanceMethodExpr extends MethodExpr{
 	public final IPersistentVector args;
 	public final String source;
 	public final int line;
+	public final int column;
 	public final Symbol tag;
 	public final java.lang.reflect.Method method;
 
@@ -1364,10 +1395,11 @@ static class InstanceMethodExpr extends MethodExpr{
 			Method.getMethod("Object invokeInstanceMethod(Object,String,Object[])");
 
 
-	public InstanceMethodExpr(String source, int line, Symbol tag, Expr target, String methodName, IPersistentVector args)
+	public InstanceMethodExpr(String source, int line, int column, Symbol tag, Expr target, String methodName, IPersistentVector args)
 			{
 		this.source = source;
 		this.line = line;
+		this.column = column;
 		this.args = args;
 		this.methodName = methodName;
 		this.target = target;
@@ -1376,8 +1408,15 @@ static class InstanceMethodExpr extends MethodExpr{
 			{
 			List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false);
 			if(methods.isEmpty())
+				{
 				method = null;
-			//throw new IllegalArgumentException("No matching method found");
+				if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+					{
+					RT.errPrintWriter()
+						.format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (no such method).\n",
+							SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName());
+					}
+				}
 			else
 				{
 				int methodidx = 0;
@@ -1401,16 +1440,23 @@ static class InstanceMethodExpr extends MethodExpr{
 					m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m);
 					}
 				method = m;
+				if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+					{
+					RT.errPrintWriter()
+						.format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (argument types: %s).\n",
+							SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName(), getTypeStringForArgs(args));
+					}
 				}
 			}
 		else
-			method = null;
-
-		if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
 			{
-			RT.errPrintWriter()
-		      .format("Reflection warning, %s:%d - call to %s can't be resolved.\n",
-					  SOURCE_PATH.deref(), line, methodName);
+			method = null;
+			if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+				{
+				RT.errPrintWriter()
+					.format("Reflection warning, %s:%d:%d - call to method %s can't be resolved (target class is unknown).\n",
+						SOURCE_PATH.deref(), line, column, methodName);
+				}
 			}
 	}
 
@@ -1432,7 +1478,7 @@ static class InstanceMethodExpr extends MethodExpr{
 		catch(Throwable e)
 			{
 			if(!(e instanceof CompilerException))
-				throw new CompilerException(source, line, e);
+				throw new CompilerException(source, line, column, e);
 			else
 				throw (CompilerException) e;
 			}
@@ -1521,6 +1567,7 @@ static class StaticMethodExpr extends MethodExpr{
 	public final IPersistentVector args;
 	public final String source;
 	public final int line;
+	public final int column;
 	public final java.lang.reflect.Method method;
 	public final Symbol tag;
 	final static Method forNameMethod = Method.getMethod("Class forName(String)");
@@ -1528,13 +1575,14 @@ static class StaticMethodExpr extends MethodExpr{
 			Method.getMethod("Object invokeStaticMethod(Class,String,Object[])");
 
 
-	public StaticMethodExpr(String source, int line, Symbol tag, Class c, String methodName, IPersistentVector args)
+	public StaticMethodExpr(String source, int line, int column, Symbol tag, Class c, String methodName, IPersistentVector args)
 			{
 		this.c = c;
 		this.methodName = methodName;
 		this.args = args;
 		this.source = source;
 		this.line = line;
+		this.column = column;
 		this.tag = tag;
 
 		List methods = Reflector.getMethods(c, args.count(), methodName, true);
@@ -1558,8 +1606,8 @@ static class StaticMethodExpr extends MethodExpr{
 		if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
 			{
 			RT.errPrintWriter()
-              .format("Reflection warning, %s:%d - call to %s can't be resolved.\n",
-                      SOURCE_PATH.deref(), line, methodName);
+				.format("Reflection warning, %s:%d:%d - call to static method %s on %s can't be resolved (argument types: %s).\n",
+					SOURCE_PATH.deref(), line, column, methodName, c.getName(), getTypeStringForArgs(args));
 			}
 	}
 
@@ -1580,7 +1628,7 @@ static class StaticMethodExpr extends MethodExpr{
 		catch(Throwable e)
 			{
 			if(!(e instanceof CompilerException))
-				throw new CompilerException(source, line, e);
+				throw new CompilerException(source, line, column, e);
 			else
 				throw (CompilerException) e;
 			}
@@ -1824,7 +1872,14 @@ static class ConstantExpr extends LiteralExpr{
 	}
 
 	public Class getJavaClass() {
-		return v.getClass();
+		if(v instanceof APersistentMap)
+			return APersistentMap.class;
+		else if (v instanceof APersistentSet)
+			return APersistentSet.class;
+		else if (v instanceof APersistentVector)
+			return APersistentVector.class;
+		else
+			return v.getClass();
 		//throw new IllegalArgumentException("Has no Java class");
 	}
 
@@ -2104,7 +2159,7 @@ public static class TryExpr implements Expr{
 			ISeq form = (ISeq) frm;
 //			if(context == C.EVAL || context == C.EXPRESSION)
 			if(context != C.RETURN)
-				return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
+				return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
 
 			//(try try-expr* catch-expr* finally-expr?)
 			//catch-expr: (catch class sym expr*)
@@ -2133,7 +2188,7 @@ public static class TryExpr implements Expr{
                                             if(bodyExpr == null)
                                                 try {
                                                     Var.pushThreadBindings(RT.map(NO_RECUR, true));
-                                                    bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
+						                            bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
                                                 } finally {
                                                     Var.popThreadBindings();
                                                 }
@@ -2159,7 +2214,7 @@ public static class TryExpr implements Expr{
 							                                (Symbol) (RT.second(f) instanceof Symbol ? RT.second(f)
 							                                                                         : null),
 							                                null,false);
-							Expr handler = (new BodyExpr.Parser()).parse(context, RT.next(RT.next(RT.next(f))));
+							Expr handler = (new BodyExpr.Parser()).parse(C.EXPRESSION, RT.next(RT.next(RT.next(f))));
 							catches = catches.cons(new CatchClause(c, lb, handler));
 							}
 						finally
@@ -2188,7 +2243,7 @@ public static class TryExpr implements Expr{
                             try 
                                 {
                                     Var.pushThreadBindings(RT.map(NO_RECUR, true));
-                                    bodyExpr = (new BodyExpr.Parser()).parse(context, RT.seq(body));
+				    bodyExpr = (new BodyExpr.Parser()).parse(C.EXPRESSION, RT.seq(body));
                                 } 
                             finally
                                 {
@@ -2280,7 +2335,7 @@ static class ThrowExpr extends UntypedExpr{
 	static class Parser implements IParser{
 		public Expr parse(C context, Object form) {
 			if(context == C.EVAL)
-				return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
+				return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
 			return new ThrowExpr(analyze(C.EXPRESSION, RT.second(form)));
 		}
 	}
@@ -2306,6 +2361,17 @@ static public boolean subsumes(Class[] c1, Class[] c2){
 	return better;
 }
 
+static String getTypeStringForArgs(IPersistentVector args){
+	StringBuilder sb = new StringBuilder();
+	for(int i = 0; i < args.count(); i++)
+		{
+		Expr arg = (Expr) args.nth(i);
+		if (i > 0) sb.append(", ");
+		sb.append(arg.hasJavaClass() ? arg.getJavaClass().getName() : "unknown");
+		}
+	return sb.toString();
+}
+
 static int getMatchingParams(String methodName, ArrayList<Class[]> paramlists, IPersistentVector argexprs,
                              List<Class> rets)
 		{
@@ -2372,7 +2438,7 @@ public static class NewExpr implements Expr{
 	final static Method forNameMethod = Method.getMethod("Class forName(String)");
 
 
-	public NewExpr(Class c, IPersistentVector args, int line) {
+	public NewExpr(Class c, IPersistentVector args, int line, int column) {
 		this.args = args;
 		this.c = c;
 		Constructor[] allctors = c.getConstructors();
@@ -2402,8 +2468,8 @@ public static class NewExpr implements Expr{
 		if(ctor == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
 			{
 			RT.errPrintWriter()
-              .format("Reflection warning, %s:%d - call to %s ctor can't be resolved.\n",
-                      SOURCE_PATH.deref(), line, c.getName());
+              .format("Reflection warning, %s:%d:%d - call to %s ctor can't be resolved.\n",
+                      SOURCE_PATH.deref(), line, column, c.getName());
 			}
 	}
 
@@ -2465,7 +2531,8 @@ public static class NewExpr implements Expr{
 
 	static class Parser implements IParser{
 		public Expr parse(C context, Object frm) {
-			int line = (Integer) LINE.deref();
+			int line = lineDeref();
+			int column = columnDeref();
 			ISeq form = (ISeq) frm;
 			//(new Classname args...)
 			if(form.count() < 2)
@@ -2476,7 +2543,7 @@ public static class NewExpr implements Expr{
 			PersistentVector args = PersistentVector.EMPTY;
 			for(ISeq s = RT.next(RT.next(form)); s != null; s = s.next())
 				args = args.cons(analyze(context == C.EVAL ? context : C.EXPRESSION, s.first()));
-			return new NewExpr(c, args, line);
+			return new NewExpr(c, args, line, column);
 		}
 	}
 
@@ -2524,13 +2591,15 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{
 	public final Expr thenExpr;
 	public final Expr elseExpr;
 	public final int line;
+	public final int column;
 
 
-	public IfExpr(int line, Expr testExpr, Expr thenExpr, Expr elseExpr){
+	public IfExpr(int line, int column, Expr testExpr, Expr thenExpr, Expr elseExpr){
 		this.testExpr = testExpr;
 		this.thenExpr = thenExpr;
 		this.elseExpr = elseExpr;
 		this.line = line;
+		this.column = column;
 	}
 
 	public Object eval() {
@@ -2555,29 +2624,22 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{
 
 		gen.visitLineNumber(line, gen.mark());
 
-		try
+		if(testExpr instanceof StaticMethodExpr && ((StaticMethodExpr)testExpr).canEmitIntrinsicPredicate())
 			{
-			if(testExpr instanceof StaticMethodExpr && ((StaticMethodExpr)testExpr).canEmitIntrinsicPredicate())
-				{
-				((StaticMethodExpr) testExpr).emitIntrinsicPredicate(C.EXPRESSION, objx, gen, falseLabel);
-				}
-			else if(maybePrimitiveType(testExpr) == boolean.class)
-				{
-				((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION, objx, gen);
-				gen.ifZCmp(gen.EQ, falseLabel);
-				}
-			else
-				{
-				testExpr.emit(C.EXPRESSION, objx, gen);
-				gen.dup();
-				gen.ifNull(nullLabel);
-				gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
-				gen.visitJumpInsn(IF_ACMPEQ, falseLabel);
-				}
+			((StaticMethodExpr) testExpr).emitIntrinsicPredicate(C.EXPRESSION, objx, gen, falseLabel);
 			}
-		catch(Exception e)
+		else if(maybePrimitiveType(testExpr) == boolean.class)
 			{
-			throw Util.sneakyThrow(e);
+			((MaybePrimitiveExpr) testExpr).emitUnboxed(C.EXPRESSION, objx, gen);
+			gen.ifZCmp(gen.EQ, falseLabel);
+			}
+		else
+			{
+			testExpr.emit(C.EXPRESSION, objx, gen);
+			gen.dup();
+			gen.ifNull(nullLabel);
+			gen.getStatic(BOOLEAN_OBJECT_TYPE, "FALSE", BOOLEAN_OBJECT_TYPE);
+			gen.visitJumpInsn(IF_ACMPEQ, falseLabel);
 			}
 		if(emitUnboxed)
 			((MaybePrimitiveExpr)thenExpr).emitUnboxed(context, objx, gen);
@@ -2599,6 +2661,8 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{
 		       && elseExpr.hasJavaClass()
 		       &&
 		       (thenExpr.getJavaClass() == elseExpr.getJavaClass()
+		        || thenExpr.getJavaClass() == RECUR_CLASS
+				|| elseExpr.getJavaClass() == RECUR_CLASS		        
 		        || (thenExpr.getJavaClass() == null && !elseExpr.getJavaClass().isPrimitive())
 		        || (elseExpr.getJavaClass() == null && !thenExpr.getJavaClass().isPrimitive()));
 	}
@@ -2608,7 +2672,9 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{
 			{
 			return thenExpr instanceof MaybePrimitiveExpr
 			       && elseExpr instanceof MaybePrimitiveExpr
-			       && thenExpr.getJavaClass() == elseExpr.getJavaClass()
+			       && (thenExpr.getJavaClass() == elseExpr.getJavaClass()
+			           || thenExpr.getJavaClass() == RECUR_CLASS
+			           || elseExpr.getJavaClass() == RECUR_CLASS)
 			       && ((MaybePrimitiveExpr)thenExpr).canEmitPrimitive()
 				   && ((MaybePrimitiveExpr)elseExpr).canEmitPrimitive();
 			}
@@ -2620,7 +2686,7 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{
 
 	public Class getJavaClass() {
 		Class thenClass = thenExpr.getJavaClass();
-		if(thenClass != null)
+		if(thenClass != null && thenClass != RECUR_CLASS)
 			return thenClass;
 		return elseExpr.getJavaClass();
 	}
@@ -2652,7 +2718,8 @@ public static class IfExpr implements Expr, MaybePrimitiveExpr{
             finally{
                 Var.popThreadBindings();
                 }
-			return new IfExpr((Integer) LINE.deref(),
+			return new IfExpr(lineDeref(),
+                              columnDeref(),
 			                  testexpr,
 			                  thenexpr,
 			                  elseexpr);
@@ -2687,6 +2754,49 @@ static final public IPersistentMap CHAR_MAP =
 '\\', "_BSLASH_",
 '?', "_QMARK_");
 
+static final public IPersistentMap DEMUNGE_MAP;
+static final public Pattern DEMUNGE_PATTERN;
+
+static {
+	// DEMUNGE_MAP maps strings to characters in the opposite
+	// direction that CHAR_MAP does, plus it maps "$" to '/'
+	IPersistentMap m = RT.map("$", '/');
+	for(ISeq s = RT.seq(CHAR_MAP); s != null; s = s.next())
+		{
+		IMapEntry e = (IMapEntry) s.first();
+		Character origCh = (Character) e.key();
+		String escapeStr = (String) e.val();
+		m = m.assoc(escapeStr, origCh);
+		}
+	DEMUNGE_MAP = m;
+
+	// DEMUNGE_PATTERN searches for the first of any occurrence of
+	// the strings that are keys of DEMUNGE_MAP.
+	// Note: Regex matching rules mean that #"_|_COLON_" "_COLON_"
+       // returns "_", but #"_COLON_|_" "_COLON_" returns "_COLON_"
+       // as desired.  Sorting string keys of DEMUNGE_MAP from longest to
+       // shortest ensures correct matching behavior, even if some strings are
+	// prefixes of others.
+	Object[] mungeStrs = RT.toArray(RT.keys(m));
+	Arrays.sort(mungeStrs, new Comparator() {
+                public int compare(Object s1, Object s2) {
+                    return ((String) s2).length() - ((String) s1).length();
+                }});
+	StringBuilder sb = new StringBuilder();
+	boolean first = true;
+	for(Object s : mungeStrs)
+		{
+		String escapeStr = (String) s;
+		if (!first)
+			sb.append("|");
+		first = false;
+		sb.append("\\Q");
+		sb.append(escapeStr);
+		sb.append("\\E");
+		}
+	DEMUNGE_PATTERN = Pattern.compile(sb.toString());
+}
+
 static public String munge(String name){
 	StringBuilder sb = new StringBuilder();
 	for(char c : name.toCharArray())
@@ -2700,6 +2810,26 @@ static public String munge(String name){
 	return sb.toString();
 }
 
+static public String demunge(String mungedName){
+	StringBuilder sb = new StringBuilder();
+	Matcher m = DEMUNGE_PATTERN.matcher(mungedName);
+	int lastMatchEnd = 0;
+	while (m.find())
+		{
+		int start = m.start();
+		int end = m.end();
+		// Keep everything before the match
+		sb.append(mungedName.substring(lastMatchEnd, start));
+		lastMatchEnd = end;
+		// Replace the match with DEMUNGE_MAP result
+		Character origCh = (Character) DEMUNGE_MAP.valAt(m.group());
+		sb.append(origCh);
+		}
+	// Keep everything after the last match
+	sb.append(mungedName.substring(lastMatchEnd));
+	return sb.toString();
+}
+
 public static class EmptyExpr implements Expr{
 	public final Object coll;
 	final static Type HASHMAP_TYPE = Type.getType(PersistentArrayMap.class);
@@ -2788,6 +2918,7 @@ public static class ListExpr implements Expr{
 public static class MapExpr implements Expr{
 	public final IPersistentVector keyvals;
 	final static Method mapMethod = Method.getMethod("clojure.lang.IPersistentMap map(Object[])");
+	final static Method mapUniqueKeysMethod = Method.getMethod("clojure.lang.IPersistentMap mapUniqueKeys(Object[])");
 
 
 	public MapExpr(IPersistentVector keyvals){
@@ -2802,8 +2933,28 @@ public static class MapExpr implements Expr{
 	}
 
 	public void emit(C context, ObjExpr objx, GeneratorAdapter gen){
+		boolean allKeysConstant = true;
+		boolean allConstantKeysUnique = true;
+		IPersistentSet constantKeys = PersistentHashSet.EMPTY;
+		for(int i = 0; i < keyvals.count(); i+=2)
+			{
+			Expr k = (Expr) keyvals.nth(i);
+			if(k instanceof LiteralExpr)
+				{
+				Object kval = k.eval();
+				if (constantKeys.contains(kval))
+					allConstantKeysUnique = false;
+				else
+					constantKeys = (IPersistentSet)constantKeys.cons(kval);
+				}
+			else
+				allKeysConstant = false;
+			}
 		MethodExpr.emitArgsAsArray(keyvals, objx, gen);
-		gen.invokeStatic(RT_TYPE, mapMethod);
+		if((allKeysConstant && allConstantKeysUnique) || (keyvals.count() <= 2))
+			gen.invokeStatic(RT_TYPE, mapUniqueKeysMethod);
+		else
+			gen.invokeStatic(RT_TYPE, mapMethod);
 		if(context == C.STATEMENT)
 			gen.pop();
 	}
@@ -2819,7 +2970,10 @@ public static class MapExpr implements Expr{
 
 	static public Expr parse(C context, IPersistentMap form) {
 		IPersistentVector keyvals = PersistentVector.EMPTY;
-		boolean constant = true;
+		boolean keysConstant = true;
+		boolean valsConstant = true;
+		boolean allConstantKeysUnique = true;
+		IPersistentSet constantKeys = PersistentHashSet.EMPTY;
 		for(ISeq s = RT.seq(form); s != null; s = s.next())
 			{
 			IMapEntry e = (IMapEntry) s.first();
@@ -2827,23 +2981,41 @@ public static class MapExpr implements Expr{
 			Expr v = analyze(context == C.EVAL ? context : C.EXPRESSION, e.val());
 			keyvals = (IPersistentVector) keyvals.cons(k);
 			keyvals = (IPersistentVector) keyvals.cons(v);
-			if(!(k instanceof LiteralExpr && v instanceof LiteralExpr))
-				constant = false;
+			if(k instanceof LiteralExpr)
+				{
+				Object kval = k.eval();
+				if (constantKeys.contains(kval))
+					allConstantKeysUnique = false;
+				else
+					constantKeys = (IPersistentSet)constantKeys.cons(kval);
+				}
+			else
+				keysConstant = false;
+			if(!(v instanceof LiteralExpr))
+				valsConstant = false;
 			}
 
 		Expr ret = new MapExpr(keyvals);
 		if(form instanceof IObj && ((IObj) form).meta() != null)
 			return new MetaExpr(ret, MapExpr
 					.parse(context == C.EVAL ? context : C.EXPRESSION, ((IObj) form).meta()));
-		else if(constant)
+		else if(keysConstant)
 			{
-			IPersistentMap m = PersistentHashMap.EMPTY;
-			for(int i=0;i<keyvals.length();i+= 2)
+			// TBD: Add more detail to exception thrown below.
+			if(!allConstantKeysUnique)
+				throw new IllegalArgumentException("Duplicate constant keys in map");
+			if(valsConstant)
 				{
-				m = m.assoc(((LiteralExpr)keyvals.nth(i)).val(), ((LiteralExpr)keyvals.nth(i+1)).val());
+				IPersistentMap m = PersistentHashMap.EMPTY;
+				for(int i=0;i<keyvals.length();i+= 2)
+					{
+					m = m.assoc(((LiteralExpr)keyvals.nth(i)).val(), ((LiteralExpr)keyvals.nth(i+1)).val());
+					}
+//				System.err.println("Constant: " + m);
+				return new ConstantExpr(m);
 				}
-//			System.err.println("Constant: " + m);
-			return new ConstantExpr(m);
+			else
+				return ret;
 			}
 		else
 			return ret;
@@ -2982,15 +3154,17 @@ static class KeywordInvokeExpr implements Expr{
 	public final Object tag;
 	public final Expr target;
 	public final int line;
+	public final int column;
 	public final int siteIndex;
 	public final String source;
 	static Type ILOOKUP_TYPE = Type.getType(ILookup.class);
 
-	public KeywordInvokeExpr(String source, int line, Symbol tag, KeywordExpr kw, Expr target){
+	public KeywordInvokeExpr(String source, int line, int column, Symbol tag, KeywordExpr kw, Expr target){
 		this.source = source;
 		this.kw = kw;
 		this.target = target;
 		this.line = line;
+		this.column = column;
 		this.tag = tag;
 		this.siteIndex = registerKeywordCallsite(kw.k);
 	}
@@ -3003,7 +3177,7 @@ static class KeywordInvokeExpr implements Expr{
 		catch(Throwable e)
 			{
 			if(!(e instanceof CompilerException))
-				throw new CompilerException(source, line, e);
+				throw new CompilerException(source, line, column, e);
 			else
 				throw (CompilerException) e;
 			}
@@ -3056,13 +3230,15 @@ static class KeywordInvokeExpr implements Expr{
 //	public final Object tag;
 //	public final Expr target;
 //	public final int line;
+//	public final int column;
 //	public final String source;
 //
-//	public KeywordSiteInvokeExpr(String source, int line, Symbol tag, Expr site, Expr target){
+//	public KeywordSiteInvokeExpr(String source, int line, int column, Symbol tag, Expr site, Expr target){
 //		this.source = source;
 //		this.site = site;
 //		this.target = target;
 //		this.line = line;
+//		this.column = column;
 //		this.tag = tag;
 //	}
 //
@@ -3075,7 +3251,7 @@ static class KeywordInvokeExpr implements Expr{
 //		catch(Throwable e)
 //			{
 //			if(!(e instanceof CompilerException))
-//				throw new CompilerException(source, line, e);
+//				throw new CompilerException(source, line, column, e);
 //			else
 //				throw (CompilerException) e;
 //			}
@@ -3201,21 +3377,14 @@ static class StaticInvokeExpr implements Expr, MaybePrimitiveExpr{
 			for(int i = 0; i < paramclasses.length - 1; i++)
 				{
 				Expr e = (Expr) args.nth(i);
-				try
+				if(maybePrimitiveType(e) == paramclasses[i])
 					{
-					if(maybePrimitiveType(e) == paramclasses[i])
-						{
-						((MaybePrimitiveExpr) e).emitUnboxed(C.EXPRESSION, objx, gen);
-						}
-					else
-						{
-						e.emit(C.EXPRESSION, objx, gen);
-						HostExpr.emitUnboxArg(objx, gen, paramclasses[i]);
-						}
+					((MaybePrimitiveExpr) e).emitUnboxed(C.EXPRESSION, objx, gen);
 					}
-				catch(Exception ex)
+				else
 					{
-					throw Util.sneakyThrow(ex);
+					e.emit(C.EXPRESSION, objx, gen);
+					HostExpr.emitUnboxArg(objx, gen, paramclasses[i]);
 					}
 				}
 			IPersistentVector restArgs = RT.subvec(args,paramclasses.length - 1,args.count());
@@ -3307,6 +3476,7 @@ static class InvokeExpr implements Expr{
 	public final Object tag;
 	public final IPersistentVector args;
 	public final int line;
+	public final int column;
 	public final String source;
 	public boolean isProtocol = false;
 	public boolean isDirect = false;
@@ -3316,11 +3486,12 @@ static class InvokeExpr implements Expr{
 	static Keyword onKey = Keyword.intern("on");
 	static Keyword methodMapKey = Keyword.intern("method-map");
 
-	public InvokeExpr(String source, int line, Symbol tag, Expr fexpr, IPersistentVector args) {
+	public InvokeExpr(String source, int line, int column, Symbol tag, Expr fexpr, IPersistentVector args) {
 		this.source = source;
 		this.fexpr = fexpr;
 		this.args = args;
 		this.line = line;
+		this.column = column;
 		if(fexpr instanceof VarExpr)
 			{
 			Var fvar = ((VarExpr)fexpr).var;
@@ -3384,7 +3555,7 @@ static class InvokeExpr implements Expr{
 		catch(Throwable e)
 			{
 			if(!(e instanceof CompilerException))
-				throw new CompilerException(source, line, e);
+				throw new CompilerException(source, line, column, e);
 			else
 				throw (CompilerException) e;
 			}
@@ -3418,8 +3589,7 @@ static class InvokeExpr implements Expr{
 		e.emit(C.EXPRESSION, objx, gen);
 		gen.dup(); //target, target
 		gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
-		gen.loadThis();
-		gen.getField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,cached-class
+		gen.getStatic(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target,class,cached-class
 		gen.visitJumpInsn(IF_ACMPEQ, callLabel); //target
 		if(protocolOn != null)
 			{
@@ -3430,9 +3600,7 @@ static class InvokeExpr implements Expr{
 
 		gen.dup(); //target, target
 		gen.invokeStatic(UTIL_TYPE,Method.getMethod("Class classOf(Object)")); //target,class
-		gen.loadThis();
-		gen.swap();
-		gen.putField(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target
+		gen.putStatic(objx.objtype, objx.cachedClassName(siteIndex),CLASS_TYPE); //target
 
 		gen.mark(callLabel); //target
 		objx.emitVar(gen, v);
@@ -3495,13 +3663,16 @@ static class InvokeExpr implements Expr{
 		if(context != C.EVAL)
 			context = C.EXPRESSION;
 		Expr fexpr = analyze(context, form.first());
-		if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE))
+		if(fexpr instanceof VarExpr && ((VarExpr)fexpr).var.equals(INSTANCE) && RT.count(form) == 3)
 			{
-			if(RT.second(form) instanceof Symbol)
+			Expr sexpr = analyze(C.EXPRESSION, RT.second(form));
+			if(sexpr instanceof ConstantExpr)
 				{
-				Class c = HostExpr.maybeClass(RT.second(form),false);
-				if(c != null)
-					return new InstanceOfExpr(c, analyze(context, RT.third(form)));
+				Object val = ((ConstantExpr) sexpr).val();
+				if(val instanceof Class)
+					{
+					return new InstanceOfExpr((Class) val, analyze(context, RT.third(form)));
+					}
 				}
 			}
 
@@ -3539,7 +3710,7 @@ static class InvokeExpr implements Expr{
 			{
 //			fexpr = new ConstantExpr(new KeywordCallSite(((KeywordExpr)fexpr).k));
 			Expr target = analyze(context, RT.second(form));
-			return new KeywordInvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form),
+			return new KeywordInvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form),
 			                             (KeywordExpr) fexpr, target);
 			}
 		PersistentVector args = PersistentVector.EMPTY;
@@ -3551,7 +3722,7 @@ static class InvokeExpr implements Expr{
 //			throw new IllegalArgumentException(
 //					String.format("No more than %d args supported", MAX_POSITIONAL_ARITY));
 
-		return new InvokeExpr((String) SOURCE.deref(), (Integer) LINE.deref(), tagOf(form), fexpr, args);
+		return new InvokeExpr((String) SOURCE.deref(), lineDeref(), columnDeref(), tagOf(form), fexpr, args);
 	}
 }
 
@@ -3643,7 +3814,7 @@ static public class FnExpr extends ObjExpr{
 		try
 			{
 			Var.pushThreadBindings(
-					RT.map(CONSTANTS, PersistentVector.EMPTY,
+					RT.mapUniqueKeys(CONSTANTS, PersistentVector.EMPTY,
 					       CONSTANT_IDS, new IdentityHashMap(),
 					       KEYWORDS, PersistentHashMap.EMPTY,
 					       VARS, PersistentHashMap.EMPTY,
@@ -3666,7 +3837,8 @@ static public class FnExpr extends ObjExpr{
 			//turn former into latter
 			if(RT.second(form) instanceof IPersistentVector)
 				form = RT.list(FN, RT.next(form));
-			fn.line = (Integer) LINE.deref();
+			fn.line = lineDeref();
+			fn.column = columnDeref();
 			FnMethod[] methodArray = new FnMethod[MAX_POSITIONAL_ARITY + 1];
 			FnMethod variadicMethod = null;
 			for(ISeq s = RT.next(form); s != null; s = RT.next(s))
@@ -3723,7 +3895,7 @@ static public class FnExpr extends ObjExpr{
 		fn.hasPrimSigs = prims.size() > 0;
 		IPersistentMap fmeta = RT.meta(origForm);
 		if(fmeta != null)
-			fmeta = fmeta.without(RT.LINE_KEY).without(RT.FILE_KEY);
+			fmeta = fmeta.without(RT.LINE_KEY).without(RT.COLUMN_KEY).without(RT.FILE_KEY);
 
 		fn.hasMeta = RT.count(fmeta) > 0;
 
@@ -3806,6 +3978,7 @@ static public class ObjExpr implements Expr{
 	IPersistentMap vars = PersistentHashMap.EMPTY;
 	Class compiledClass;
 	int line;
+	int column;
 	PersistentVector constants;
 	int constantsID;
 	int altCtorDrops = 0;
@@ -3861,6 +4034,10 @@ static public class ObjExpr implements Expr{
 		return line;
 	}
 
+	public final int column(){
+		return column;
+	}
+
 	public final PersistentVector constants(){
 		return constants;
 	}
@@ -3930,6 +4107,8 @@ static public class ObjExpr implements Expr{
 		String source = (String) SOURCE.deref();
 		int lineBefore = (Integer) LINE_BEFORE.deref();
 		int lineAfter = (Integer) LINE_AFTER.deref() + 1;
+		int columnBefore = (Integer) COLUMN_BEFORE.deref();
+		int columnAfter = (Integer) COLUMN_AFTER.deref() + 1;
 
 		if(source != null && SOURCE_PATH.deref() != null)
 			{
@@ -4059,12 +4238,10 @@ static public class ObjExpr implements Expr{
 				}
 			}
 
-		//instance fields for callsites and thunks
+		//static fields for callsites and thunks
 		for(int i=0;i<protocolCallsites.count();i++)
 			{
-			cv.visitField(ACC_PRIVATE, cachedClassName(i), CLASS_TYPE.getDescriptor(), null, null);
-			cv.visitField(ACC_PRIVATE, cachedProtoFnName(i), AFUNCTION_TYPE.getDescriptor(), null, null);
-			cv.visitField(ACC_PRIVATE, cachedProtoImplName(i), IFN_TYPE.getDescriptor(), null, null);			
+			cv.visitField(ACC_PRIVATE + ACC_STATIC, cachedClassName(i), CLASS_TYPE.getDescriptor(), null, null);
 			}
 
  		//ctor that takes closed-overs and inits base + fields
@@ -4569,19 +4746,12 @@ static public class ObjExpr implements Expr{
 
 	synchronized Class getCompiledClass(){
 		if(compiledClass == null)
-			try
+//			if(RT.booleanCast(COMPILE_FILES.deref()))
+//				compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode);
+//			else
 				{
-//				if(RT.booleanCast(COMPILE_FILES.deref()))
-//					compiledClass = RT.classForName(name);//loader.defineClass(name, bytecode);
-//				else
-					{
-					loader = (DynamicClassLoader) LOADER.deref();
-					compiledClass = loader.defineClass(name, bytecode, src);
-					}
-				}
-			catch(Exception e)
-				{
-				throw Util.sneakyThrow(e);
+				loader = (DynamicClassLoader) LOADER.deref();
+				compiledClass = loader.defineClass(name, bytecode, src);
 				}
 		return compiledClass;
 	}
@@ -4827,14 +4997,6 @@ static public class ObjExpr implements Expr{
 		return "__cached_var__" + n;
 	}
 
-	String cachedProtoFnName(int n){
-		return "__cached_proto_fn__" + n;
-	}
-
-	String cachedProtoImplName(int n){
-		return "__cached_proto_impl__" + n;
-	}
-
 	String varCallsiteName(int n){
 		return "__var__callsite__" + n;
 	}
@@ -4942,13 +5104,14 @@ public static class FnMethod extends ObjMethod{
 		try
 			{
 			FnMethod method = new FnMethod(objx, (ObjMethod) METHOD.deref());
-			method.line = (Integer) LINE.deref();
+			method.line = lineDeref();
+			method.column = columnDeref();
 			//register as the current method and set up a new env frame
             PathNode pnode =  (PathNode) CLEAR_PATH.get();
 			if(pnode == null)
 				pnode = new PathNode(PATHTYPE.PATH,null);
 			Var.pushThreadBindings(
-					RT.map(
+					RT.mapUniqueKeys(
 							METHOD, method,
 							LOCAL_ENV, LOCAL_ENV.deref(),
 							LOOP_LOCALS, null,
@@ -5093,10 +5256,6 @@ public static class FnMethod extends ObjMethod{
 				gen.visitLocalVariable(lb.name, argtypes[lb.idx].getDescriptor(), null, loopLabel, end, lb.idx);
 				}
 			}
-		catch(Exception e)
-			{
-			throw Util.sneakyThrow(e);
-			}
 		finally
 			{
 			Var.popThreadBindings();
@@ -5132,7 +5291,11 @@ public static class FnMethod extends ObjMethod{
 	}
 
 	public void doEmitPrim(ObjExpr fn, ClassVisitor cv){
-		Method ms = new Method("invokePrim", getReturnType(), argtypes);
+		Type returnType;
+		if (retClass == double.class || retClass == long.class)
+			returnType = getReturnType();
+		else returnType = OBJECT_TYPE;
+		Method ms = new Method("invokePrim", returnType, argtypes);
 
 		GeneratorAdapter gen = new GeneratorAdapter(ACC_PUBLIC + ACC_FINAL,
 		                                            ms,
@@ -5157,10 +5320,6 @@ public static class FnMethod extends ObjMethod{
 				gen.visitLocalVariable(lb.name, argtypes[lb.idx-1].getDescriptor(), null, loopLabel, end, lb.idx);
 				}
 			}
-		catch(Exception e)
-			{
-			throw Util.sneakyThrow(e);
-			}
 		finally
 			{
 			Var.popThreadBindings();
@@ -5312,6 +5471,7 @@ abstract public static class ObjMethod{
 	PersistentVector argLocals;
 	int maxLocal = 0;
 	int line;
+	int column;
 	PersistentHashSet localsUsedInCatchFinally = PersistentHashSet.EMPTY;
 	protected IPersistentMap methodMeta;
 
@@ -5340,6 +5500,10 @@ abstract public static class ObjMethod{
 		return line;
 	}
 
+	public final int column(){
+		return column;
+	}
+
 	public ObjMethod(ObjExpr objx, ObjMethod parent){
 		this.parent = parent;
 		this.objx = objx;
@@ -5716,7 +5880,7 @@ public static class LetFnExpr implements Expr{
 			ISeq body = RT.next(RT.next(form));
 
 			if(context == C.EVAL)
-				return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
+				return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
 
 			IPersistentMap dynamicBindings = RT.map(LOCAL_ENV, LOCAL_ENV.deref(),
 			                                        NEXT_LOCAL_NUM, NEXT_LOCAL_NUM.deref());
@@ -5845,7 +6009,7 @@ public static class LetExpr implements Expr, MaybePrimitiveExpr{
 
 			if(context == C.EVAL
 			   || (context == C.EXPRESSION && isLoop))
-				return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
+				return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
 
 			ObjMethod method = (ObjMethod) METHOD.deref();
 			IPersistentMap backupMethodLocals = method.locals;
@@ -5863,6 +6027,9 @@ public static class LetExpr implements Expr, MaybePrimitiveExpr{
 				method.locals = backupMethodLocals;
 				method.indexlocals = backupMethodIndexLocals;
 
+				PathNode looproot = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());
+				PathNode clearroot = new PathNode(PATHTYPE.PATH,looproot);
+				PathNode clearpath = new PathNode(PATHTYPE.PATH,looproot);
 				if(isLoop)
 					dynamicBindings = dynamicBindings.assoc(LOOP_LOCALS, null);
 
@@ -5885,22 +6052,37 @@ public static class LetExpr implements Expr, MaybePrimitiveExpr{
 							{
 							if(recurMismatches != null && RT.booleanCast(recurMismatches.nth(i/2)))
 								{
-								init = new StaticMethodExpr("", 0, null, RT.class, "box", RT.vector(init));
+								init = new StaticMethodExpr("", 0, 0, null, RT.class, "box", RT.vector(init));
 								if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
 									RT.errPrintWriter().println("Auto-boxing loop arg: " + sym);
 								}
 							else if(maybePrimitiveType(init) == int.class)
-								init = new StaticMethodExpr("", 0, null, RT.class, "longCast", RT.vector(init));
+								init = new StaticMethodExpr("", 0, 0, null, RT.class, "longCast", RT.vector(init));
 							else if(maybePrimitiveType(init) == float.class)
-								init = new StaticMethodExpr("", 0, null, RT.class, "doubleCast", RT.vector(init));
+								init = new StaticMethodExpr("", 0, 0, null, RT.class, "doubleCast", RT.vector(init));
 							}
 						//sequential enhancement of env (like Lisp let*)
-						LocalBinding lb = registerLocal(sym, tagOf(sym), init,false);
-						BindingInit bi = new BindingInit(lb, init);
-						bindingInits = bindingInits.cons(bi);
+						try
+							{
+							if(isLoop)
+								{
+	                            Var.pushThreadBindings(
+									RT.map(CLEAR_PATH, clearpath,
+	                                       CLEAR_ROOT, clearroot,
+	                                       NO_RECUR, null));
 
-						if(isLoop)
-							loopLocals = loopLocals.cons(lb);
+								}
+							LocalBinding lb = registerLocal(sym, tagOf(sym), init,false);
+							BindingInit bi = new BindingInit(lb, init);
+							bindingInits = bindingInits.cons(bi);
+							if(isLoop)
+								loopLocals = loopLocals.cons(lb);
+							}
+						finally
+							{
+							if(isLoop)
+							    Var.popThreadBindings();
+							}
 						}
 					if(isLoop)
 						LOOP_LOCALS.set(loopLocals);
@@ -5909,11 +6091,10 @@ public static class LetExpr implements Expr, MaybePrimitiveExpr{
 					try {
 						if(isLoop)
 							{
-							PathNode root = new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());
-                                                        Var.pushThreadBindings(
-								RT.map(CLEAR_PATH, new PathNode(PATHTYPE.PATH,root),
-                                                                       CLEAR_ROOT, new PathNode(PATHTYPE.PATH,root),
-                                                                       NO_RECUR, null));
+                            Var.pushThreadBindings(
+								RT.map(CLEAR_PATH, clearpath,
+                                       CLEAR_ROOT, clearroot,
+                                       NO_RECUR, null));
                                                        
 							}
 						bodyExpr = (new BodyExpr.Parser()).parse(isLoop ? C.RETURN : context, body);
@@ -6029,17 +6210,19 @@ public static class LetExpr implements Expr, MaybePrimitiveExpr{
 
 }
 
-public static class RecurExpr implements Expr{
+public static class RecurExpr implements Expr, MaybePrimitiveExpr{
 	public final IPersistentVector args;
 	public final IPersistentVector loopLocals;
 	final int line;
+	final int column;
 	final String source;
 
 
-	public RecurExpr(IPersistentVector loopLocals, IPersistentVector args, int line, String source){
+	public RecurExpr(IPersistentVector loopLocals, IPersistentVector args, int line, int column, String source){
 		this.loopLocals = loopLocals;
 		this.args = args;
 		this.line = line;
+		this.column = column;
 		this.source = source;
 	}
 
@@ -6058,49 +6241,42 @@ public static class RecurExpr implements Expr{
 			if(lb.getPrimitiveType() != null)
 				{
 				Class primc = lb.getPrimitiveType();
-				try
+				final Class pc = maybePrimitiveType(arg);
+				if(pc == primc)
+					((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
+				else if(primc == long.class && pc == int.class)
 					{
-					final Class pc = maybePrimitiveType(arg);
-					if(pc == primc)
-						((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
-					else if(primc == long.class && pc == int.class)
-						{
-						((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
-						gen.visitInsn(I2L);						
-						}
-					else if(primc == double.class && pc == float.class)
-						{
-						((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
-						gen.visitInsn(F2D);						
-						}
-					else if(primc == int.class && pc == long.class)
-						{
-						((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
-						gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
-						}
-					else if(primc == float.class && pc == double.class)
-						{
-						((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
-						gen.visitInsn(D2F);						
-						}
-					else
-						{
-//						if(true)//RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
-							throw new IllegalArgumentException
-//							RT.errPrintWriter().println
-								(//source + ":" + line +
-								 " recur arg for primitive local: " +
-						                                   lb.name + " is not matching primitive, had: " +
+					((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
+					gen.visitInsn(I2L);
+					}
+				else if(primc == double.class && pc == float.class)
+					{
+					((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
+					gen.visitInsn(F2D);
+					}
+				else if(primc == int.class && pc == long.class)
+					{
+					((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
+					gen.invokeStatic(RT_TYPE, Method.getMethod("int intCast(long)"));
+					}
+				else if(primc == float.class && pc == double.class)
+					{
+					((MaybePrimitiveExpr) arg).emitUnboxed(C.EXPRESSION, objx, gen);
+					gen.visitInsn(D2F);
+					}
+				else
+					{
+//					if(true)//RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
+						throw new IllegalArgumentException
+//						RT.errPrintWriter().println
+							(//source + ":" + line +
+							 " recur arg for primitive local: " +
+					                                   lb.name + " is not matching primitive, had: " +
 															(arg.hasJavaClass() ? arg.getJavaClass().getName():"Object") +
 															", needed: " +
 															primc.getName());
-//						arg.emit(C.EXPRESSION, objx, gen);
-//						HostExpr.emitUnboxArg(objx,gen,primc);
-						}
-					}
-				catch(Exception e)
-					{
-					throw Util.sneakyThrow(e);
+//					arg.emit(C.EXPRESSION, objx, gen);
+//					HostExpr.emitUnboxArg(objx,gen,primc);
 					}
 				}
 			else
@@ -6132,20 +6308,19 @@ public static class RecurExpr implements Expr{
 	}
 
 	public Class getJavaClass() {
-		return null;
+		return RECUR_CLASS;
 	}
 
 	static class Parser implements IParser{
 		public Expr parse(C context, Object frm) {
-			int line = (Integer) LINE.deref();
+			int line = lineDeref();
+			int column = columnDeref();
 			String source = (String) SOURCE.deref();
 
 			ISeq form = (ISeq) frm;
 			IPersistentVector loopLocals = (IPersistentVector) LOOP_LOCALS.deref();
 			if(context != C.RETURN || loopLocals == null)
 				throw new UnsupportedOperationException("Can only recur from tail position");
-			if(IN_CATCH_FINALLY.deref() != null)
-				throw new UnsupportedOperationException("Cannot recur from catch/finally");
                         if(NO_RECUR.deref() != null)
                             throw new UnsupportedOperationException("Cannot recur across try");
 			PersistentVector args = PersistentVector.EMPTY;
@@ -6194,9 +6369,17 @@ public static class RecurExpr implements Expr{
 						}
 					}
 				}
-			return new RecurExpr(loopLocals, args, line, source);
+			return new RecurExpr(loopLocals, args, line, column, source);
 		}
 	}
+
+	public boolean canEmitPrimitive() {
+		return true;
+	}
+
+	public void emitUnboxed(C context, ObjExpr objx, GeneratorAdapter gen) {
+		emit(context, objx, gen);
+	}
 }
 
 private static LocalBinding registerLocal(Symbol sym, Symbol tag, Expr init, boolean isArg) {
@@ -6278,7 +6461,7 @@ private static Expr analyze(C context, Object form, String name) {
 	catch(Throwable e)
 		{
 		if(!(e instanceof CompilerException))
-			throw new CompilerException((String) SOURCE_PATH.deref(), (Integer) LINE.deref(), e);
+			throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(), e);
 		else
 			throw (CompilerException) e;
 		}
@@ -6287,9 +6470,12 @@ private static Expr analyze(C context, Object form, String name) {
 static public class CompilerException extends RuntimeException{
 	final public String source;
 	
-	public CompilerException(String source, int line, Throwable cause){
-		super(errorMsg(source, line, cause.toString()), cause);
+	final public int line;
+
+	public CompilerException(String source, int line, int column, Throwable cause){
+		super(errorMsg(source, line, column, cause.toString()), cause);
 		this.source = source;
+		this.line = line;
 	}
 
 	public String toString(){
@@ -6431,11 +6617,14 @@ static Object macroexpand(Object form) {
 }
 
 private static Expr analyzeSeq(C context, ISeq form, String name) {
-	Integer line = (Integer) LINE.deref();
+	Object line = lineDeref();
+	Object column = columnDeref();
 	if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
-		line = (Integer) RT.meta(form).valAt(RT.LINE_KEY);
+		line = RT.meta(form).valAt(RT.LINE_KEY);
+	if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
+		column = RT.meta(form).valAt(RT.COLUMN_KEY);
 	Var.pushThreadBindings(
-			RT.map(LINE, line));
+			RT.map(LINE, line, COLUMN, column));
 	try
 		{
 		Object me = macroexpand1(form);
@@ -6459,7 +6648,7 @@ private static Expr analyzeSeq(C context, ISeq form, String name) {
 	catch(Throwable e)
 		{
 		if(!(e instanceof CompilerException))
-			throw new CompilerException((String) SOURCE_PATH.deref(), (Integer) LINE.deref(), e);
+			throw new CompilerException((String) SOURCE_PATH.deref(), lineDeref(), columnDeref(), e);
 		else
 			throw (CompilerException) e;
 		}
@@ -6469,8 +6658,8 @@ private static Expr analyzeSeq(C context, ISeq form, String name) {
 		}
 }
 
-static String errorMsg(String source, int line, String s){
-	return String.format("%s, compiling:(%s:%d)", s, source, line);
+static String errorMsg(String source, int line, int column, String s){
+	return String.format("%s, compiling:(%s:%d:%d)", s, source, line, column);
 }
 
 public static Object eval(Object form) {
@@ -6486,14 +6675,17 @@ public static Object eval(Object form, boolean freshLoader) {
 		}
 	try
 		{
-		Integer line = (Integer) LINE.deref();
+		Object line = lineDeref();
+		Object column = columnDeref();
 		if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
-			line = (Integer) RT.meta(form).valAt(RT.LINE_KEY);
-		Var.pushThreadBindings(RT.map(LINE, line));
+			line = RT.meta(form).valAt(RT.LINE_KEY);
+		if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
+			column = RT.meta(form).valAt(RT.COLUMN_KEY);
+		Var.pushThreadBindings(RT.map(LINE, line, COLUMN, column));
 		try
 			{
 			form = macroexpand(form);
-			if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO))
+			if(form instanceof ISeq && Util.equals(RT.first(form), DO))
 				{
 				ISeq s = RT.next(form);
 				for(; RT.next(s) != null; s = RT.next(s))
@@ -6516,12 +6708,6 @@ public static Object eval(Object form, boolean freshLoader) {
 				return expr.eval();
 				}
 			}
-		catch(Throwable e)
-			{
-			if(!(e instanceof RuntimeException))
-				throw Util.sneakyThrow(e);
-			throw (RuntimeException)e;
-			}
 		finally
 			{
 			Var.popThreadBindings();
@@ -6619,25 +6805,13 @@ static PathNode commonPath(PathNode n1, PathNode n2){
 }
 
 static void addAnnotation(Object visitor, IPersistentMap meta){
-	try{
 	if(meta != null && ADD_ANNOTATIONS.isBound())
 		 ADD_ANNOTATIONS.invoke(visitor, meta);
-	}
-	catch (Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
 }
 
 static void addParameterAnnotation(Object visitor, IPersistentMap meta, int i){
-	try{
 	if(meta != null && ADD_ANNOTATIONS.isBound())
 		 ADD_ANNOTATIONS.invoke(visitor, meta, i);
-	}
-	catch (Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
 }
 
 private static Expr analyzeSymbol(Symbol sym) {
@@ -6659,7 +6833,7 @@ private static Expr analyzeSymbol(Symbol sym) {
 			if(c != null)
 				{
 				if(Reflector.getField(c, sym.name, true) != null)
-					return new StaticFieldExpr((Integer) LINE.deref(), c, sym.name, tag);
+					return new StaticFieldExpr(lineDeref(), columnDeref(), c, sym.name, tag);
 				throw Util.runtimeException("Unable to find static field: " + sym.name + " in " + c);
 				}
 			}
@@ -6928,16 +7102,19 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) {
 			(rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr :
 			new LineNumberingPushbackReader(rdr);
 	Var.pushThreadBindings(
-			RT.map(LOADER, RT.makeClassLoader(),
+			RT.mapUniqueKeys(LOADER, RT.makeClassLoader(),
 			       SOURCE_PATH, sourcePath,
 			       SOURCE, sourceName,
 			       METHOD, null,
 			       LOCAL_ENV, null,
 					LOOP_LOCALS, null,
 					NEXT_LOCAL_NUM, 0,
+					RT.READEVAL, RT.T,
 			       RT.CURRENT_NS, RT.CURRENT_NS.deref(),
 			       LINE_BEFORE, pushbackReader.getLineNumber(),
-			       LINE_AFTER, pushbackReader.getLineNumber()
+			       COLUMN_BEFORE, pushbackReader.getColumnNumber(),
+			       LINE_AFTER, pushbackReader.getLineNumber(),
+			       COLUMN_AFTER, pushbackReader.getColumnNumber()
 			       ,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()
 					,RT.WARN_ON_REFLECTION, RT.WARN_ON_REFLECTION.deref()
 			       ,RT.DATA_READERS, RT.DATA_READERS.deref()
@@ -6949,13 +7126,22 @@ public static Object load(Reader rdr, String sourcePath, String sourceName) {
 		    r = LispReader.read(pushbackReader, false, EOF, false))
 			{
 			LINE_AFTER.set(pushbackReader.getLineNumber());
+			COLUMN_AFTER.set(pushbackReader.getColumnNumber());
 			ret = eval(r,false);
 			LINE_BEFORE.set(pushbackReader.getLineNumber());
+			COLUMN_BEFORE.set(pushbackReader.getColumnNumber());
 			}
 		}
 	catch(LispReader.ReaderException e)
 		{
-		throw new CompilerException(sourcePath, e.line, e.getCause());
+		throw new CompilerException(sourcePath, e.line, e.column, e.getCause());
+		}
+	catch(Throwable e)
+		{
+		if(!(e instanceof CompilerException))
+			throw new CompilerException(sourcePath, (Integer) LINE_BEFORE.deref(), (Integer) COLUMN_BEFORE.deref(), e);
+		else
+			throw (CompilerException) e;
 		}
 	finally
 		{
@@ -7000,7 +7186,9 @@ public static void pushNSandLoader(ClassLoader loader){
 	Var.pushThreadBindings(RT.map(Var.intern(Symbol.intern("clojure.core"),
 	                                         Symbol.intern("*ns*")).setDynamic(),
 	                              null,
-	                              RT.FN_LOADER_VAR, loader));
+	                              RT.FN_LOADER_VAR, loader,
+	                              RT.READEVAL, RT.T
+	                              ));
 }
 
 public static ILookupThunk getLookupThunk(Object target, Keyword k){
@@ -7008,17 +7196,20 @@ public static ILookupThunk getLookupThunk(Object target, Keyword k){
 }
 
 static void compile1(GeneratorAdapter gen, ObjExpr objx, Object form) {
-	Integer line = (Integer) LINE.deref();
+	Object line = lineDeref();
+	Object column = columnDeref();
 	if(RT.meta(form) != null && RT.meta(form).containsKey(RT.LINE_KEY))
-		line = (Integer) RT.meta(form).valAt(RT.LINE_KEY);
+		line = RT.meta(form).valAt(RT.LINE_KEY);
+	if(RT.meta(form) != null && RT.meta(form).containsKey(RT.COLUMN_KEY))
+		column = RT.meta(form).valAt(RT.COLUMN_KEY);
 	Var.pushThreadBindings(
-			RT.map(LINE, line
+			RT.map(LINE, line, COLUMN, column
 			       ,LOADER, RT.makeClassLoader()
 			));
 	try
 		{
 		form = macroexpand(form);
-		if(form instanceof IPersistentCollection && Util.equals(RT.first(form), DO))
+		if(form instanceof ISeq && Util.equals(RT.first(form), DO))
 			{
 			for(ISeq s = RT.next(form); s != null; s = RT.next(s))
 				{
@@ -7051,15 +7242,18 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t
 			(rdr instanceof LineNumberingPushbackReader) ? (LineNumberingPushbackReader) rdr :
 			new LineNumberingPushbackReader(rdr);
 	Var.pushThreadBindings(
-			RT.map(SOURCE_PATH, sourcePath,
+			RT.mapUniqueKeys(SOURCE_PATH, sourcePath,
 			       SOURCE, sourceName,
 			       METHOD, null,
 			       LOCAL_ENV, null,
 					LOOP_LOCALS, null,
 					NEXT_LOCAL_NUM, 0,
-			       RT.CURRENT_NS, RT.CURRENT_NS.deref(),
+					RT.READEVAL, RT.T,
+					RT.CURRENT_NS, RT.CURRENT_NS.deref(),
 			       LINE_BEFORE, pushbackReader.getLineNumber(),
+			       COLUMN_BEFORE, pushbackReader.getColumnNumber(),
 			       LINE_AFTER, pushbackReader.getLineNumber(),
+			       COLUMN_AFTER, pushbackReader.getColumnNumber(),
 			       CONSTANTS, PersistentVector.EMPTY,
 			       CONSTANT_IDS, new IdentityHashMap(),
 			       KEYWORDS, PersistentHashMap.EMPTY,
@@ -7094,8 +7288,10 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t
 		    r = LispReader.read(pushbackReader, false, EOF, false))
 			{
 				LINE_AFTER.set(pushbackReader.getLineNumber());
+				COLUMN_AFTER.set(pushbackReader.getColumnNumber());
 				compile1(gen, objx, r);
 				LINE_BEFORE.set(pushbackReader.getLineNumber());
+				COLUMN_BEFORE.set(pushbackReader.getColumnNumber());
 			}
 		//end of load
 		gen.returnValue();
@@ -7187,7 +7383,7 @@ public static Object compile(Reader rdr, String sourcePath, String sourceName) t
 		}
 	catch(LispReader.ReaderException e)
 		{
-		throw new CompilerException(sourcePath, e.line, e.getCause());
+		throw new CompilerException(sourcePath, e.line, e.column, e.getCause());
 		}
 	finally
 		{
@@ -7321,7 +7517,7 @@ static public class NewInstanceExpr extends ObjExpr{
 		try
 			{
 			Var.pushThreadBindings(
-					RT.map(CONSTANTS, PersistentVector.EMPTY,
+					RT.mapUniqueKeys(CONSTANTS, PersistentVector.EMPTY,
 					       CONSTANT_IDS, new IdentityHashMap(),
 					       KEYWORDS, PersistentHashMap.EMPTY,
 					       VARS, PersistentHashMap.EMPTY,
@@ -7331,7 +7527,7 @@ static public class NewInstanceExpr extends ObjExpr{
                                                NO_RECUR, null));
 			if(ret.isDeftype())
 				{
-				Var.pushThreadBindings(RT.map(METHOD, null,
+				Var.pushThreadBindings(RT.mapUniqueKeys(METHOD, null,
 				                              LOCAL_ENV, ret.fields
 						, COMPILE_STUB_SYM, Symbol.intern(null, tagName)
 						, COMPILE_STUB_CLASS, stub));
@@ -7340,7 +7536,8 @@ static public class NewInstanceExpr extends ObjExpr{
 				}
 
 			//now (methodname [args] body)*
-			ret.line = (Integer) LINE.deref();
+			ret.line = lineDeref();
+			ret.column = columnDeref();
 			IPersistentCollection methods = null;
 			for(ISeq s = methodForms; s != null; s = RT.next(s))
 				{
@@ -7700,11 +7897,12 @@ public static class NewInstanceMethod extends ObjMethod{
 		ISeq body = RT.next(RT.next(form));
 		try
 			{
-			method.line = (Integer) LINE.deref();
+			method.line = lineDeref();
+			method.column = columnDeref();
 			//register as the current method and set up a new env frame
             PathNode pnode =  new PathNode(PATHTYPE.PATH, (PathNode) CLEAR_PATH.get());
 			Var.pushThreadBindings(
-					RT.map(
+					RT.mapUniqueKeys(
 							METHOD, method,
 							LOCAL_ENV, LOCAL_ENV.deref(),
 							LOOP_LOCALS, null,
@@ -7879,10 +8077,6 @@ public static class NewInstanceMethod extends ObjMethod{
 				gen.visitLocalVariable(lb.name, argTypes[lb.idx-1].getDescriptor(), null, loopLabel, end, lb.idx);
 				}
 			}
-		catch(Exception e)
-			{
-			throw Util.sneakyThrow(e);
-			}
 		finally
 			{
 			Var.popThreadBindings();
@@ -8003,6 +8197,7 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{
 	public final Set<Integer> skipCheck;
 	public final Class returnType;
 	public final int line;
+	public final int column;
 
 	final static Type NUMBER_TYPE = Type.getType(Number.class);
 	final static Method intValueMethod = Method.getMethod("int intValue()");
@@ -8016,7 +8211,7 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{
     final static Keyword hashEquivKey = Keyword.intern(null, "hash-equiv");
     final static Keyword intKey = Keyword.intern(null, "int");
 	//(case* expr shift mask default map<minhash, [test then]> table-type test-type skip-check?)
-	public CaseExpr(int line, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr,
+	public CaseExpr(int line, int column, LocalBindingExpr expr, int shift, int mask, int low, int high, Expr defaultExpr,
 	        SortedMap<Integer,Expr> tests,HashMap<Integer,Expr> thens, Keyword switchType, Keyword testType, Set<Integer> skipCheck){
 		this.expr = expr;
 		this.shift = shift;
@@ -8027,6 +8222,7 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{
 		this.tests = tests;
 		this.thens = thens;
 		this.line = line;
+		this.column = column;
 		if (switchType != compactKey && switchType != sparseKey)
 		    throw new IllegalArgumentException("Unexpected switch type: "+switchType);
 		this.switchType = switchType;
@@ -8040,8 +8236,8 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{
         if(RT.count(skipCheck) > 0 && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
             {
             RT.errPrintWriter()
-              .format("Performance warning, %s:%d - hash collision of some case test constants; if selected, those entries will be tested sequentially.\n",
-                      SOURCE_PATH.deref(), line);
+              .format("Performance warning, %s:%d:%d - hash collision of some case test constants; if selected, those entries will be tested sequentially.\n",
+                      SOURCE_PATH.deref(), line, column);
             }
 	}
 
@@ -8145,8 +8341,8 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{
             if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
                 {
                 RT.errPrintWriter()
-                  .format("Performance warning, %s:%d - case has int tests, but tested expression is not primitive.\n",
-                          SOURCE_PATH.deref(), line);
+                  .format("Performance warning, %s:%d:%d - case has int tests, but tested expression is not primitive.\n",
+                          SOURCE_PATH.deref(), line, column);
                 }
             expr.emit(C.EXPRESSION, objx, gen);
             gen.instanceOf(NUMBER_TYPE);
@@ -8244,7 +8440,7 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{
 		public Expr parse(C context, Object frm) {
 			ISeq form = (ISeq) frm;
 			if(context == C.EVAL)
-				return analyze(context, RT.list(RT.list(FN, PersistentVector.EMPTY, form)));
+				return analyze(context, RT.list(RT.list(FNONCE, PersistentVector.EMPTY, form)));
 			PersistentVector args = PersistentVector.create(form.next());
 
 			Object exprForm = args.nth(0);
@@ -8301,7 +8497,8 @@ public static class CaseExpr implements Expr, MaybePrimitiveExpr{
                 }
 
             int line = ((Number)LINE.deref()).intValue();
-			return new CaseExpr(line, testexpr, shift, mask, low, high,
+            int column = ((Number)COLUMN.deref()).intValue();
+			return new CaseExpr(line, column, testexpr, shift, mask, low, high,
 			        defaultExpr, tests, thens, switchType, testType, skipCheck);
 		}
 	}
diff --git a/src/jvm/clojure/lang/Delay.java b/src/jvm/clojure/lang/Delay.java
index c8227fe..ef8110b 100644
--- a/src/jvm/clojure/lang/Delay.java
+++ b/src/jvm/clojure/lang/Delay.java
@@ -14,11 +14,13 @@ package clojure.lang;
 
 public class Delay implements IDeref, IPending{
 Object val;
+Throwable exception;
 IFn fn;
 
 public Delay(IFn fn){
 	this.fn = fn;
 	this.val = null;
+        this.exception = null;
 }
 
 static public Object force(Object x) {
@@ -30,9 +32,18 @@ static public Object force(Object x) {
 synchronized public Object deref() {
 	if(fn != null)
 		{
-		val = fn.invoke();
+		try
+			{
+			val = fn.invoke();
+			}
+		catch(Throwable t)
+			{
+			exception = t;
+			}
 		fn = null;
 		}
+	if(exception != null)
+		throw Util.sneakyThrow(exception);
 	return val;
 }
 
diff --git a/src/jvm/clojure/lang/EdnReader.java b/src/jvm/clojure/lang/EdnReader.java
new file mode 100644
index 0000000..08cfe20
--- /dev/null
+++ b/src/jvm/clojure/lang/EdnReader.java
@@ -0,0 +1,737 @@
+/**
+ *   Copyright (c) Rich Hickey. All rights reserved.
+ *   The use and distribution terms for this software are covered by the
+ *   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ *   which can be found in the file epl-v10.html at the root of this distribution.
+ *   By using this software in any fashion, you are agreeing to be bound by
+ * 	 the terms of this license.
+ *   You must not remove this notice, or any other, from this software.
+ **/
+
+package clojure.lang;
+
+import java.io.IOException;
+import java.io.PushbackReader;
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class EdnReader{
+
+static IFn[] macros = new IFn[256];
+static IFn[] dispatchMacros = new IFn[256];
+static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?(/|[\\D&&[^/]][^/]*)");
+static Pattern intPat =
+		Pattern.compile(
+				"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
+static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
+static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
+
+static IFn taggedReader = new TaggedReader();
+
+static
+	{
+	macros['"'] = new StringReader();
+	macros[';'] = new CommentReader();
+	macros['^'] = new MetaReader();
+	macros['('] = new ListReader();
+	macros[')'] = new UnmatchedDelimiterReader();
+	macros['['] = new VectorReader();
+	macros[']'] = new UnmatchedDelimiterReader();
+	macros['{'] = new MapReader();
+	macros['}'] = new UnmatchedDelimiterReader();
+	macros['\\'] = new CharacterReader();
+	macros['#'] = new DispatchReader();
+
+
+	dispatchMacros['^'] = new MetaReader();
+	//dispatchMacros['"'] = new RegexReader();
+	dispatchMacros['{'] = new SetReader();
+	dispatchMacros['<'] = new UnreadableReader();
+	dispatchMacros['_'] = new DiscardReader();
+	}
+
+static boolean nonConstituent(int ch){
+	return ch == '@' || ch == '`' || ch == '~';
+}
+
+static public Object readString(String s, IPersistentMap opts){
+	PushbackReader r = new PushbackReader(new java.io.StringReader(s));
+	return read(r, opts);
+}
+
+static boolean isWhitespace(int ch){
+	return Character.isWhitespace(ch) || ch == ',';
+}
+
+static void unread(PushbackReader r, int ch) {
+	if(ch != -1)
+		try
+			{
+			r.unread(ch);
+			}
+		catch(IOException e)
+			{
+			throw Util.sneakyThrow(e);
+			}
+}
+
+public static class ReaderException extends RuntimeException{
+	final int line;
+	final int column;
+
+	public ReaderException(int line, int column, Throwable cause){
+		super(cause);
+		this.line = line;
+		this.column = column;
+	}
+}
+
+static public int read1(Reader r){
+	try
+		{
+		return r.read();
+		}
+	catch(IOException e)
+		{
+		throw Util.sneakyThrow(e);
+		}
+}
+
+static final Keyword EOF = Keyword.intern(null,"eof");
+
+static public Object read(PushbackReader r, IPersistentMap opts){
+	return read(r,!opts.containsKey(EOF),opts.valAt(EOF),false,opts);
+}
+
+static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive,
+                          Object opts)
+{
+
+	try
+		{
+		for(; ;)
+			{
+			int ch = read1(r);
+
+			while(isWhitespace(ch))
+				ch = read1(r);
+
+			if(ch == -1)
+				{
+				if(eofIsError)
+					throw Util.runtimeException("EOF while reading");
+				return eofValue;
+				}
+
+			if(Character.isDigit(ch))
+				{
+				Object n = readNumber(r, (char) ch);
+				if(RT.suppressRead())
+					return null;
+				return n;
+				}
+
+			IFn macroFn = getMacro(ch);
+			if(macroFn != null)
+				{
+				Object ret = macroFn.invoke(r, (char) ch, opts);
+				if(RT.suppressRead())
+					return null;
+				//no op macros return the reader
+				if(ret == r)
+					continue;
+				return ret;
+				}
+
+			if(ch == '+' || ch == '-')
+				{
+				int ch2 = read1(r);
+				if(Character.isDigit(ch2))
+					{
+					unread(r, ch2);
+					Object n = readNumber(r, (char) ch);
+					if(RT.suppressRead())
+						return null;
+					return n;
+					}
+				unread(r, ch2);
+				}
+
+			String token = readToken(r, (char) ch, true);
+			if(RT.suppressRead())
+				return null;
+			return interpretToken(token);
+			}
+		}
+	catch(Exception e)
+		{
+		if(isRecursive || !(r instanceof LineNumberingPushbackReader))
+			throw Util.sneakyThrow(e);
+		LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
+		//throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
+		throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
+		}
+}
+
+static private String readToken(PushbackReader r, char initch, boolean leadConstituent) {
+	StringBuilder sb = new StringBuilder();
+	if(leadConstituent && nonConstituent(initch))
+		throw Util.runtimeException("Invalid leading character: " + (char)initch);
+
+	sb.append(initch);
+
+	for(; ;)
+		{
+		int ch = read1(r);
+
+		if(ch == -1 || isWhitespace(ch) || isTerminatingMacro(ch))
+			{
+			unread(r, ch);
+			return sb.toString();
+			}
+		else if(nonConstituent(ch))
+			throw Util.runtimeException("Invalid constituent character: " + (char)ch);
+		sb.append((char) ch);
+		}
+}
+
+static private Object readNumber(PushbackReader r, char initch) {
+	StringBuilder sb = new StringBuilder();
+	sb.append(initch);
+
+	for(; ;)
+		{
+		int ch = read1(r);
+		if(ch == -1 || isWhitespace(ch) || isMacro(ch))
+			{
+			unread(r, ch);
+			break;
+			}
+		sb.append((char) ch);
+		}
+
+	String s = sb.toString();
+	Object n = matchNumber(s);
+	if(n == null)
+		throw new NumberFormatException("Invalid number: " + s);
+	return n;
+}
+
+static private int readUnicodeChar(String token, int offset, int length, int base) {
+	if(token.length() != offset + length)
+		throw new IllegalArgumentException("Invalid unicode character: \\" + token);
+	int uc = 0;
+	for(int i = offset; i < offset + length; ++i)
+		{
+		int d = Character.digit(token.charAt(i), base);
+		if(d == -1)
+			throw new IllegalArgumentException("Invalid digit: " + token.charAt(i));
+		uc = uc * base + d;
+		}
+	return (char) uc;
+}
+
+static private int readUnicodeChar(PushbackReader r, int initch, int base, int length, boolean exact) {
+	int uc = Character.digit(initch, base);
+	if(uc == -1)
+		throw new IllegalArgumentException("Invalid digit: " + (char) initch);
+	int i = 1;
+	for(; i < length; ++i)
+		{
+		int ch = read1(r);
+		if(ch == -1 || isWhitespace(ch) || isMacro(ch))
+			{
+			unread(r, ch);
+			break;
+			}
+		int d = Character.digit(ch, base);
+		if(d == -1)
+			throw new IllegalArgumentException("Invalid digit: " + (char) ch);
+		uc = uc * base + d;
+		}
+	if(i != length && exact)
+		throw new IllegalArgumentException("Invalid character length: " + i + ", should be: " + length);
+	return uc;
+}
+
+static private Object interpretToken(String s) {
+	if(s.equals("nil"))
+		{
+		return null;
+		}
+	else if(s.equals("true"))
+		{
+		return RT.T;
+		}
+	else if(s.equals("false"))
+		{
+		return RT.F;
+		}
+
+	Object ret = null;
+
+	ret = matchSymbol(s);
+	if(ret != null)
+		return ret;
+
+	throw Util.runtimeException("Invalid token: " + s);
+}
+
+
+private static Object matchSymbol(String s){
+	Matcher m = symbolPat.matcher(s);
+	if(m.matches())
+		{
+		int gc = m.groupCount();
+		String ns = m.group(1);
+		String name = m.group(2);
+		if(ns != null && ns.endsWith(":/")
+		   || name.endsWith(":")
+		   || s.indexOf("::", 1) != -1)
+			return null;
+		if(s.startsWith("::"))
+			{
+			return null;
+			}
+		boolean isKeyword = s.charAt(0) == ':';
+		Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
+		if(isKeyword)
+			return Keyword.intern(sym);
+		return sym;
+		}
+	return null;
+}
+
+
+private static Object matchNumber(String s){
+	Matcher m = intPat.matcher(s);
+	if(m.matches())
+		{
+		if(m.group(2) != null)
+			{
+			if(m.group(8) != null)
+				return BigInt.ZERO;
+			return Numbers.num(0);
+			}
+		boolean negate = (m.group(1).equals("-"));
+		String n;
+		int radix = 10;
+		if((n = m.group(3)) != null)
+			radix = 10;
+		else if((n = m.group(4)) != null)
+			radix = 16;
+		else if((n = m.group(5)) != null)
+			radix = 8;
+		else if((n = m.group(7)) != null)
+			radix = Integer.parseInt(m.group(6));
+		if(n == null)
+			return null;
+		BigInteger bn = new BigInteger(n, radix);
+		if(negate)
+			bn = bn.negate();
+		if(m.group(8) != null)
+			return BigInt.fromBigInteger(bn);
+		return bn.bitLength() < 64 ?
+		       Numbers.num(bn.longValue())
+		                           : BigInt.fromBigInteger(bn);
+		}
+	m = floatPat.matcher(s);
+	if(m.matches())
+		{
+		if(m.group(4) != null)
+			return new BigDecimal(m.group(1));
+		return Double.parseDouble(s);
+		}
+	m = ratioPat.matcher(s);
+	if(m.matches())
+		{
+		String numerator = m.group(1);
+		if (numerator.startsWith("+")) numerator = numerator.substring(1);
+
+		return Numbers.divide(Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(numerator))),
+		                      Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(m.group(2)))));
+		}
+	return null;
+}
+
+static private IFn getMacro(int ch){
+	if(ch < macros.length)
+		return macros[ch];
+	return null;
+}
+
+static private boolean isMacro(int ch){
+	return (ch < macros.length && macros[ch] != null);
+}
+
+static private boolean isTerminatingMacro(int ch){
+	return (ch != '#' && ch != '\'' && isMacro(ch));
+}
+
+/*
+public static class RegexReader extends AFn{
+
+	static StringReader stringrdr = new StringReader();
+
+	public Object invoke(Object reader, Object doublequote) {
+		StringBuilder sb = new StringBuilder();
+		Reader r = (Reader) reader;
+		for(int ch = read1(r); ch != '"'; ch = read1(r))
+			{
+			if(ch == -1)
+				throw Util.runtimeException("EOF while reading regex");
+			sb.append( (char) ch );
+			if(ch == '\\')	//escape
+				{
+				ch = read1(r);
+				if(ch == -1)
+					throw Util.runtimeException("EOF while reading regex");
+				sb.append( (char) ch ) ;
+				}
+			}
+		return Pattern.compile(sb.toString());
+	}
+}
+*/
+
+public static class StringReader extends AFn{
+	public Object invoke(Object reader, Object doublequote, Object opts) {
+		StringBuilder sb = new StringBuilder();
+		Reader r = (Reader) reader;
+
+		for(int ch = read1(r); ch != '"'; ch = read1(r))
+			{
+			if(ch == -1)
+				throw Util.runtimeException("EOF while reading string");
+			if(ch == '\\')	//escape
+				{
+				ch = read1(r);
+				if(ch == -1)
+					throw Util.runtimeException("EOF while reading string");
+				switch(ch)
+					{
+					case 't':
+						ch = '\t';
+						break;
+					case 'r':
+						ch = '\r';
+						break;
+					case 'n':
+						ch = '\n';
+						break;
+					case '\\':
+						break;
+					case '"':
+						break;
+					case 'b':
+						ch = '\b';
+						break;
+					case 'f':
+						ch = '\f';
+						break;
+					case 'u':
+					{
+					ch = read1(r);
+					if (Character.digit(ch, 16) == -1)
+						throw Util.runtimeException("Invalid unicode escape: \\u" + (char) ch);
+					ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true);
+					break;
+					}
+					default:
+					{
+					if(Character.isDigit(ch))
+						{
+						ch = readUnicodeChar((PushbackReader) r, ch, 8, 3, false);
+						if(ch > 0377)
+							throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
+						}
+					else
+						throw Util.runtimeException("Unsupported escape character: \\" + (char) ch);
+					}
+					}
+				}
+			sb.append((char) ch);
+			}
+		return sb.toString();
+	}
+}
+
+public static class CommentReader extends AFn{
+	public Object invoke(Object reader, Object semicolon, Object opts) {
+		Reader r = (Reader) reader;
+		int ch;
+		do
+			{
+			ch = read1(r);
+			} while(ch != -1 && ch != '\n' && ch != '\r');
+		return r;
+	}
+
+}
+
+public static class DiscardReader extends AFn{
+	public Object invoke(Object reader, Object underscore, Object opts) {
+		PushbackReader r = (PushbackReader) reader;
+		read(r, true, null, true, opts);
+		return r;
+	}
+}
+
+public static class DispatchReader extends AFn{
+	public Object invoke(Object reader, Object hash, Object opts) {
+		int ch = read1((Reader) reader);
+		if(ch == -1)
+			throw Util.runtimeException("EOF while reading character");
+		IFn fn = dispatchMacros[ch];
+
+		if(fn == null) {
+			//try tagged reader
+		    if(Character.isLetter(ch))
+				{
+				unread((PushbackReader) reader, ch);
+		        return taggedReader.invoke(reader, ch, opts);
+				}
+
+			throw Util.runtimeException(String.format("No dispatch macro for: %c", (char) ch));
+		}
+		return fn.invoke(reader, ch, opts);
+	}
+}
+
+public static class MetaReader extends AFn{
+	public Object invoke(Object reader, Object caret, Object opts) {
+		PushbackReader r = (PushbackReader) reader;
+		int line = -1;
+		int column = -1;
+		if(r instanceof LineNumberingPushbackReader)
+			{
+			line = ((LineNumberingPushbackReader) r).getLineNumber();
+			column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
+			}
+		Object meta = read(r, true, null, true, opts);
+		if(meta instanceof Symbol || meta instanceof String)
+			meta = RT.map(RT.TAG_KEY, meta);
+		else if (meta instanceof Keyword)
+			meta = RT.map(meta, RT.T);
+		else if(!(meta instanceof IPersistentMap))
+			throw new IllegalArgumentException("Metadata must be Symbol,Keyword,String or Map");
+
+		Object o = read(r, true, null, true, opts);
+		if(o instanceof IMeta)
+			{
+			if(line != -1 && o instanceof ISeq)
+				{
+				meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line).assoc(RT.COLUMN_KEY, column);
+				}
+			if(o instanceof IReference)
+				{
+				((IReference)o).resetMeta((IPersistentMap) meta);
+				return o;
+				}
+			Object ometa = RT.meta(o);
+			for(ISeq s = RT.seq(meta); s != null; s = s.next()) {
+			IMapEntry kv = (IMapEntry) s.first();
+			ometa = RT.assoc(ometa, kv.getKey(), kv.getValue());
+			}
+			return ((IObj) o).withMeta((IPersistentMap) ometa);
+			}
+		else
+			throw new IllegalArgumentException("Metadata can only be applied to IMetas");
+	}
+
+}
+
+public static class CharacterReader extends AFn{
+	public Object invoke(Object reader, Object backslash, Object opts) {
+		PushbackReader r = (PushbackReader) reader;
+		int ch = read1(r);
+		if(ch == -1)
+			throw Util.runtimeException("EOF while reading character");
+		String token = readToken(r, (char) ch, false);
+		if(token.length() == 1)
+			return Character.valueOf(token.charAt(0));
+		else if(token.equals("newline"))
+			return '\n';
+		else if(token.equals("space"))
+			return ' ';
+		else if(token.equals("tab"))
+			return '\t';
+		else if(token.equals("backspace"))
+			return '\b';
+		else if(token.equals("formfeed"))
+			return '\f';
+		else if(token.equals("return"))
+			return '\r';
+		else if(token.startsWith("u"))
+			{
+			char c = (char) readUnicodeChar(token, 1, 4, 16);
+			if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
+				throw Util.runtimeException("Invalid character constant: \\u" + Integer.toString(c, 16));
+			return c;
+			}
+		else if(token.startsWith("o"))
+			{
+			int len = token.length() - 1;
+			if(len > 3)
+				throw Util.runtimeException("Invalid octal escape sequence length: " + len);
+			int uc = readUnicodeChar(token, 1, len, 8);
+			if(uc > 0377)
+				throw Util.runtimeException("Octal escape sequence must be in range [0, 377].");
+			return (char) uc;
+			}
+		throw Util.runtimeException("Unsupported character: \\" + token);
+	}
+
+}
+
+public static class ListReader extends AFn{
+	public Object invoke(Object reader, Object leftparen, Object opts) {
+		PushbackReader r = (PushbackReader) reader;
+		int line = -1;
+		int column = -1;
+		if(r instanceof LineNumberingPushbackReader)
+			{
+			line = ((LineNumberingPushbackReader) r).getLineNumber();
+			column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
+			}
+		List list = readDelimitedList(')', r, true, opts);
+		if(list.isEmpty())
+			return PersistentList.EMPTY;
+		IObj s = (IObj) PersistentList.create(list);
+//		IObj s = (IObj) RT.seq(list);
+//		if(line != -1)
+//			{
+//			return s.withMeta(RT.map(RT.LINE_KEY, line, RT.COLUMN_KEY, column));
+//			}
+//		else
+			return s;
+	}
+
+}
+
+public static class VectorReader extends AFn{
+	public Object invoke(Object reader, Object leftparen, Object opts) {
+		PushbackReader r = (PushbackReader) reader;
+		return LazilyPersistentVector.create(readDelimitedList(']', r, true, opts));
+	}
+
+}
+
+public static class MapReader extends AFn{
+	public Object invoke(Object reader, Object leftparen, Object opts) {
+		PushbackReader r = (PushbackReader) reader;
+		Object[] a = readDelimitedList('}', r, true, opts).toArray();
+		if((a.length & 1) == 1)
+			throw Util.runtimeException("Map literal must contain an even number of forms");
+		return RT.map(a);
+	}
+
+}
+
+public static class SetReader extends AFn{
+	public Object invoke(Object reader, Object leftbracket, Object opts) {
+		PushbackReader r = (PushbackReader) reader;
+		return PersistentHashSet.createWithCheck(readDelimitedList('}', r, true, opts));
+	}
+
+}
+
+public static class UnmatchedDelimiterReader extends AFn{
+	public Object invoke(Object reader, Object rightdelim, Object opts) {
+		throw Util.runtimeException("Unmatched delimiter: " + rightdelim);
+	}
+
+}
+
+public static class UnreadableReader extends AFn{
+	public Object invoke(Object reader, Object leftangle, Object opts) {
+		throw Util.runtimeException("Unreadable form");
+	}
+}
+
+public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive, Object opts) {
+	final int firstline =
+			(r instanceof LineNumberingPushbackReader) ?
+			((LineNumberingPushbackReader) r).getLineNumber() : -1;
+
+	ArrayList a = new ArrayList();
+
+	for(; ;)
+		{
+		int ch = read1(r);
+
+		while(isWhitespace(ch))
+			ch = read1(r);
+
+		if(ch == -1)
+			{
+			if(firstline < 0)
+				throw Util.runtimeException("EOF while reading");
+			else
+				throw Util.runtimeException("EOF while reading, starting at line " + firstline);
+			}
+
+		if(ch == delim)
+			break;
+
+		IFn macroFn = getMacro(ch);
+		if(macroFn != null)
+			{
+			Object mret = macroFn.invoke(r, (char) ch, opts);
+			//no op macros return the reader
+			if(mret != r)
+				a.add(mret);
+			}
+		else
+			{
+			unread(r, ch);
+
+			Object o = read(r, true, null, isRecursive, opts);
+			if(o != r)
+				a.add(o);
+			}
+		}
+
+
+	return a;
+}
+
+public static class TaggedReader extends AFn{
+	public Object invoke(Object reader, Object firstChar, Object opts){
+		PushbackReader r = (PushbackReader) reader;
+		Object name = read(r, true, null, false, opts);
+		if (!(name instanceof Symbol))
+			throw new RuntimeException("Reader tag must be a symbol");
+		Symbol sym = (Symbol)name;
+		return readTagged(r, sym, (IPersistentMap) opts);
+	}
+
+	static Keyword READERS = Keyword.intern(null,"readers");
+	static Keyword DEFAULT = Keyword.intern(null,"default");
+
+	private Object readTagged(PushbackReader reader, Symbol tag, IPersistentMap opts){
+		Object o = read(reader, true, null, true, opts);
+
+		ILookup readers = (ILookup)RT.get(opts, READERS);
+		IFn dataReader = (IFn)RT.get(readers, tag);
+		if(dataReader == null)
+			dataReader = (IFn)RT.get(RT.DEFAULT_DATA_READERS.deref(),tag);
+		if(dataReader == null){
+			IFn defaultReader = (IFn)RT.get(opts, DEFAULT);
+			if(defaultReader != null)
+				return defaultReader.invoke(tag, o);
+			else
+				throw new RuntimeException("No reader function for tag " + tag.toString());
+		}
+		else
+			return dataReader.invoke(o);
+	}
+
+}
+}
+
diff --git a/src/jvm/clojure/lang/ExceptionInfo.java b/src/jvm/clojure/lang/ExceptionInfo.java
index 1ebc83c..7ccaa0b 100644
--- a/src/jvm/clojure/lang/ExceptionInfo.java
+++ b/src/jvm/clojure/lang/ExceptionInfo.java
@@ -15,7 +15,7 @@ package clojure.lang;
  * richer semantics for exceptions should use this in lieu of defining project-specific
  * exception classes.
  */
-public class ExceptionInfo extends RuntimeException{
+public class ExceptionInfo extends RuntimeException implements IExceptionInfo {
     public final IPersistentMap data;
 
     public ExceptionInfo(String s, IPersistentMap data) {
diff --git a/src/jvm/clojure/lang/IExceptionInfo.java b/src/jvm/clojure/lang/IExceptionInfo.java
new file mode 100644
index 0000000..be7dc80
--- /dev/null
+++ b/src/jvm/clojure/lang/IExceptionInfo.java
@@ -0,0 +1,20 @@
+/**
+ * Copyright (c) Rich Hickey. All rights reserved.
+ * The use and distribution terms for this software are covered by the
+ * Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+ * which can be found in the file epl-v10.html at the root of this distribution.
+ * By using this software in any fashion, you are agreeing to be bound by
+ * the terms of this license.
+ * You must not remove this notice, or any other, from this software.
+ */
+
+package clojure.lang;
+
+/**
+ * Interface for exceptions that carry data (a map) as additional payload. Clojure 
+ * programs that need richer semantics for exceptions should use this in lieu of 
+ * defining project-specific exception classes.
+ */
+public interface IExceptionInfo {
+    public IPersistentMap getData();
+}
diff --git a/src/jvm/clojure/lang/IFn.java b/src/jvm/clojure/lang/IFn.java
index 2cea542..14e3175 100644
--- a/src/jvm/clojure/lang/IFn.java
+++ b/src/jvm/clojure/lang/IFn.java
@@ -14,6 +14,12 @@ package clojure.lang;
 
 import java.util.concurrent.Callable;
 
+/**
+ * <p><code>IFn</code> provides complete access to invoking
+ * any of Clojure's <a href="http://clojure.github.io/clojure/">API</a>s.
+ * You can also access any other library written in Clojure, after adding
+ * either its source or compiled form to the classpath.</p>
+ */
 public interface IFn extends Callable, Runnable{
 
 public Object invoke() ;
diff --git a/src/jvm/clojure/lang/Intrinsics.java b/src/jvm/clojure/lang/Intrinsics.java
index 88d3b2d..e71b3bd 100644
--- a/src/jvm/clojure/lang/Intrinsics.java
+++ b/src/jvm/clojure/lang/Intrinsics.java
@@ -29,6 +29,7 @@ static IPersistentMap ops = RT.map(
  "public static long clojure.lang.Numbers.remainder(long,long)", LREM,
  "public static long clojure.lang.Numbers.shiftLeft(long,long)", oa(L2I, LSHL),
  "public static long clojure.lang.Numbers.shiftRight(long,long)", oa(L2I, LSHR),
+ "public static long clojure.lang.Numbers.unsignedShiftRight(long,long)", oa(L2I, LUSHR),
  "public static double clojure.lang.Numbers.minus(double)", DNEG,
  "public static double clojure.lang.Numbers.minus(double,double)", DSUB,
  "public static double clojure.lang.Numbers.inc(double)", oa(DCONST_1, DADD),
@@ -36,6 +37,7 @@ static IPersistentMap ops = RT.map(
  "public static long clojure.lang.Numbers.quotient(long,long)", LDIV,
  "public static int clojure.lang.Numbers.shiftLeftInt(int,int)", ISHL,
  "public static int clojure.lang.Numbers.shiftRightInt(int,int)", ISHR,
+ "public static int clojure.lang.Numbers.unsignedShiftRightInt(int,int)", IUSHR,
  "public static int clojure.lang.Numbers.unchecked_int_add(int,int)", IADD,
  "public static int clojure.lang.Numbers.unchecked_int_subtract(int,int)", ISUB,
  "public static int clojure.lang.Numbers.unchecked_int_negate(int)", INEG,
diff --git a/src/jvm/clojure/lang/Keyword.java b/src/jvm/clojure/lang/Keyword.java
index 15693c8..7b71d44 100644
--- a/src/jvm/clojure/lang/Keyword.java
+++ b/src/jvm/clojure/lang/Keyword.java
@@ -21,12 +21,12 @@ import java.lang.ref.ReferenceQueue;
 import java.lang.ref.SoftReference;
 
 
-public class Keyword implements IFn, Comparable, Named, Serializable {
+public class Keyword implements IFn, Comparable, Named, Serializable, IHashEq {
 
 private static ConcurrentHashMap<Symbol, Reference<Keyword>> table = new ConcurrentHashMap();
 static final ReferenceQueue rq = new ReferenceQueue();
 public final Symbol sym;
-final int hash;
+final int hasheq;
 String _str;
 
 public static Keyword intern(Symbol sym){
@@ -55,7 +55,7 @@ public static Keyword intern(String nsname){
 
 private Keyword(Symbol sym){
 	this.sym = sym;
-	hash = sym.hashCode() + 0x9e3779b9;
+	hasheq = sym.hasheq() + 0x9e3779b9;
 }
 
 public static Keyword find(Symbol sym){
@@ -75,7 +75,11 @@ public static Keyword find(String nsname){
 }
 
 public final int hashCode(){
-	return hash;
+	return sym.hashCode() + 0x9e3779b9;
+}
+
+public int hasheq() {
+	return hasheq;
 }
 
 public String toString(){
diff --git a/src/jvm/clojure/lang/LazySeq.java b/src/jvm/clojure/lang/LazySeq.java
index 914cc52..5554676 100644
--- a/src/jvm/clojure/lang/LazySeq.java
+++ b/src/jvm/clojure/lang/LazySeq.java
@@ -37,19 +37,8 @@ public Obj withMeta(IPersistentMap meta){
 final synchronized Object sval(){
 	if(fn != null)
 		{
-		try
-			{
-			sv = fn.invoke();
-			fn = null;
-			}
-		catch(RuntimeException e)
-			{
-			throw e;
-			}
-		catch(Exception e)
-			{
-			throw Util.sneakyThrow(e);
-			}
+                sv = fn.invoke();
+                fn = null;
 		}
 	if(sv != null)
 		return sv;
@@ -119,10 +108,7 @@ public int hashCode(){
 }
 
 public int hasheq(){
-	ISeq s = seq();
-	if(s == null)
-		return 1;
-	return Util.hasheq(seq());
+	return Murmur3.hashOrdered(this);
 }
 
 public boolean equals(Object o){
diff --git a/src/jvm/clojure/lang/LineNumberingPushbackReader.java b/src/jvm/clojure/lang/LineNumberingPushbackReader.java
index ad81ce9..599f49e 100644
--- a/src/jvm/clojure/lang/LineNumberingPushbackReader.java
+++ b/src/jvm/clojure/lang/LineNumberingPushbackReader.java
@@ -27,25 +27,44 @@ private static final int newline = (int) '\n';
 
 private boolean _atLineStart = true;
 private boolean _prev;
+private int _columnNumber = 1;
 
 public LineNumberingPushbackReader(Reader r){
 	super(new LineNumberReader(r));
 }
 
+public LineNumberingPushbackReader(Reader r, int size){
+	super(new LineNumberReader(r, size));
+}
+
 public int getLineNumber(){
 	return ((LineNumberReader) in).getLineNumber() + 1;
 }
 
+public int getColumnNumber(){
+	return _columnNumber;
+}
+
 public int read() throws IOException{
     int c = super.read();
     _prev = _atLineStart;
-    _atLineStart = (c == newline) || (c == -1);
+    if((c == newline) || (c == -1))
+        {
+        _atLineStart = true;
+        _columnNumber = 1;
+        }
+    else
+        {
+        _atLineStart = false;
+        _columnNumber++;
+        }
     return c;
 }
 
 public void unread(int c) throws IOException{
     super.unread(c);
     _atLineStart = _prev;
+    _columnNumber--;
 }
 
 public String readLine() throws IOException{
@@ -64,6 +83,7 @@ public String readLine() throws IOException{
         line = (rest == null) ? first : first + rest;
         _prev = false;
         _atLineStart = true;
+        _columnNumber = 1;
         break;
     }
     return line;
diff --git a/src/jvm/clojure/lang/LispReader.java b/src/jvm/clojure/lang/LispReader.java
index 8f67b3b..b926a12 100644
--- a/src/jvm/clojure/lang/LispReader.java
+++ b/src/jvm/clojure/lang/LispReader.java
@@ -53,12 +53,13 @@ static Symbol VECTOR = Symbol.intern("clojure.core", "vector");
 static Symbol WITH_META = Symbol.intern("clojure.core", "with-meta");
 static Symbol META = Symbol.intern("clojure.core", "meta");
 static Symbol DEREF = Symbol.intern("clojure.core", "deref");
+static Keyword UNKNOWN = Keyword.intern(null, "unknown");
 //static Symbol DEREF_BANG = Symbol.intern("clojure.core", "deref!");
 
 static IFn[] macros = new IFn[256];
 static IFn[] dispatchMacros = new IFn[256];
 //static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^:/]][^:/]*/)?[\\D&&[^:/]][^:/]*");
-static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?([\\D&&[^/]][^/]*)");
+static Pattern symbolPat = Pattern.compile("[:]?([\\D&&[^/]].*/)?(/|[\\D&&[^/]][^/]*)");
 //static Pattern varPat = Pattern.compile("([\\D&&[^:\\.]][^:\\.]*):([\\D&&[^:\\.]][^:\\.]*)");
 //static Pattern intPat = Pattern.compile("[-+]?[0-9]+\\.?");
 static Pattern intPat =
@@ -66,8 +67,6 @@ static Pattern intPat =
 				"([-+]?)(?:(0)|([1-9][0-9]*)|0[xX]([0-9A-Fa-f]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9A-Za-z]+)|0[0-9]+)(N)?");
 static Pattern ratioPat = Pattern.compile("([-+]?[0-9]+)/([0-9]+)");
 static Pattern floatPat = Pattern.compile("([-+]?[0-9]+(\\.[0-9]*)?([eE][-+]?[0-9]+)?)(M)?");
-static final Symbol SLASH = Symbol.intern("/");
-static final Symbol CLOJURE_SLASH = Symbol.intern("clojure.core","/");
 //static Pattern accessorPat = Pattern.compile("\\.[a-zA-Z_]\\w*");
 //static Pattern instanceMemberPat = Pattern.compile("\\.([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
 //static Pattern staticMemberPat = Pattern.compile("([a-zA-Z_][\\w\\.]*)\\.([a-zA-Z_]\\w*)");
@@ -79,7 +78,7 @@ static Var GENSYM_ENV = Var.create(null).setDynamic();
 static Var ARG_ENV = Var.create(null).setDynamic();
 static IFn ctorReader = new CtorReader();
 
-    static
+static
 	{
 	macros['"'] = new StringReader();
 	macros[';'] = new CommentReader();
@@ -129,10 +128,12 @@ static void unread(PushbackReader r, int ch) {
 
 public static class ReaderException extends RuntimeException{
 	final int line;
+	final int column;
 
-	public ReaderException(int line, Throwable cause){
+	public ReaderException(int line, int column, Throwable cause){
 		super(cause);
 		this.line = line;
+		this.column = column;
 	}
 }
 
@@ -148,7 +149,9 @@ static public int read1(Reader r){
 }
 
 static public Object read(PushbackReader r, boolean eofIsError, Object eofValue, boolean isRecursive)
-		{
+{
+	if(RT.READEVAL.deref() == UNKNOWN)
+		throw Util.runtimeException("Reading disallowed - *read-eval* bound to :unknown");
 
 	try
 		{
@@ -212,7 +215,7 @@ static public Object read(PushbackReader r, boolean eofIsError, Object eofValue,
 			throw Util.sneakyThrow(e);
 		LineNumberingPushbackReader rdr = (LineNumberingPushbackReader) r;
 		//throw Util.runtimeException(String.format("ReaderError:(%d,1) %s", rdr.getLineNumber(), e.getMessage()), e);
-		throw new ReaderException(rdr.getLineNumber(), e);
+		throw new ReaderException(rdr.getLineNumber(), rdr.getColumnNumber(), e);
 		}
 }
 
@@ -304,14 +307,6 @@ static private Object interpretToken(String s) {
 		{
 		return RT.F;
 		}
-	else if(s.equals("/"))
-		{
-		return SLASH;
-		}
-	else if(s.equals("clojure.core//"))
-		{
-		return CLOJURE_SLASH;
-		}
 	Object ret = null;
 
 	ret = matchSymbol(s);
@@ -342,10 +337,10 @@ private static Object matchSymbol(String s){
 			else
 				kns = Compiler.currentNS();
 			//auto-resolving keyword
-            if (kns != null)
-			    return Keyword.intern(kns.name.name,ks.name);
-            else
-                return null;    
+			if (kns != null)
+				return Keyword.intern(kns.name.name,ks.name);
+			else
+				return null;
 			}
 		boolean isKeyword = s.charAt(0) == ':';
 		Symbol sym = Symbol.intern(s.substring(isKeyword ? 1 : 0));
@@ -386,8 +381,8 @@ private static Object matchNumber(String s){
 		if(m.group(8) != null)
 			return BigInt.fromBigInteger(bn);
 		return bn.bitLength() < 64 ?
-		        Numbers.num(bn.longValue())
-				: BigInt.fromBigInteger(bn);
+		       Numbers.num(bn.longValue())
+		                           : BigInt.fromBigInteger(bn);
 		}
 	m = floatPat.matcher(s);
 	if(m.matches())
@@ -399,7 +394,10 @@ private static Object matchNumber(String s){
 	m = ratioPat.matcher(s);
 	if(m.matches())
 		{
-		return Numbers.divide(Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(m.group(1)))),
+		String numerator = m.group(1);
+		if (numerator.startsWith("+")) numerator = numerator.substring(1);
+
+		return Numbers.divide(Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(numerator))),
 		                      Numbers.reduceBigInt(BigInt.fromBigInteger(new BigInteger(m.group(2)))));
 		}
 	return null;
@@ -416,7 +414,7 @@ static private boolean isMacro(int ch){
 }
 
 static private boolean isTerminatingMacro(int ch){
-	return (ch != '#' && ch != '\'' && isMacro(ch));
+	return (ch != '#' && ch != '\'' && ch != '%' && isMacro(ch));
 }
 
 public static class RegexReader extends AFn{
@@ -481,7 +479,7 @@ public static class StringReader extends AFn{
 					{
 					ch = read1(r);
 					if (Character.digit(ch, 16) == -1)
-					    throw Util.runtimeException("Invalid unicode escape: \\u" + (char) ch);
+						throw Util.runtimeException("Invalid unicode escape: \\u" + (char) ch);
 					ch = readUnicodeChar((PushbackReader) r, ch, 16, 4, true);
 					break;
 					}
@@ -542,17 +540,17 @@ public static class WrappingReader extends AFn{
 
 public static class DeprecatedWrappingReader extends AFn{
 	final Symbol sym;
-        final String macro;
+	final String macro;
 
 	public DeprecatedWrappingReader(Symbol sym, String macro){
 		this.sym = sym;
-                this.macro = macro;
+		this.macro = macro;
 	}
 
 	public Object invoke(Object reader, Object quote) {
-                System.out.println("WARNING: reader macro " + macro +
-                                   " is deprecated; use " + sym.getName() +
-                                   " instead");
+		System.out.println("WARNING: reader macro " + macro +
+		                   " is deprecated; use " + sym.getName() +
+		                   " instead");
 		PushbackReader r = (PushbackReader) reader;
 		Object o = read(r, true, null, true);
 		return RT.list(sym, o);
@@ -607,13 +605,13 @@ public static class DispatchReader extends AFn{
 
 		// Try the ctor reader first
 		if(fn == null) {
-			unread((PushbackReader) reader, ch);
-			Object result = ctorReader.invoke(reader, ch);
+		unread((PushbackReader) reader, ch);
+		Object result = ctorReader.invoke(reader, ch);
 
-			if(result != null)
-				return result;
-			else
-				throw Util.runtimeException(String.format("No dispatch macro for: %c", (char) ch));
+		if(result != null)
+			return result;
+		else
+			throw Util.runtimeException(String.format("No dispatch macro for: %c", (char) ch));
 		}
 		return fn.invoke(reader, ch);
 	}
@@ -709,8 +707,12 @@ public static class MetaReader extends AFn{
 	public Object invoke(Object reader, Object caret) {
 		PushbackReader r = (PushbackReader) reader;
 		int line = -1;
+		int column = -1;
 		if(r instanceof LineNumberingPushbackReader)
+			{
 			line = ((LineNumberingPushbackReader) r).getLineNumber();
+			column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
+			}
 		Object meta = read(r, true, null, true);
 		if(meta instanceof Symbol || meta instanceof String)
 			meta = RT.map(RT.TAG_KEY, meta);
@@ -723,7 +725,9 @@ public static class MetaReader extends AFn{
 		if(o instanceof IMeta)
 			{
 			if(line != -1 && o instanceof ISeq)
-				meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line);
+				{
+				meta = ((IPersistentMap) meta).assoc(RT.LINE_KEY, line).assoc(RT.COLUMN_KEY, column);
+				}
 			if(o instanceof IReference)
 				{
 				((IReference)o).resetMeta((IPersistentMap) meta);
@@ -731,9 +735,9 @@ public static class MetaReader extends AFn{
 				}
 			Object ometa = RT.meta(o);
 			for(ISeq s = RT.seq(meta); s != null; s = s.next()) {
-				IMapEntry kv = (IMapEntry) s.first();
-				ometa = RT.assoc(ometa, kv.getKey(), kv.getValue());
-				}
+			IMapEntry kv = (IMapEntry) s.first();
+			ometa = RT.assoc(ometa, kv.getKey(), kv.getValue());
+			}
 			return ((IObj) o).withMeta((IPersistentMap) ometa);
 			}
 		else
@@ -787,21 +791,21 @@ public static class SyntaxQuoteReader extends AFn{
 			else if(sym.ns == null && sym.name.startsWith("."))
 				{
 				// Simply quote method names.
- 				}
-            else
+				}
+			else
 				{
-					Object maybeClass = null;
-					if(sym.ns != null)
-						maybeClass = Compiler.currentNS().getMapping(
-								Symbol.intern(null, sym.ns));
-					if(maybeClass instanceof Class)
-						{
-						// Classname/foo -> package.qualified.Classname/foo
-						sym = Symbol.intern(
-								((Class)maybeClass).getName(), sym.name);
-						}
-					else
-						sym = Compiler.resolveSymbol(sym);
+				Object maybeClass = null;
+				if(sym.ns != null)
+					maybeClass = Compiler.currentNS().getMapping(
+							Symbol.intern(null, sym.ns));
+				if(maybeClass instanceof Class)
+					{
+					// Classname/foo -> package.qualified.Classname/foo
+					sym = Symbol.intern(
+							((Class)maybeClass).getName(), sym.name);
+					}
+				else
+					sym = Compiler.resolveSymbol(sym);
 				}
 			ret = RT.list(Compiler.QUOTE, sym);
 			}
@@ -811,26 +815,28 @@ public static class SyntaxQuoteReader extends AFn{
 			throw new IllegalStateException("splice not in list");
 		else if(form instanceof IPersistentCollection)
 			{
-			if(form instanceof IPersistentMap)
+			if(form instanceof IRecord)
+				ret = form;
+			else if(form instanceof IPersistentMap)
 				{
 				IPersistentVector keyvals = flattenMap(form);
-                ret = RT.list(APPLY, HASHMAP, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(keyvals.seq()))));
+				ret = RT.list(APPLY, HASHMAP, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(keyvals.seq()))));
 				}
 			else if(form instanceof IPersistentVector)
-                {
-                ret = RT.list(APPLY, VECTOR, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentVector) form).seq()))));
-                }
+				{
+				ret = RT.list(APPLY, VECTOR, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentVector) form).seq()))));
+				}
 			else if(form instanceof IPersistentSet)
-                    {
-                    ret = RT.list(APPLY, HASHSET, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentSet) form).seq()))));
-                    }
+				{
+				ret = RT.list(APPLY, HASHSET, RT.list(SEQ, RT.cons(CONCAT, sqExpandList(((IPersistentSet) form).seq()))));
+				}
 			else if(form instanceof ISeq || form instanceof IPersistentList)
 				{
 				ISeq seq = RT.seq(form);
-                if(seq == null)
-                    ret = RT.cons(LIST,null);
-                else
-                    ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq)));
+				if(seq == null)
+					ret = RT.cons(LIST,null);
+				else
+					ret = RT.list(SEQ, RT.cons(CONCAT, sqExpandList(seq)));
 				}
 			else
 				throw new UnsupportedOperationException("Unknown Collection type");
@@ -845,8 +851,8 @@ public static class SyntaxQuoteReader extends AFn{
 
 		if(form instanceof IObj && RT.meta(form) != null)
 			{
-			//filter line numbers
-			IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY);
+			//filter line and column numbers
+			IPersistentMap newMeta = ((IObj) form).meta().without(RT.LINE_KEY).without(RT.COLUMN_KEY);
 			if(newMeta.count() > 0)
 				return RT.list(WITH_META, ret, syntaxQuote(((IObj) form).meta()));
 			}
@@ -932,12 +938,12 @@ public static class CharacterReader extends AFn{
 		else if(token.equals("return"))
 			return '\r';
 		else if(token.startsWith("u"))
-		    {
-			 char c = (char) readUnicodeChar(token, 1, 4, 16);
-			 if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
-			     throw Util.runtimeException("Invalid character constant: \\u" + Integer.toString(c, 16));
-			 return c;
-		    }
+			{
+			char c = (char) readUnicodeChar(token, 1, 4, 16);
+			if(c >= '\uD800' && c <= '\uDFFF') // surrogate code unit?
+				throw Util.runtimeException("Invalid character constant: \\u" + Integer.toString(c, 16));
+			return c;
+			}
 		else if(token.startsWith("o"))
 			{
 			int len = token.length() - 1;
@@ -957,15 +963,21 @@ public static class ListReader extends AFn{
 	public Object invoke(Object reader, Object leftparen) {
 		PushbackReader r = (PushbackReader) reader;
 		int line = -1;
+		int column = -1;
 		if(r instanceof LineNumberingPushbackReader)
+			{
 			line = ((LineNumberingPushbackReader) r).getLineNumber();
+			column = ((LineNumberingPushbackReader) r).getColumnNumber()-1;
+			}
 		List list = readDelimitedList(')', r, true);
 		if(list.isEmpty())
 			return PersistentList.EMPTY;
 		IObj s = (IObj) PersistentList.create(list);
 //		IObj s = (IObj) RT.seq(list);
 		if(line != -1)
-			return s.withMeta(RT.map(RT.LINE_KEY, line));
+			{
+			return s.withMeta(RT.map(RT.LINE_KEY, line, RT.COLUMN_KEY, column));
+			}
 		else
 			return s;
 	}
@@ -1007,10 +1019,10 @@ static class CtorReader extends AFn{
 public static class EvalReader extends AFn{
 	public Object invoke(Object reader, Object eq) {
 		if (!RT.booleanCast(RT.READEVAL.deref()))
-	    {
-		  throw Util.runtimeException("EvalReader not allowed when *read-eval* is false.");
-	    }
-		
+			{
+			throw Util.runtimeException("EvalReader not allowed when *read-eval* is false.");
+			}
+
 		PushbackReader r = (PushbackReader) reader;
 		Object o = read(r, true, null, true);
 		if(o instanceof Symbol)
@@ -1097,8 +1109,8 @@ public static class UnreadableReader extends AFn{
 
 public static List readDelimitedList(char delim, PushbackReader r, boolean isRecursive) {
 	final int firstline =
-		(r instanceof LineNumberingPushbackReader) ?
-		((LineNumberingPushbackReader) r).getLineNumber() : -1;
+			(r instanceof LineNumberingPushbackReader) ?
+			((LineNumberingPushbackReader) r).getLineNumber() : -1;
 
 	ArrayList a = new ArrayList();
 
@@ -1146,8 +1158,8 @@ public static class CtorReader extends AFn{
 	public Object invoke(Object reader, Object firstChar){
 		PushbackReader r = (PushbackReader) reader;
 		Object name = read(r, true, null, false);
-                if (!(name instanceof Symbol)) 
-                        throw new RuntimeException("Reader tag must be a symbol");
+		if (!(name instanceof Symbol))
+			throw new RuntimeException("Reader tag must be a symbol");
 		Symbol sym = (Symbol)name;
 		return sym.getName().contains(".") ? readRecord(r, sym) : readTagged(r, sym);
 	}
@@ -1158,30 +1170,43 @@ public static class CtorReader extends AFn{
 		ILookup data_readers = (ILookup)RT.DATA_READERS.deref();
 		IFn data_reader = (IFn)RT.get(data_readers, tag);
 		if(data_reader == null){
-                        data_readers = (ILookup)RT.DEFAULT_DATA_READERS.deref();
-                        data_reader = (IFn)RT.get(data_readers, tag);
-                        if(data_reader == null)
-                                throw new RuntimeException("No reader function for tag " + tag.toString());
-                }
+		data_readers = (ILookup)RT.DEFAULT_DATA_READERS.deref();
+		data_reader = (IFn)RT.get(data_readers, tag);
+		if(data_reader == null){
+		IFn default_reader = (IFn)RT.DEFAULT_DATA_READER_FN.deref();
+		if(default_reader != null)
+			return default_reader.invoke(tag, o);
+		else
+			throw new RuntimeException("No reader function for tag " + tag.toString());
+		}
+		}
 
-                return data_reader.invoke(o);
+		return data_reader.invoke(o);
 	}
 
-        private Object readRecord(PushbackReader r, Symbol recordName){
-		Class recordClass = RT.classForName(recordName.toString());
+	private Object readRecord(PushbackReader r, Symbol recordName){
+        boolean readeval = RT.booleanCast(RT.READEVAL.deref());
+
+	    if(!readeval)
+		    {
+		    throw Util.runtimeException("Record construction syntax can only be used when *read-eval* == true");
+		    }
+
+		Class recordClass = RT.classForNameNonLoading(recordName.toString());
+
 		char endch;
 		boolean shortForm = true;
 		int ch = read1(r);
 
 		// flush whitespace
-		//while(isWhitespace(ch))
-		//	ch = read1(r);
+		while(isWhitespace(ch))
+			ch = read1(r);
 
 		// A defrecord ctor can take two forms. Check for map->R version first.
 		if(ch == '{')
 			{
-				endch = '}';
-				shortForm = false;
+			endch = '}';
+			shortForm = false;
 			}
 		else if (ch == '[')
 			endch = ']';
@@ -1216,7 +1241,7 @@ public static class CtorReader extends AFn{
 			ret = Reflector.invokeStaticMethod(recordClass, "create", new Object[]{vals});
 			}
 
-	return ret;
+		return ret;
 	}
 }
 
diff --git a/src/jvm/clojure/lang/LockingTransaction.java b/src/jvm/clojure/lang/LockingTransaction.java
index 44d2de6..c28d2b2 100644
--- a/src/jvm/clojure/lang/LockingTransaction.java
+++ b/src/jvm/clojure/lang/LockingTransaction.java
@@ -222,13 +222,23 @@ static LockingTransaction getRunning(){
 
 static public Object runInTransaction(Callable fn) throws Exception{
 	LockingTransaction t = transaction.get();
-	if(t == null)
+	Object ret;
+	if(t == null) {
 		transaction.set(t = new LockingTransaction());
+		try {
+			ret = t.run(fn);
+		} finally {
+			transaction.remove();
+		}
+	} else {
+		if(t.info != null) {
+			ret = fn.call();
+		} else {
+			ret = t.run(fn);
+		}
+	}
 
-	if(t.info != null)
-		return fn.call();
-
-	return t.run(fn);
+	return ret;
 }
 
 static class Notify{
@@ -305,7 +315,6 @@ Object run(Callable fn) throws Exception{
 
 				//at this point, all values calced, all refs to be written locked
 				//no more client code to be called
-				long msecs = System.currentTimeMillis();
 				long commitPoint = getCommitPoint();
 				for(Map.Entry<Ref, Object> e : vals.entrySet())
 					{
@@ -316,12 +325,12 @@ Object run(Callable fn) throws Exception{
 
 					if(ref.tvals == null)
 						{
-						ref.tvals = new Ref.TVal(newval, commitPoint, msecs);
+						ref.tvals = new Ref.TVal(newval, commitPoint);
 						}
 					else if((ref.faults.get() > 0 && hcount < ref.maxHistory)
 							|| hcount < ref.minHistory)
 						{
-						ref.tvals = new Ref.TVal(newval, commitPoint, msecs, ref.tvals);
+						ref.tvals = new Ref.TVal(newval, commitPoint, ref.tvals);
 						ref.faults.set(0);
 						}
 					else
@@ -329,7 +338,6 @@ Object run(Callable fn) throws Exception{
 						ref.tvals = ref.tvals.next;
 						ref.tvals.val = newval;
 						ref.tvals.point = commitPoint;
-						ref.tvals.msecs = msecs;
 						}
 					if(ref.getWatches().count() > 0)
 						notify.add(new Notify(ref, oldval, newval));
diff --git a/src/jvm/clojure/lang/MultiFn.java b/src/jvm/clojure/lang/MultiFn.java
index 1fbfc76..f7bca33 100644
--- a/src/jvm/clojure/lang/MultiFn.java
+++ b/src/jvm/clojure/lang/MultiFn.java
@@ -13,16 +13,18 @@
 package clojure.lang;
 
 import java.util.Map;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 public class MultiFn extends AFn{
 final public IFn dispatchFn;
 final public Object defaultDispatchVal;
 final public IRef hierarchy;
 final String name;
-IPersistentMap methodTable;
-IPersistentMap preferTable;
-IPersistentMap methodCache;
-Object cachedHierarchy;
+final ReentrantReadWriteLock rw;
+volatile IPersistentMap methodTable;
+volatile IPersistentMap preferTable;
+volatile IPersistentMap methodCache;
+volatile Object cachedHierarchy;
 
 static final Var assoc = RT.var("clojure.core", "assoc");
 static final Var dissoc = RT.var("clojure.core", "dissoc");
@@ -30,6 +32,7 @@ static final Var isa = RT.var("clojure.core", "isa?");
 static final Var parents = RT.var("clojure.core", "parents");
 
 public MultiFn(String name, IFn dispatchFn, Object defaultDispatchVal, IRef hierarchy) {
+	this.rw = new ReentrantReadWriteLock();
 	this.name = name;
 	this.dispatchFn = dispatchFn;
 	this.defaultDispatchVal = defaultDispatchVal;
@@ -40,35 +43,63 @@ public MultiFn(String name, IFn dispatchFn, Object defaultDispatchVal, IRef hier
 	cachedHierarchy = null;
 }
 
-synchronized public MultiFn reset(){
-	methodTable = methodCache = preferTable = PersistentHashMap.EMPTY;
-	cachedHierarchy = null;
-	return this;
+public MultiFn reset(){
+	rw.writeLock().lock();
+	try{
+		methodTable = methodCache = preferTable = PersistentHashMap.EMPTY;
+		cachedHierarchy = null;
+		return this;
+	}
+	finally {
+		rw.writeLock().unlock();
+	}
 }
 
-synchronized public MultiFn addMethod(Object dispatchVal, IFn method) {
-	methodTable = getMethodTable().assoc(dispatchVal, method);
-	resetCache();
-	return this;
+public MultiFn addMethod(Object dispatchVal, IFn method) {
+	rw.writeLock().lock();
+	try{
+		methodTable = getMethodTable().assoc(dispatchVal, method);
+		resetCache();
+		return this;
+	}
+	finally {
+		rw.writeLock().unlock();
+	}
 }
 
-synchronized public MultiFn removeMethod(Object dispatchVal) {
-	methodTable = getMethodTable().without(dispatchVal);
-	resetCache();
-	return this;
+public MultiFn removeMethod(Object dispatchVal) {
+	rw.writeLock().lock();
+	try
+		{
+		methodTable = getMethodTable().without(dispatchVal);
+		resetCache();
+		return this;
+		}
+	finally
+		{
+		rw.writeLock().unlock();
+		}
 }
 
-synchronized public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) {
-	if(prefers(dispatchValY, dispatchValX))
-		throw new IllegalStateException(
-				String.format("Preference conflict in multimethod '%s': %s is already preferred to %s",
-				              name, dispatchValY, dispatchValX));
-	preferTable = getPreferTable().assoc(dispatchValX, RT.conj((IPersistentCollection) RT.get(getPreferTable(),
-	                                                                                     dispatchValX,
-	                                                                                     PersistentHashSet.EMPTY),
-	                                                      dispatchValY));
-	resetCache();
-	return this;
+public MultiFn preferMethod(Object dispatchValX, Object dispatchValY) {
+	rw.writeLock().lock();
+	try
+		{
+		if(prefers(dispatchValY, dispatchValX))
+			throw new IllegalStateException(
+					String.format("Preference conflict in multimethod '%s': %s is already preferred to %s",
+					              name, dispatchValY, dispatchValX));
+		preferTable = getPreferTable().assoc(dispatchValX, RT.conj((IPersistentCollection) RT.get(getPreferTable(),
+		                                                                                     dispatchValX,
+		                                                                                     PersistentHashSet.EMPTY),
+		                                                      dispatchValY));
+		resetCache();
+		return this;
+		}
+	finally
+		{
+		rw.writeLock().unlock();
+		}
 }
 
 private boolean prefers(Object x, Object y) {
@@ -97,18 +128,26 @@ private boolean dominates(Object x, Object y) {
 }
 
 private IPersistentMap resetCache() {
-	methodCache = getMethodTable();
-	cachedHierarchy = hierarchy.deref();
-	return methodCache;
+	rw.writeLock().lock();
+	try
+		{
+		methodCache = getMethodTable();
+		cachedHierarchy = hierarchy.deref();
+		return methodCache;
+		}
+	finally
+		{
+		rw.writeLock().unlock();
+		}
 }
 
-synchronized public IFn getMethod(Object dispatchVal) {
+ public IFn getMethod(Object dispatchVal) {
 	if(cachedHierarchy != hierarchy.deref())
 		resetCache();
 	IFn targetFn = (IFn) methodCache.valAt(dispatchVal);
 	if(targetFn != null)
 		return targetFn;
-	targetFn = findAndCacheBestMethod(dispatchVal);
+	 targetFn = findAndCacheBestMethod(dispatchVal);
 	if(targetFn != null)
 		return targetFn;
 	targetFn = (IFn) getMethodTable().valAt(defaultDispatchVal);
@@ -124,34 +163,59 @@ private IFn getFn(Object dispatchVal) {
 }
 
 private IFn findAndCacheBestMethod(Object dispatchVal) {
-	Map.Entry bestEntry = null;
-	for(Object o : getMethodTable())
+	rw.readLock().lock();
+	Map.Entry bestEntry;
+	IPersistentMap mt = methodTable;
+	IPersistentMap pt = preferTable;
+	Object ch = cachedHierarchy;
+	try
 		{
-		Map.Entry e = (Map.Entry) o;
-		if(isA(dispatchVal, e.getKey()))
+		bestEntry = null;
+		for(Object o : getMethodTable())
 			{
-			if(bestEntry == null || dominates(e.getKey(), bestEntry.getKey()))
-				bestEntry = e;
-			if(!dominates(bestEntry.getKey(), e.getKey()))
-				throw new IllegalArgumentException(
-						String.format(
-								"Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred",
-								name, dispatchVal, e.getKey(), bestEntry.getKey()));
+			Map.Entry e = (Map.Entry) o;
+			if(isA(dispatchVal, e.getKey()))
+				{
+				if(bestEntry == null || dominates(e.getKey(), bestEntry.getKey()))
+					bestEntry = e;
+				if(!dominates(bestEntry.getKey(), e.getKey()))
+					throw new IllegalArgumentException(
+							String.format(
+									"Multiple methods in multimethod '%s' match dispatch value: %s -> %s and %s, and neither is preferred",
+									name, dispatchVal, e.getKey(), bestEntry.getKey()));
+				}
 			}
+		if(bestEntry == null)
+			return null;
 		}
-	if(bestEntry == null)
-		return null;
+	finally
+		{
+		rw.readLock().unlock();
+		}
+
+
 	//ensure basis has stayed stable throughout, else redo
-	if(cachedHierarchy == hierarchy.deref())
+	rw.writeLock().lock();
+	try
 		{
-		//place in cache
-		methodCache = methodCache.assoc(dispatchVal, bestEntry.getValue());
-		return (IFn) bestEntry.getValue();
+		if( mt == methodTable &&
+		    pt == preferTable &&
+		    ch == cachedHierarchy &&
+			cachedHierarchy == hierarchy.deref())
+			{
+			//place in cache
+			methodCache = methodCache.assoc(dispatchVal, bestEntry.getValue());
+			return (IFn) bestEntry.getValue();
+			}
+		else
+			{
+			resetCache();
+			return findAndCacheBestMethod(dispatchVal);
+			}
 		}
-	else
+	finally
 		{
-		resetCache();
-		return findAndCacheBestMethod(dispatchVal);
+		rw.writeLock().unlock();
 		}
 }
 
diff --git a/src/jvm/clojure/lang/Murmur3.java b/src/jvm/clojure/lang/Murmur3.java
new file mode 100644
index 0000000..7a8e552
--- /dev/null
+++ b/src/jvm/clojure/lang/Murmur3.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2011 The Guava Authors
+ *
+ * Licensed 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.
+ */
+
+/*
+ * MurmurHash3 was written by Austin Appleby, and is placed in the public
+ * domain. The author hereby disclaims copyright to this source code.
+ */
+
+/*
+ * Source:
+ * http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
+ * (Modified to adapt to Guava coding conventions and to use the HashFunction interface)
+ */
+
+/**
+ * Modified to remove stuff Clojure doesn't need, placed under clojure.lang namespace,
+ * all fns made static, added hashOrdered/Unordered
+ */
+
+package clojure.lang;
+
+import java.io.Serializable;
+import java.nio.ByteBuffer;
+
+/**
+ * See http://smhasher.googlecode.com/svn/trunk/MurmurHash3.cpp
+ * MurmurHash3_x86_32
+ *
+ * @author Austin Appleby
+ * @author Dimitris Andreou
+ * @author Kurt Alfred Kluever
+ */
+
+public final class Murmur3{
+private static final int seed = 0;
+private static final int C1 = 0xcc9e2d51;
+private static final int C2 = 0x1b873593;
+
+public static int hashInt(int input){
+	if(input == 0) return 0;
+	int k1 = mixK1(input);
+	int h1 = mixH1(seed, k1);
+
+	return fmix(h1, 4);
+}
+
+public static int hashLong(long input){
+	if(input == 0) return 0;
+	int low = (int) input;
+	int high = (int) (input >>> 32);
+
+	int k1 = mixK1(low);
+	int h1 = mixH1(seed, k1);
+
+	k1 = mixK1(high);
+	h1 = mixH1(h1, k1);
+
+	return fmix(h1, 8);
+}
+
+public static int hashUnencodedChars(CharSequence input){
+	int h1 = seed;
+
+	// step through the CharSequence 2 chars at a time
+	for(int i = 1; i < input.length(); i += 2)
+		{
+		int k1 = input.charAt(i - 1) | (input.charAt(i) << 16);
+		k1 = mixK1(k1);
+		h1 = mixH1(h1, k1);
+		}
+
+	// deal with any remaining characters
+	if((input.length() & 1) == 1)
+		{
+		int k1 = input.charAt(input.length() - 1);
+		k1 = mixK1(k1);
+		h1 ^= k1;
+		}
+
+	return fmix(h1, 2 * input.length());
+}
+
+public static int mixCollHash(int hash, int count){
+	int h1 = seed;
+	int k1 = mixK1(hash);
+	h1 = mixH1(h1, k1);
+	return fmix(h1, count);
+}
+
+public static int hashOrdered(Iterable xs){
+	int n = 0;
+	int hash = 1;
+
+	for(Object x : xs)
+		{
+		hash = 31 * hash + Util.hasheq(x);
+		++n;
+		}
+
+	return mixCollHash(hash, n);
+}
+
+public static int hashUnordered(Iterable xs){
+	int hash = 0;
+	int n = 0;
+	for(Object x : xs)
+		{
+		hash += Util.hasheq(x);
+		++n;
+		}	
+
+	return mixCollHash(hash, n);
+}
+
+private static int mixK1(int k1){
+	k1 *= C1;
+	k1 = Integer.rotateLeft(k1, 15);
+	k1 *= C2;
+	return k1;
+}
+
+private static int mixH1(int h1, int k1){
+	h1 ^= k1;
+	h1 = Integer.rotateLeft(h1, 13);
+	h1 = h1 * 5 + 0xe6546b64;
+	return h1;
+}
+
+// Finalization mix - force all bits of a hash block to avalanche
+private static int fmix(int h1, int length){
+	h1 ^= length;
+	h1 ^= h1 >>> 16;
+	h1 *= 0x85ebca6b;
+	h1 ^= h1 >>> 13;
+	h1 *= 0xc2b2ae35;
+	h1 ^= h1 >>> 16;
+	return h1;
+}
+
+}
diff --git a/src/jvm/clojure/lang/Numbers.java b/src/jvm/clojure/lang/Numbers.java
index a50f287..b349c9d 100644
--- a/src/jvm/clojure/lang/Numbers.java
+++ b/src/jvm/clojure/lang/Numbers.java
@@ -389,6 +389,23 @@ static public long shiftRight(long x, long n){
 	return x >> n;
 }
 
+static public int unsignedShiftRightInt(int x, int n){
+	return x >>> n;
+}
+
+static public long unsignedShiftRight(Object x, Object y){
+    return unsignedShiftRight(bitOpsCast(x),bitOpsCast(y));
+}
+static public long unsignedShiftRight(Object x, long y){
+    return unsignedShiftRight(bitOpsCast(x),y);
+}
+static public long unsignedShiftRight(long x, Object y){
+    return unsignedShiftRight(x,bitOpsCast(y));
+}
+static public long unsignedShiftRight(long x, long n){
+	return x >>> n;
+}
+
 final static class LongOps implements Ops{
 	public Ops combine(Ops y){
 		return y.opsWith(this);
@@ -444,6 +461,8 @@ final static class LongOps implements Ops{
 
 	final public Number multiplyP(Number x, Number y){
 		long lx = x.longValue(), ly = y.longValue();
+    if (lx == Long.MIN_VALUE && ly < 0)
+      return BIGINT_OPS.multiply(x, y);
 		long ret = lx * ly;
 		if (ly != 0 && ret/ly != lx)
 			return BIGINT_OPS.multiply(x, y);
@@ -898,7 +917,7 @@ final static class BigDecimalOps extends OpsP{
 	}
 
 	public boolean equiv(Number x, Number y){
-		return toBigDecimal(x).equals(toBigDecimal(y));
+		return toBigDecimal(x).compareTo(toBigDecimal(y)) == 0;
 	}
 
 	public boolean lt(Number x, Number y){
@@ -967,10 +986,27 @@ static int hasheq(Number x){
 	if(xc == Long.class
 		|| xc == Integer.class
 		|| xc == Short.class
-		|| xc == Byte.class)
+		|| xc == Byte.class
+		|| (xc == BigInteger.class && lte(x, Long.MAX_VALUE) && gte(x,Long.MIN_VALUE)))
 		{
 		long lpart = x.longValue();
-		return (int) (lpart ^ (lpart >>> 32));
+		return Murmur3.hashLong(lpart);
+		//return (int) (lpart ^ (lpart >>> 32));
+		}
+	if(xc == BigDecimal.class)
+		{
+		// stripTrailingZeros() to make all numerically equal
+		// BigDecimal values come out the same before calling
+		// hashCode.  Special check for 0 because
+		// stripTrailingZeros() does not do anything to values
+		// equal to 0 with different scales.
+		if (isZero(x))
+			return BigDecimal.ZERO.hashCode();
+		else
+			{
+			BigDecimal tmp = ((BigDecimal) x).stripTrailingZeros();
+			return tmp.hashCode();
+			}
 		}
 	return x.hashCode();
 }
@@ -1144,7 +1180,7 @@ static public short[] short_array(int size, Object init){
 		{
 		ISeq s = RT.seq(init);
 		for(int i = 0; i < size && s != null; i++, s = s.next())
-			ret[i] = (Short) s.first();
+			ret[i] = ((Number) s.first()).shortValue();
 		}
 	return ret;
 }
@@ -1158,7 +1194,7 @@ static public short[] short_array(Object sizeOrSeq){
 		int size = RT.count(s);
 		short[] ret = new short[size];
 		for(int i = 0; i < size && s != null; i++, s = s.next())
-			ret[i] = (Short) s.first();
+			ret[i] = ((Number) s.first()).shortValue();
 		return ret;
 		}
 }
@@ -1206,7 +1242,7 @@ static public byte[] byte_array(int size, Object init){
 		{
 		ISeq s = RT.seq(init);
 		for(int i = 0; i < size && s != null; i++, s = s.next())
-			ret[i] = (Byte) s.first();
+			ret[i] = ((Number) s.first()).byteValue();
 		}
 	return ret;
 }
@@ -1220,7 +1256,7 @@ static public byte[] byte_array(Object sizeOrSeq){
 		int size = RT.count(s);
 		byte[] ret = new byte[size];
 		for(int i = 0; i < size && s != null; i++, s = s.next())
-			ret[i] = (Byte)s.first();
+			ret[i] = ((Number) s.first()).byteValue();
 		return ret;
 		}
 }
@@ -1747,6 +1783,8 @@ static public Number decP(long x){
 
 
 static public long multiply(long x, long y){
+  if (x == Long.MIN_VALUE && y < 0)
+		return throwIntOverflow();
 	long ret = x * y;
 	if (y != 0 && ret/y != x)
 		return throwIntOverflow();
@@ -1754,6 +1792,8 @@ static public long multiply(long x, long y){
 }
 
 static public Number multiplyP(long x, long y){
+  if (x == Long.MIN_VALUE && y < 0)
+		return multiplyP((Number)x,(Number)y);
 	long ret = x * y;
 	if (y != 0 && ret/y != x)
 		return multiplyP((Number)x,(Number)y);
diff --git a/src/jvm/clojure/lang/PersistentArrayMap.java b/src/jvm/clojure/lang/PersistentArrayMap.java
index acd6474..30841a6 100644
--- a/src/jvm/clojure/lang/PersistentArrayMap.java
+++ b/src/jvm/clojure/lang/PersistentArrayMap.java
@@ -72,6 +72,68 @@ static public PersistentArrayMap createWithCheck(Object[] init){
 		}
 	return new PersistentArrayMap(init);
 }
+
+static public PersistentArrayMap createAsIfByAssoc(Object[] init){
+	// If this looks like it is doing busy-work, it is because it
+	// is achieving these goals: O(n^2) run time like
+	// createWithCheck(), never modify init arg, and only
+	// allocate memory if there are duplicate keys.
+	int n = 0;
+	for(int i=0;i< init.length;i += 2)
+		{
+		boolean duplicateKey = false;
+		for(int j=0;j<i;j += 2)
+			{
+			if(equalKey(init[i],init[j]))
+				{
+				duplicateKey = true;
+				break;
+				}
+			}
+		if(!duplicateKey)
+			n += 2;
+		}
+	if(n < init.length)
+		{
+		// Create a new shorter array with unique keys, and
+		// the last value associated with each key.  To behave
+		// like assoc, the first occurrence of each key must
+		// be used, since its metadata may be different than
+		// later equal keys.
+		Object[] nodups = new Object[n];
+		int m = 0;
+		for(int i=0;i< init.length;i += 2)
+			{
+			boolean duplicateKey = false;
+			for(int j=0;j<m;j += 2)
+				{
+				if(equalKey(init[i],nodups[j]))
+					{
+					duplicateKey = true;
+					break;
+					}
+				}
+			if(!duplicateKey)
+				{
+				int j;
+				for (j=init.length-2; j>=i; j -= 2)
+					{
+					if(equalKey(init[i],init[j]))
+						{
+						break;
+						}
+					}
+				nodups[m] = init[i];
+				nodups[m+1] = init[j+1];
+				m += 2;
+				}
+			}
+		if (m != n)
+			throw new IllegalArgumentException("Internal error: m=" + m);
+		init = nodups;
+		}
+	return new PersistentArrayMap(init);
+}
 /**
  * This ctor captures/aliases the passed array, so do not modify later
  *
@@ -188,16 +250,33 @@ public int capacity(){
 	return count();
 }
 
-private int indexOf(Object key){
-	for(int i = 0; i < array.length; i += 2)
-		{
-		if(equalKey(array[i], key))
-			return i;
-		}
+private int indexOfObject(Object key){
+    Util.EquivPred ep = Util.equivPred(key);
+    for(int i = 0; i < array.length; i += 2)
+        {
+        if(ep.equiv(key, array[i]))
+            return i;
+        }
 	return -1;
 }
 
+private int indexOf(Object key){
+    if(key instanceof Keyword)
+        {
+        for(int i = 0; i < array.length; i += 2)
+            {
+            if(key == array[i])
+                return i;
+            }
+    	return -1;
+        }
+    else
+        return indexOfObject(key);
+}
+
 static boolean equalKey(Object k1, Object k2){
+    if(k1 instanceof Keyword)
+        return k1 == k2;
 	return Util.equiv(k1, k2);
 }
 
@@ -282,6 +361,8 @@ static class Iter implements Iterator{
 public Object kvreduce(IFn f, Object init){
     for(int i=0;i < array.length;i+=2){
         init = f.invoke(init, array[i], array[i+1]);
+	    if(RT.isReduced(init))
+		    return ((IDeref)init).deref();
         }
     return init;
 }
diff --git a/src/jvm/clojure/lang/PersistentHashMap.java b/src/jvm/clojure/lang/PersistentHashMap.java
index b319d03..34a90c4 100644
--- a/src/jvm/clojure/lang/PersistentHashMap.java
+++ b/src/jvm/clojure/lang/PersistentHashMap.java
@@ -11,9 +11,8 @@
 package clojure.lang;
 
 import java.io.Serializable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
+import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicReference;
 
 /*
@@ -180,10 +179,32 @@ public Iterator iterator(){
 
 public Object kvreduce(IFn f, Object init){
     init = hasNull?f.invoke(init,null,nullValue):init;
-    if(root != null){
-        return root.kvreduce(f,init);
-    }
-    return init;
+	if(RT.isReduced(init))
+		return ((IDeref)init).deref();
+	if(root != null){
+		init = root.kvreduce(f,init);
+		if(RT.isReduced(init))
+			return ((IDeref)init).deref();
+		else
+			return init;
+	}
+	return init;
+}
+
+public Object fold(long n, final IFn combinef, final IFn reducef,
+                   IFn fjinvoke, final IFn fjtask, final IFn fjfork, final IFn fjjoin){
+	//we are ignoring n for now
+	Callable top = new Callable(){
+		public Object call() throws Exception{
+			Object ret = combinef.invoke();
+			if(root != null)
+				ret = combinef.invoke(ret, root.fold(combinef,reducef,fjtask,fjfork,fjjoin));
+			return hasNull?
+			       combinef.invoke(ret,reducef.invoke(combinef.invoke(),null,nullValue))
+			       :ret;
+		}
+	};
+	return fjinvoke.invoke(top);
 }
 
 public int count(){
@@ -287,7 +308,7 @@ static final class TransientHashMap extends ATransientMap {
 			else
 				return notFound;
 		if (root == null)
-			return null;
+			return notFound;
 		return root.find(0, hash(key), key, notFound);
 	}
 
@@ -322,6 +343,7 @@ static interface INode extends Serializable {
 
     public Object kvreduce(IFn f, Object init);
 
+	Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin);
 }
 
 final static class ArrayNode implements INode{
@@ -383,14 +405,62 @@ final static class ArrayNode implements INode{
 	}
 
     public Object kvreduce(IFn f, Object init){
-        for(INode node : array)
-            {
-            if(node != null)
+        for(INode node : array){
+            if(node != null){
                 init = node.kvreduce(f,init);
-            }
+	            if(RT.isReduced(init))
+		            return init;
+	            }
+	        }
         return init;
     }
 
+	public Object fold(final IFn combinef, final IFn reducef,
+	                   final IFn fjtask, final IFn fjfork, final IFn fjjoin){
+		List<Callable> tasks = new ArrayList();
+		for(final INode node : array){
+			if(node != null){
+				tasks.add(new Callable(){
+					public Object call() throws Exception{
+						return node.fold(combinef, reducef, fjtask, fjfork, fjjoin);
+					}
+				});
+				}
+			}
+
+		return foldTasks(tasks,combinef,fjtask,fjfork,fjjoin);
+		}
+
+	static public Object foldTasks(List<Callable> tasks, final IFn combinef,
+	                          final IFn fjtask, final IFn fjfork, final IFn fjjoin){
+
+		if(tasks.isEmpty())
+			return combinef.invoke();
+
+		if(tasks.size() == 1){
+			Object ret = null;
+			try
+				{
+				return tasks.get(0).call();
+				}
+			catch(Exception e)
+				{
+				throw Util.sneakyThrow(e);
+				}
+			}
+
+		List<Callable> t1 = tasks.subList(0,tasks.size()/2);
+		final List<Callable> t2 = tasks.subList(tasks.size()/2, tasks.size());
+
+		Object forked = fjfork.invoke(fjtask.invoke(new Callable() {
+			public Object call() throws Exception{
+				return foldTasks(t2,combinef,fjtask,fjfork,fjjoin);
+			}
+		}));
+
+		return combinef.invoke(foldTasks(t1,combinef,fjtask,fjfork,fjjoin),fjjoin.invoke(forked));
+	}
+
 
 	private ArrayNode ensureEditable(AtomicReference<Thread> edit){
 		if(this.edit == edit)
@@ -625,6 +695,9 @@ final static class BitmapIndexedNode implements INode{
          return NodeSeq.kvreduce(array,f,init);
     }
 
+	public Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin){
+		return NodeSeq.kvreduce(array, reducef, combinef.invoke());
+	}
 
 	private BitmapIndexedNode ensureEditable(AtomicReference<Thread> edit){
 		if(this.edit == edit)
@@ -767,10 +840,10 @@ final static class HashCollisionNode implements INode{
 					return this;
 				return new HashCollisionNode(null, hash, count, cloneAndSet(array, idx + 1, val));
 			}
-			Object[] newArray = new Object[array.length + 2];
-			System.arraycopy(array, 0, newArray, 0, array.length);
-			newArray[array.length] = key;
-			newArray[array.length + 1] = val;
+			Object[] newArray = new Object[2 * (count + 1)];
+			System.arraycopy(array, 0, newArray, 0, 2 * count);
+			newArray[2 * count] = key;
+			newArray[2 * count + 1] = val;
 			addedLeaf.val = addedLeaf;
 			return new HashCollisionNode(edit, hash, count + 1, newArray);
 		}
@@ -814,6 +887,10 @@ final static class HashCollisionNode implements INode{
          return NodeSeq.kvreduce(array,f,init);
     }
 
+	public Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin){
+		return NodeSeq.kvreduce(array, reducef, combinef.invoke());
+	}
+
 	public int findIndex(Object key){
 		for(int i = 0; i < 2*count; i+=2)
 			{
@@ -1013,21 +1090,21 @@ private static INode createNode(int shift, Object key1, Object val1, int key2has
 	int key1hash = hash(key1);
 	if(key1hash == key2hash)
 		return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
-	Box _ = new Box(null);
+	Box addedLeaf = new Box(null);
 	AtomicReference<Thread> edit = new AtomicReference<Thread>();
 	return BitmapIndexedNode.EMPTY
-		.assoc(edit, shift, key1hash, key1, val1, _)
-		.assoc(edit, shift, key2hash, key2, val2, _);
+		.assoc(edit, shift, key1hash, key1, val1, addedLeaf)
+		.assoc(edit, shift, key2hash, key2, val2, addedLeaf);
 }
 
 private static INode createNode(AtomicReference<Thread> edit, int shift, Object key1, Object val1, int key2hash, Object key2, Object val2) {
 	int key1hash = hash(key1);
 	if(key1hash == key2hash)
 		return new HashCollisionNode(null, key1hash, 2, new Object[] {key1, val1, key2, val2});
-	Box _ = new Box(null);
+	Box addedLeaf = new Box(null);
 	return BitmapIndexedNode.EMPTY
-		.assoc(edit, shift, key1hash, key1, val1, _)
-		.assoc(edit, shift, key2hash, key2, val2, _);
+		.assoc(edit, shift, key1hash, key1, val1, addedLeaf)
+		.assoc(edit, shift, key2hash, key2, val2, addedLeaf);
 }
 
 private static int bitpos(int hash, int shift){
@@ -1058,6 +1135,8 @@ static final class NodeSeq extends ASeq {
                  if(node != null)
                      init = node.kvreduce(f,init);
                  }
+             if(RT.isReduced(init))
+	             return init;
              }
         return init;
     }
diff --git a/src/jvm/clojure/lang/PersistentList.java b/src/jvm/clojure/lang/PersistentList.java
index 1d27d9e..edd2cb0 100644
--- a/src/jvm/clojure/lang/PersistentList.java
+++ b/src/jvm/clojure/lang/PersistentList.java
@@ -27,7 +27,7 @@ public static IFn creator = new RestFn(){
 	final protected Object doInvoke(Object args) {
 		if(args instanceof ArraySeq)
 			{
-			Object[] argsarray = (Object[]) ((ArraySeq) args).array;
+			Object[] argsarray = ((ArraySeq) args).array;
 			IPersistentList ret = EMPTY;
 			for(int i = argsarray.length - 1; i >= 0; --i)
 				ret = (IPersistentList) ret.cons(argsarray[i]);
@@ -126,12 +126,17 @@ public Object reduce(IFn f, Object start) {
 }
 
 
-    static class EmptyList extends Obj implements IPersistentList, List, ISeq, Counted{
+    static class EmptyList extends Obj implements IPersistentList, List, ISeq, Counted, IHashEq{
+	static final int hasheq = Murmur3.hashOrdered(Collections.EMPTY_LIST);
 
 	public int hashCode(){
 		return 1;
 	}
 
+	public int hasheq(){
+		return hasheq;
+	}
+
     public boolean equals(Object o) {
         return (o instanceof Sequential || o instanceof List) && RT.seq(o) == null;
     }
diff --git a/src/jvm/clojure/lang/PersistentQueue.java b/src/jvm/clojure/lang/PersistentQueue.java
index fed7e40..ef126bd 100644
--- a/src/jvm/clojure/lang/PersistentQueue.java
+++ b/src/jvm/clojure/lang/PersistentQueue.java
@@ -17,24 +17,25 @@ import java.util.Iterator;
 /**
  * conses onto rear, peeks/pops from front
  * See Okasaki's Batched Queues
- * This differs in that it uses a PersistentVector as the rear, which is in-order,
+ * This differs in that it uses a PersistentVector as the rear, which is in-order,
  * so no reversing or suspensions required for persistent use
  */
 
-public class PersistentQueue extends Obj implements IPersistentList, Collection, Counted{
+public class PersistentQueue extends Obj implements IPersistentList, Collection, Counted, IHashEq{
 
-final public static PersistentQueue EMPTY = new PersistentQueue(null, 0, null, null);
+final public static PersistentQueue EMPTY = new PersistentQueue(null, 0, null, null);
 
 //*
-final int cnt;
+final int cnt;
 final ISeq f;
 final PersistentVector r;
 //static final int INITIAL_REAR_SIZE = 4;
 int _hash = -1;
+int _hasheq = -1;
 
-PersistentQueue(IPersistentMap meta, int cnt, ISeq f, PersistentVector r){
+PersistentQueue(IPersistentMap meta, int cnt, ISeq f, PersistentVector r){
 	super(meta);
-	this.cnt = cnt;
+	this.cnt = cnt;
 	this.f = f;
 	this.r = r;
 }
@@ -70,16 +71,30 @@ public boolean equals(Object obj){
 public int hashCode(){
 	if(_hash == -1)
 		{
-		int hash = 0;
+		int hash = 1;
 		for(ISeq s = seq(); s != null; s = s.next())
 			{
-			hash = Util.hashCombine(hash, Util.hash(s.first()));
+			hash = 31 * hash + (s.first() == null ? 0 : s.first().hashCode());
 			}
 		this._hash = hash;
 		}
 	return _hash;
 }
 
+public int hasheq() {
+	if(_hasheq == -1)
+		{
+//		int hash = 1;
+//		for(ISeq s = seq(); s != null; s = s.next())
+//			{
+//			hash = 31 * hash + Util.hasheq(s.first());
+//			}
+//		this._hasheq = hash;
+		_hasheq  = Murmur3.hashOrdered(this);
+		}
+    return _hasheq;
+}
+
 public Object peek(){
 	return RT.first(f);
 }
@@ -95,11 +110,11 @@ public PersistentQueue pop(){
 		f1 = RT.seq(r);
 		r1 = null;
 		}
-	return new PersistentQueue(meta(), cnt - 1, f1, r1);
+	return new PersistentQueue(meta(), cnt - 1, f1, r1);
 }
 
 public int count(){
-	return cnt;
+	return cnt;
 }
 
 public ISeq seq(){
@@ -110,17 +125,17 @@ public ISeq seq(){
 
 public PersistentQueue cons(Object o){
 	if(f == null)     //empty
-		return new PersistentQueue(meta(), cnt + 1, RT.list(o), null);
+		return new PersistentQueue(meta(), cnt + 1, RT.list(o), null);
 	else
-		return new PersistentQueue(meta(), cnt + 1, f, (r != null ? r : PersistentVector.EMPTY).cons(o));
+		return new PersistentQueue(meta(), cnt + 1, f, (r != null ? r : PersistentVector.EMPTY).cons(o));
 }
 
 public IPersistentCollection empty(){
-	return EMPTY.withMeta(meta());
+	return EMPTY.withMeta(meta());
 }
 
 public PersistentQueue withMeta(IPersistentMap meta){
-	return new PersistentQueue(meta, cnt, f, r);
+	return new PersistentQueue(meta, cnt, f, r);
 }
 
 static class Seq extends ASeq{
diff --git a/src/jvm/clojure/lang/PersistentTreeMap.java b/src/jvm/clojure/lang/PersistentTreeMap.java
index 46c15da..bf1e611 100644
--- a/src/jvm/clojure/lang/PersistentTreeMap.java
+++ b/src/jvm/clojure/lang/PersistentTreeMap.java
@@ -207,7 +207,9 @@ public NodeIterator iterator(){
 
 public Object kvreduce(IFn f, Object init){
     if(tree != null)
-        return tree.kvreduce(f,init);
+        init = tree.kvreduce(f,init);
+    if(RT.isReduced(init))
+        init = ((IDeref)init).deref();
     return init;
 }
 
@@ -537,12 +539,19 @@ static abstract class Node extends AMapEntry{
 	abstract Node replace(Object key, Object val, Node left, Node right);
 
     public Object kvreduce(IFn f, Object init){
-        init = f.invoke(init, key(), val());
-        if(left() != null)
+	    if(left() != null){
             init = left().kvreduce(f, init);
-        if(right() != null)
+	        if(RT.isReduced(init))
+		        return init;
+	        }
+	    init = f.invoke(init, key(), val());
+	    if(RT.isReduced(init))
+		    return init;
+
+	    if(right() != null){
             init = right().kvreduce(f, init);
-        return init;
+	        }
+	    return init;
     }
 
 
diff --git a/src/jvm/clojure/lang/PersistentVector.java b/src/jvm/clojure/lang/PersistentVector.java
index 3db6d7c..102c9ad 100644
--- a/src/jvm/clojure/lang/PersistentVector.java
+++ b/src/jvm/clojure/lang/PersistentVector.java
@@ -13,16 +13,17 @@
 package clojure.lang;
 
 import java.io.Serializable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicReference;
 
 public class PersistentVector extends APersistentVector implements IObj, IEditableCollection{
 
-static class Node implements Serializable {
-	transient final AtomicReference<Thread> edit;
-	final Object[] array;
+public static class Node implements Serializable {
+	transient public final AtomicReference<Thread> edit;
+	public final Object[] array;
 
-	Node(AtomicReference<Thread> edit, Object[] array){
+	public Node(AtomicReference<Thread> edit, Object[] array){
 		this.edit = edit;
 		this.array = array;
 	}
@@ -34,12 +35,12 @@ static class Node implements Serializable {
 }
 
 final static AtomicReference<Thread> NOEDIT = new AtomicReference<Thread>(null);
-final static Node EMPTY_NODE = new Node(NOEDIT, new Object[32]);
+public final static Node EMPTY_NODE = new Node(NOEDIT, new Object[32]);
 
 final int cnt;
-final int shift;
-final Node root;
-final Object[] tail;
+public final int shift;
+public final Node root;
+public final Object[] tail;
 final IPersistentMap _meta;
 
 
@@ -232,18 +233,48 @@ public ISeq seq(){
 	return chunkedSeq();
 }
 
+ at Override
+Iterator rangedIterator(final int start, final int end){
+	return new Iterator(){
+		int i = start;
+		int base = i - (i%32);
+		Object[] array = (start < count())?arrayFor(i):null;
+
+		public boolean hasNext(){
+			return i < end;
+			}
+
+		public Object next(){
+			if(i-base == 32){
+				array = arrayFor(i);
+				base += 32;
+				}
+			return array[i++ & 0x01f];
+			}
+
+		public void remove(){
+			throw new UnsupportedOperationException();
+		}
+	};
+}
+
+public Iterator iterator(){return rangedIterator(0,count());}
+
 public Object kvreduce(IFn f, Object init){
     int step = 0;
     for(int i=0;i<cnt;i+=step){
         Object[] array = arrayFor(i);
-        for(int j =0;j<array.length;++j)
+        for(int j =0;j<array.length;++j){
             init = f.invoke(init,j+i,array[j]);
+            if(RT.isReduced(init))
+	            return ((IDeref)init).deref();
+            }
         step = array.length;
     }
     return init;
 }
 
-static public final class ChunkedSeq extends ASeq implements IChunkedSeq{
+static public final class ChunkedSeq extends ASeq implements IChunkedSeq,Counted{
 
 	public final PersistentVector vec;
 	final Object[] node;
@@ -304,6 +335,10 @@ static public final class ChunkedSeq extends ASeq implements IChunkedSeq{
 			return new ChunkedSeq(vec, node, i, offset + 1);
 		return chunkedNext();
 	}
+
+	public int count(){
+		return vec.cnt - (i + offset);
+	}
 }
 
 public IPersistentCollection empty(){
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java
index 9042077..492df8d 100644
--- a/src/jvm/clojure/lang/RT.java
+++ b/src/jvm/clojure/lang/RT.java
@@ -168,6 +168,14 @@ Symbol.intern("SuppressWarnings"), SuppressWarnings.class
 // single instance of UTF-8 Charset, so as to avoid catching UnsupportedCharsetExceptions everywhere
 static public Charset UTF8 = Charset.forName("UTF-8");
 
+static Object readTrueFalseUnknown(String s){
+	if(s.equals("true"))
+		return Boolean.TRUE;
+	else if(s.equals("false"))
+		return Boolean.FALSE;
+	return Keyword.intern(null, "unknown");
+}
+
 static public final Namespace CLOJURE_NS = Namespace.findOrCreate(Symbol.intern("clojure.core"));
 //static final Namespace USER_NS = Namespace.findOrCreate(Symbol.intern("user"));
 final static public Var OUT =
@@ -181,12 +189,15 @@ final static public Var ERR =
 final static Keyword TAG_KEY = Keyword.intern(null, "tag");
 final static Keyword CONST_KEY = Keyword.intern(null, "const");
 final static public Var AGENT = Var.intern(CLOJURE_NS, Symbol.intern("*agent*"), null).setDynamic();
-final static public Var READEVAL = Var.intern(CLOJURE_NS, Symbol.intern("*read-eval*"), T).setDynamic();
+static Object readeval = readTrueFalseUnknown(System.getProperty("clojure.read.eval","true"));
+final static public Var READEVAL = Var.intern(CLOJURE_NS, Symbol.intern("*read-eval*"),  readeval).setDynamic();
 final static public Var DATA_READERS = Var.intern(CLOJURE_NS, Symbol.intern("*data-readers*"), RT.map()).setDynamic();
+final static public Var DEFAULT_DATA_READER_FN = Var.intern(CLOJURE_NS, Symbol.intern("*default-data-reader-fn*"), RT.map()).setDynamic();
 final static public Var DEFAULT_DATA_READERS = Var.intern(CLOJURE_NS, Symbol.intern("default-data-readers"), RT.map());
 final static public Var ASSERT = Var.intern(CLOJURE_NS, Symbol.intern("*assert*"), T).setDynamic();
 final static public Var MATH_CONTEXT = Var.intern(CLOJURE_NS, Symbol.intern("*math-context*"), null).setDynamic();
 static Keyword LINE_KEY = Keyword.intern(null, "line");
+static Keyword COLUMN_KEY = Keyword.intern(null, "column");
 static Keyword FILE_KEY = Keyword.intern(null, "file");
 static Keyword DECLARED_KEY = Keyword.intern(null, "declared");
 static Keyword DOC_KEY = Keyword.intern(null, "doc");
@@ -413,7 +424,7 @@ static public void load(String scriptbase, boolean failIfNotFound) throws IOExce
 	   || classURL == null) {
 		try {
 			Var.pushThreadBindings(
-					RT.map(CURRENT_NS, CURRENT_NS.deref(),
+					RT.mapUniqueKeys(CURRENT_NS, CURRENT_NS.deref(),
 					       WARN_ON_REFLECTION, WARN_ON_REFLECTION.deref()
 							,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()));
 			loaded = (loadClassForName(scriptbase.replace('/', '.') + LOADER_SUFFIX) != null);
@@ -436,7 +447,7 @@ static void doInit() throws ClassNotFoundException, IOException{
 	load("clojure/core");
 
 	Var.pushThreadBindings(
-			RT.map(CURRENT_NS, CURRENT_NS.deref(),
+			RT.mapUniqueKeys(CURRENT_NS, CURRENT_NS.deref(),
 			       WARN_ON_REFLECTION, WARN_ON_REFLECTION.deref()
 					,RT.UNCHECKED_MATH, RT.UNCHECKED_MATH.deref()));
 	try {
@@ -710,7 +721,7 @@ static public Object contains(Object coll, Object key){
 		int n = ((Number) key).intValue();
 		return n >= 0 && n < count(coll);
 	}
-	return F;
+	throw new IllegalArgumentException("contains? not supported on type: " + coll.getClass().getName());
 }
 
 static public Object find(Object coll, Object key){
@@ -1134,6 +1145,8 @@ static public long longCast(Object x){
 	    return ((Number) x).longValue();
 	else if (x instanceof Ratio)
 	    return longCast(((Ratio)x).bigIntegerValue());
+	else if (x instanceof Character)
+	    return longCast(((Character) x).charValue());
 	else
 	    return longCast(((Number)x).doubleValue());
 }
@@ -1449,6 +1462,14 @@ static public IPersistentMap map(Object... init){
 	return PersistentHashMap.createWithCheck(init);
 }
 
+static public IPersistentMap mapUniqueKeys(Object... init){
+	if(init == null)
+		return PersistentArrayMap.EMPTY;
+	else if(init.length <= PersistentArrayMap.HASHTABLE_THRESHOLD)
+		return new PersistentArrayMap(init);
+	return PersistentHashMap.create(init);
+}
+
 static public IPersistentSet set(Object... init){
 	return PersistentHashSet.createWithCheck(init);
 }
@@ -1670,6 +1691,12 @@ static public int getLineNumber(Reader r){
 	return 0;
 }
 
+static public int getColumnNumber(Reader r){
+	if(r instanceof LineNumberingPushbackReader)
+		return ((LineNumberingPushbackReader) r).getColumnNumber();
+	return 0;
+}
+
 static public LineNumberingPushbackReader getLineNumberingReader(Reader r){
 	if(isLineNumberingReader(r))
 		return (LineNumberingPushbackReader) r;
@@ -1680,6 +1707,10 @@ static public boolean isLineNumberingReader(Reader r){
 	return r instanceof LineNumberingPushbackReader;
 }
 
+static public boolean isReduced(Object r){
+	return r instanceof Reduced;
+}
+
 static public String resolveClassNameInContext(String className){
 	//todo - look up in context var
 	return className;
@@ -1703,12 +1734,7 @@ static public String printString(Object x){
 
 static public Object readString(String s){
 	PushbackReader r = new PushbackReader(new StringReader(s));
-	try {
-		return LispReader.read(r, true, null, false);
-	}
-	catch(Exception e) {
-		throw Util.sneakyThrow(e);
-	}
+	return LispReader.read(r, true, null, false);
 }
 
 static public void print(Object x, Writer w) throws IOException{
@@ -2044,6 +2070,17 @@ static public Class classForName(String name) {
 		}
 }
 
+static Class classForNameNonLoading(String name) {
+	try
+		{
+		return Class.forName(name, false, baseLoader());
+		}
+	catch(ClassNotFoundException e)
+		{
+		throw Util.sneakyThrow(e);
+		}
+}
+
 static public Class loadClassForName(String name) throws ClassNotFoundException{
 	try
 		{
diff --git a/src/jvm/clojure/lang/Delay.java b/src/jvm/clojure/lang/Reduced.java
similarity index 56%
copy from src/jvm/clojure/lang/Delay.java
copy to src/jvm/clojure/lang/Reduced.java
index c8227fe..8ed00b8 100644
--- a/src/jvm/clojure/lang/Delay.java
+++ b/src/jvm/clojure/lang/Reduced.java
@@ -8,35 +8,16 @@
  *   You must not remove this notice, or any other, from this software.
  **/
 
-/* rich Jun 28, 2007 */
-
 package clojure.lang;
 
-public class Delay implements IDeref, IPending{
+public class Reduced implements IDeref{
 Object val;
-IFn fn;
-
-public Delay(IFn fn){
-	this.fn = fn;
-	this.val = null;
-}
 
-static public Object force(Object x) {
-	return (x instanceof Delay) ?
-	       ((Delay) x).deref()
-	       : x;
+public Reduced(Object val){
+	this.val = val;
 }
 
-synchronized public Object deref() {
-	if(fn != null)
-		{
-		val = fn.invoke();
-		fn = null;
-		}
+public Object deref(){
 	return val;
 }
-
-synchronized public boolean isRealized(){
-	return fn == null;
-}
 }
diff --git a/src/jvm/clojure/lang/Ref.java b/src/jvm/clojure/lang/Ref.java
index cf7ffa7..18c4d71 100644
--- a/src/jvm/clojure/lang/Ref.java
+++ b/src/jvm/clojure/lang/Ref.java
@@ -47,24 +47,21 @@ public Ref setMaxHistory(int maxHistory){
 public static class TVal{
 	Object val;
 	long point;
-	long msecs;
 	TVal prior;
 	TVal next;
 
-	TVal(Object val, long point, long msecs, TVal prior){
+	TVal(Object val, long point, TVal prior){
 		this.val = val;
 		this.point = point;
-		this.msecs = msecs;
 		this.prior = prior;
 		this.next = prior.next;
 		this.prior.next = this;
 		this.next.prior = this;
 	}
 
-	TVal(Object val, long point, long msecs){
+	TVal(Object val, long point){
 		this.val = val;
 		this.point = point;
-		this.msecs = msecs;
 		this.next = this;
 		this.prior = this;
 	}
@@ -92,7 +89,7 @@ public Ref(Object initVal,IPersistentMap meta) {
     this.id = ids.getAndIncrement();
 	this.faults = new AtomicInteger();
 	this.lock = new ReentrantReadWriteLock();
-	tvals = new TVal(initVal, 0, System.currentTimeMillis());
+	tvals = new TVal(initVal, 0);
 }
 
 //the latest val
@@ -241,14 +238,7 @@ public Object call() {
 }
 
 public void run(){
-	try
-		{
-		invoke();
-		}
-	catch(Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
+	invoke();
 }
 
 public Object invoke() {
diff --git a/src/jvm/clojure/lang/Reflector.java b/src/jvm/clojure/lang/Reflector.java
index a28104b..117a56b 100644
--- a/src/jvm/clojure/lang/Reflector.java
+++ b/src/jvm/clojure/lang/Reflector.java
@@ -291,13 +291,29 @@ public static Object setInstanceField(Object target, String fieldName, Object va
 		+ " for " + target.getClass());
 }
 
+// not used as of Clojure 1.6, but left for runtime compatibility with
+// compiled bytecode from older versions
 public static Object invokeNoArgInstanceMember(Object target, String name) {
-	//favor method over field
-	List meths = getMethods(target.getClass(), 0, name, false);
-	if(meths.size() > 0)
-		return invokeMatchingMethod(name, meths, target, RT.EMPTY_ARRAY);
-	else
-		return getInstanceField(target, name);
+	return invokeNoArgInstanceMember(target, name, false);
+}
+
+public static Object invokeNoArgInstanceMember(Object target, String name, boolean requireField) {
+	Class c = target.getClass();
+
+	if(requireField) {
+		Field f = getField(c, name, false);
+		if(f != null)
+			return getInstanceField(target, name);
+		else
+			throw new IllegalArgumentException("No matching field found: " + name
+					+ " for " + target.getClass());
+	} else {
+		List meths = getMethods(c, 0, name, false);
+		if(meths.size() > 0)
+			return invokeMatchingMethod(name, meths, target, RT.EMPTY_ARRAY);
+		else
+			return getInstanceField(target, name);
+	}
 }
 
 public static Object invokeInstanceMember(Object target, String name) {
diff --git a/src/jvm/clojure/lang/Symbol.java b/src/jvm/clojure/lang/Symbol.java
index d39f53c..1efc38e 100644
--- a/src/jvm/clojure/lang/Symbol.java
+++ b/src/jvm/clojure/lang/Symbol.java
@@ -16,11 +16,11 @@ import java.io.Serializable;
 import java.io.ObjectStreamException;
 
 
-public class Symbol extends AFn implements IObj, Comparable, Named, Serializable{
+public class Symbol extends AFn implements IObj, Comparable, Named, Serializable, IHashEq{
 //these must be interned strings!
 final String ns;
 final String name;
-final int hash;
+private int _hasheq;
 final IPersistentMap _meta;
 String _str;
 
@@ -57,7 +57,7 @@ static public Symbol intern(String ns, String name){
 }
 
 static public Symbol intern(String nsname){
-	int i = nsname.lastIndexOf('/');
+	int i = nsname.indexOf('/');
 	if(i == -1 || nsname.equals("/"))
 		return new Symbol(null, nsname.intern());
 	else
@@ -67,7 +67,6 @@ static public Symbol intern(String nsname){
 private Symbol(String ns_interned, String name_interned){
 	this.name = name_interned;
 	this.ns = ns_interned;
-	this.hash = Util.hashCombine(name.hashCode(), Util.hash(ns));
 	this._meta = null;
 }
 
@@ -84,7 +83,14 @@ public boolean equals(Object o){
 }
 
 public int hashCode(){
-	return hash;
+	return Util.hashCombine(name.hashCode(), Util.hash(ns));
+}
+
+public int hasheq() {
+	if(_hasheq == 0){
+		_hasheq = Util.hashCombine(Murmur3.hashUnencodedChars(name), Util.hash(ns));
+	}
+	return _hasheq;
 }
 
 public IObj withMeta(IPersistentMap meta){
@@ -95,7 +101,6 @@ private Symbol(IPersistentMap meta, String ns, String name){
 	this.name = name;
 	this.ns = ns;
 	this._meta = meta;
-	this.hash = Util.hashCombine(name.hashCode(), Util.hash(ns));
 }
 
 public int compareTo(Object o){
diff --git a/src/jvm/clojure/lang/TransactionalHashMap.java b/src/jvm/clojure/lang/TransactionalHashMap.java
index c8e3080..f208b47 100644
--- a/src/jvm/clojure/lang/TransactionalHashMap.java
+++ b/src/jvm/clojure/lang/TransactionalHashMap.java
@@ -86,15 +86,7 @@ public V remove(Object k){
 	Ref r = bins[binFor(k)];
 	IPersistentMap map = (IPersistentMap) r.deref();
 	Object ret = map.valAt(k);
-	//checked exceptions are a bad idea, especially in an interface
-	try
-		{
-		r.set(map.without(k));
-		}
-	catch(Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
+	r.set(map.without(k));
 	return (V) ret;
 }
 
@@ -156,15 +148,7 @@ public boolean remove(Object k, Object v){
 	Entry e = map.entryAt(k);
 	if(e != null && e.getValue().equals(v))
 		{
-		//checked exceptions are a bad idea, especially in an interface
-		try
-			{
-			r.set(map.without(k));
-			}
-		catch(Exception ex)
-			{
-			throw Util.sneakyThrow(ex);
-			}
+		r.set(map.without(k));
 		return true;
 		}
 	return false;
diff --git a/src/jvm/clojure/lang/Util.java b/src/jvm/clojure/lang/Util.java
index 02b5466..cdac732 100644
--- a/src/jvm/clojure/lang/Util.java
+++ b/src/jvm/clojure/lang/Util.java
@@ -14,6 +14,7 @@ package clojure.lang;
 
 import java.lang.ref.Reference;
 import java.math.BigInteger;
+import java.util.Collection;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.lang.ref.SoftReference;
@@ -34,6 +35,50 @@ static public boolean equiv(Object k1, Object k2){
 	return false;
 }
 
+public interface EquivPred{
+    boolean equiv(Object k1, Object k2);
+}
+
+static EquivPred equivNull = new EquivPred() {
+        public boolean equiv(Object k1, Object k2) {
+            return k2 == null;
+        }
+    };
+
+static EquivPred equivEquals = new EquivPred(){
+        public boolean equiv(Object k1, Object k2) {
+            return k1.equals(k2);
+        }
+    };
+
+static EquivPred equivNumber = new EquivPred(){
+        public boolean equiv(Object k1, Object k2) {
+            if(k2 instanceof Number)
+                return Numbers.equal((Number) k1, (Number) k2);
+            return false;
+        }
+    };
+
+static EquivPred equivColl = new EquivPred(){
+        public boolean equiv(Object k1, Object k2) {
+            if(k1 instanceof IPersistentCollection || k2 instanceof IPersistentCollection)
+                return pcequiv(k1, k2);
+            return k1.equals(k2);
+        }
+    };
+
+static public EquivPred equivPred(Object k1){
+    if(k1 == null)
+        return equivNull;
+    else if (k1 instanceof Number)
+        return equivNumber;
+    else if (k1 instanceof String || k1 instanceof Symbol)
+        return equivEquals;
+    else if (k1 instanceof Collection || k1 instanceof Map)
+        return equivColl;
+    return equivEquals;
+}
+
 static public boolean equiv(long k1, long k2){
 	return k1 == k2;
 }
@@ -116,16 +161,22 @@ static public int hash(Object o){
 	return o.hashCode();
 }
 
-static public int hasheq(Object o){
+public static int hasheq(Object o){
 	if(o == null)
 		return 0;
+	if(o instanceof IHashEq)
+		return dohasheq((IHashEq) o);	
 	if(o instanceof Number)
 		return Numbers.hasheq((Number)o);
-	else if(o instanceof IHashEq)
-		return ((IHashEq)o).hasheq();
+	if(o instanceof String)
+		return Murmur3.hashInt(o.hashCode());
 	return o.hashCode();
 }
 
+private static int dohasheq(IHashEq o) {
+	return o.hasheq();
+}
+
 static public int hashCombine(int seed, int hash){
 	//a la boost
 	seed ^= hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
diff --git a/src/jvm/clojure/lang/Var.java b/src/jvm/clojure/lang/Var.java
index 1630889..9e79aac 100644
--- a/src/jvm/clojure/lang/Var.java
+++ b/src/jvm/clojure/lang/Var.java
@@ -45,17 +45,13 @@ static public class Unbound extends AFn{
 }
 
 static class Frame{
+	final static Frame TOP = new Frame(PersistentHashMap.EMPTY, null);
 	//Var->TBox
 	Associative bindings;
 	//Var->val
 //	Associative frameBindings;
 	Frame prev;
 
-
-	public Frame(){
-		this(PersistentHashMap.EMPTY, null);
-	}
-
 	public Frame(Associative bindings, Frame prev){
 //		this.frameBindings = frameBindings;
 		this.bindings = bindings;
@@ -63,9 +59,7 @@ static class Frame{
 	}
 
     	protected Object clone() {
-		Frame f = new Frame();
-		f.bindings = this.bindings;
-		return f;
+		return new Frame(this.bindings, null);
     	}
 
 }
@@ -73,7 +67,7 @@ static class Frame{
 static final ThreadLocal<Frame> dvals = new ThreadLocal<Frame>(){
 
 	protected Frame initialValue(){
-		return new Frame();
+		return Frame.TOP;
 	}
 };
 
@@ -96,17 +90,11 @@ public final Namespace ns;
 //IPersistentMap _meta;
 
 public static Object getThreadBindingFrame(){
-	Frame f = dvals.get();
-	if(f != null)
-		return f;
-	return new Frame();
+	return dvals.get();
 }
 
 public static Object cloneThreadBindingFrame(){
-	Frame f = dvals.get();
-	if(f != null)
-		return f.clone();
-	return new Frame();
+	return dvals.get().clone();
 }
 
 public static void resetThreadBindingFrame(Object frame){
@@ -248,14 +236,7 @@ public void setMeta(IPersistentMap m) {
 }
 
 public void setMacro() {
-    try
-        {
-        alterMeta(assoc, RT.list(macroKey, RT.T));
-        }
-    catch (Exception e)
-        {
-        throw Util.sneakyThrow(e);
-        }
+    alterMeta(assoc, RT.list(macroKey, RT.T));
 }
 
 public boolean isMacro(){
@@ -279,14 +260,7 @@ public Object getTag(){
 }
 
 public void setTag(Symbol tag) {
-    try
-        {
-        alterMeta(assoc, RT.list(RT.TAG_KEY, tag));
-        }
-    catch (Exception e)
-        {
-        throw Util.sneakyThrow(e);
-        }
+    alterMeta(assoc, RT.list(RT.TAG_KEY, tag));
 }
 
 final public boolean hasRoot(){
@@ -299,14 +273,7 @@ synchronized public void bindRoot(Object root){
 	Object oldroot = this.root;
 	this.root = root;
 	++rev;
-    try
-        {
         alterMeta(dissoc, RT.list(macroKey));
-        }
-    catch (Exception e)
-        {
-        throw Util.sneakyThrow(e);
-        }
     notifyWatches(oldroot,this.root);
 }
 
@@ -359,10 +326,14 @@ public static void pushThreadBindings(Associative bindings){
 }
 
 public static void popThreadBindings(){
-	Frame f = dvals.get();
-	if(f.prev == null)
-		throw new IllegalStateException("Pop without matching push");
-	dvals.set(f.prev);
+    Frame f = dvals.get().prev;
+    if (f == null) {
+        throw new IllegalStateException("Pop without matching push");
+    } else if (f == Frame.TOP) {
+        dvals.remove();
+    } else {
+        dvals.set(f);
+    }
 }
 
 public static Associative getThreadBindings(){
@@ -397,14 +368,7 @@ public Object call() {
 }
 
 public void run(){
-	try
-		{
-		invoke();
-		}
-	catch(Exception e)
-		{
-		throw Util.sneakyThrow(e);
-		}
+        invoke();
 }
 
 public Object invoke() {
@@ -412,111 +376,296 @@ public Object invoke() {
 }
 
 public Object invoke(Object arg1) {
-	return fn().invoke(arg1);
+    return fn().invoke(Util.ret1(arg1,arg1=null));
 }
 
 public Object invoke(Object arg1, Object arg2) {
-	return fn().invoke(arg1, arg2);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3) {
-	return fn().invoke(arg1, arg2, arg3);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) {
-	return fn().invoke(arg1, arg2, arg3, arg4);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7)
 		{
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13)
 		{
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14)
 		{
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null),
+                       Util.ret1(arg15,arg15=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
-	                   arg16);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null),
+                       Util.ret1(arg15,arg15=null),
+                       Util.ret1(arg16,arg16=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
-	                   arg16, arg17);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null),
+                       Util.ret1(arg15,arg15=null),
+                       Util.ret1(arg16,arg16=null),
+                       Util.ret1(arg17,arg17=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17, Object arg18) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
-	                   arg16, arg17, arg18);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null),
+                       Util.ret1(arg15,arg15=null),
+                       Util.ret1(arg16,arg16=null),
+                       Util.ret1(arg17,arg17=null),
+                       Util.ret1(arg18,arg18=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) {
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
-	                   arg16, arg17, arg18, arg19);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null),
+                       Util.ret1(arg15,arg15=null),
+                       Util.ret1(arg16,arg16=null),
+                       Util.ret1(arg17,arg17=null),
+                       Util.ret1(arg18,arg18=null),
+                       Util.ret1(arg19,arg19=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
                      Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14,
                      Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20)
 		{
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
-	                   arg16, arg17, arg18, arg19, arg20);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null),
+                       Util.ret1(arg15,arg15=null),
+                       Util.ret1(arg16,arg16=null),
+                       Util.ret1(arg17,arg17=null),
+                       Util.ret1(arg18,arg18=null),
+                       Util.ret1(arg19,arg19=null),
+                       Util.ret1(arg20,arg20=null));
 }
 
 public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7,
@@ -524,8 +673,27 @@ public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object
                      Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20,
                      Object... args)
 		{
-	return fn().invoke(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10, arg11, arg12, arg13, arg14, arg15,
-	                   arg16, arg17, arg18, arg19, arg20, args);
+    return fn().invoke(Util.ret1(arg1,arg1=null),
+                       Util.ret1(arg2,arg2=null),
+                       Util.ret1(arg3,arg3=null),
+                       Util.ret1(arg4,arg4=null),
+                       Util.ret1(arg5,arg5=null),
+                       Util.ret1(arg6,arg6=null),
+                       Util.ret1(arg7,arg7=null),
+                       Util.ret1(arg8,arg8=null),
+                       Util.ret1(arg9,arg9=null),
+                       Util.ret1(arg10,arg10=null),
+                       Util.ret1(arg11,arg11=null),
+                       Util.ret1(arg12,arg12=null),
+                       Util.ret1(arg13,arg13=null),
+                       Util.ret1(arg14,arg14=null),
+                       Util.ret1(arg15,arg15=null),
+                       Util.ret1(arg16,arg16=null),
+                       Util.ret1(arg17,arg17=null),
+                       Util.ret1(arg18,arg18=null),
+                       Util.ret1(arg19,arg19=null),
+                       Util.ret1(arg20,arg20=null),
+                       (Object[])Util.ret1(args, args=null));
 }
 
 public Object applyTo(ISeq arglist) {
@@ -541,14 +709,7 @@ static IFn assoc = new AFn(){
 static IFn dissoc = new AFn() {
     @Override
     public Object invoke(Object c, Object k)  {
-	    try
-		    {
-		    return RT.dissoc(c, k);
-		    }
-	    catch(Exception e)
-		    {
-            throw Util.sneakyThrow(e);
-		    }
+            return RT.dissoc(c, k);
     }
 };
 }
diff --git a/src/jvm/clojure/lang/package.html b/src/jvm/clojure/lang/package.html
new file mode 100644
index 0000000..c5df388
--- /dev/null
+++ b/src/jvm/clojure/lang/package.html
@@ -0,0 +1,21 @@
+<html>
+
+<!--
+Copyright (c) Rich Hickey and Contributors. All rights reserved.
+The use and distribution terms for this software are covered by the
+Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+which can be found in the file epl-v10.html at the root of this distribution.
+By using this software in any fashion, you are agreeing to be bound by
+the terms of this license.
+You must not remove this notice, or any other, from this software.
+-->
+
+<body>Clojure language implementation.
+
+<p>The clojure.lang package holds the implementation for Clojure.
+The only class considered part of the public API is
+{@link clojure.lang.IFn}. All other classes should be considered
+implementation details.</p>
+
+</body>
+</html>
diff --git a/src/script/run_tests.clj b/src/script/run_tests.clj
old mode 100755
new mode 100644
index 1aa8b99..a038384
--- a/src/script/run_tests.clj
+++ b/src/script/run_tests.clj
@@ -1,61 +1,4 @@
-(ns clojure.test-clojure (:require clojure.test))
-
-(def test-namespaces '[
-clojure.test-clojure.agents
-clojure.test-clojure.annotations
-clojure.test-clojure.atoms
-clojure.test-clojure.clojure-set
-clojure.test-clojure.clojure-walk                       
-clojure.test-clojure.clojure-xml
-clojure.test-clojure.clojure-zip
-clojure.test-clojure.compilation
-clojure.test-clojure.control
-clojure.test-clojure.data
-clojure.test-clojure.data-structures
-clojure.test-clojure.def
-clojure.test-clojure.errors
-clojure.test-clojure.evaluation
-clojure.test-clojure.for
-clojure.test-clojure.genclass.examples
-clojure.test-clojure.genclass
-clojure.test-clojure.java.io
-clojure.test-clojure.java.javadoc
-clojure.test-clojure.java.shell
-clojure.test-clojure.java-interop
-clojure.test-clojure.keywords
-clojure.test-clojure.load
-clojure.test-clojure.logic
-clojure.test-clojure.macros
-clojure.test-clojure.main
-clojure.test-clojure.metadata
-clojure.test-clojure.multimethods
-clojure.test-clojure.ns-libs
-clojure.test-clojure.numbers
-clojure.test-clojure.other-functions
-clojure.test-clojure.parallel
-clojure.test-clojure.pprint
-clojure.test-clojure.predicates
-clojure.test-clojure.printer
-clojure.test-clojure.protocols
-clojure.test-clojure.protocols.hash-collisions
-clojure.test-clojure.reader
-clojure.test-clojure.reflect
-clojure.test-clojure.refs
-clojure.test-clojure.repl
-clojure.test-clojure.rt
-clojure.test-clojure.sequences
-clojure.test-clojure.serialization
-clojure.test-clojure.special
-clojure.test-clojure.string
-clojure.test-clojure.test
-clojure.test-clojure.test-fixtures
-clojure.test-clojure.transients
-clojure.test-clojure.try-catch
-clojure.test-clojure.vars
-clojure.test-clojure.vectors
-])
-
-(apply require test-namespaces)
-
-(let [results (apply clojure.test/run-tests test-namespaces)]
-  (System/exit (+ (:error results) (:fail results))))
+(System/setProperty "clojure.test.generative.msec" "60000")
+(System/setProperty "java.awt.headless" "true")
+(require '[clojure.test.generative.runner :as runner])
+(runner/-main "test")
diff --git a/test/clojure/test_clojure/agents.clj b/test/clojure/test_clojure/agents.clj
index 49c209e..47ae938 100644
--- a/test/clojure/test_clojure/agents.clj
+++ b/test/clojure/test_clojure/agents.clj
@@ -49,7 +49,10 @@
     (is (= agt (first @err)))
   (is (true? (instance? ArithmeticException (second @err))))))
 
-(deftest fail-handler
+
+;; TODO: make these tests deterministic (i.e. not sleep and hope)
+
+#_(deftest fail-handler
   (let [err (atom nil)
         agt (agent 0 :error-mode :fail :error-handler #(reset! err %&))]
     (send agt /)
@@ -79,7 +82,7 @@
     (send failing-agent (fn [_] (throw (RuntimeException.))))
     (is (.await latch 10 TimeUnit/SECONDS))))
 
-(deftest restart-no-clear
+#_(deftest restart-no-clear
   (let [p (promise)
         agt (agent 1 :error-mode :fail)]
     (send agt (fn [v] @p))
@@ -95,7 +98,7 @@
     (is (= 12 @agt))
     (is (nil? (agent-error agt)))))
 
-(deftest restart-clear
+#_(deftest restart-clear
   (let [p (promise)
         agt (agent 1 :error-mode :fail)]
     (send agt (fn [v] @p))
@@ -115,7 +118,7 @@
     (is (= 11 @agt))
     (is (nil? (agent-error agt)))))
 
-(deftest invalid-restart
+#_(deftest invalid-restart
   (let [p (promise)
         agt (agent 2 :error-mode :fail :validator even?)]
     (is (thrown? RuntimeException (restart-agent agt 4)))
@@ -151,6 +154,28 @@
       (.join))
     (is (= @a :thread-binding))))
 
+;; check for a race condition that was causing seque to leak threads from the
+;; send-off pool. Specifically, if we consume all items from the seque, and
+;; the LBQ continues to grow, it means there was an agent action blocking on
+;; the .put, which would block indefinitely outside of this test.
+(deftest seque-threads
+  (let [queue-size 5
+        slow-seq (for [x (take (* 2 queue-size) (iterate inc 0))]
+                   (do (Thread/sleep 25)
+                       x))
+        small-lbq (java.util.concurrent.LinkedBlockingQueue. queue-size)
+        worker (seque small-lbq slow-seq)]
+    (doall worker)
+    (is (= worker slow-seq))
+    (Thread/sleep 250) ;; make sure agents have time to run or get blocked
+    (let [queue-backlog (.size small-lbq)]
+      (is (<= 0 queue-backlog queue-size))
+      (when-not (zero? queue-backlog)
+        (.take small-lbq)
+        (Thread/sleep 250) ;; see if agent was blocking, indicating a thread leak
+        (is (= (.size small-lbq)
+               (dec queue-backlog)))))))
+
 ; http://clojure.org/agents
 
 ; agent
diff --git a/test/clojure/test_clojure/annotations.clj b/test/clojure/test_clojure/annotations.clj
index e015b2d..e1a1e05 100644
--- a/test/clojure/test_clojure/annotations.clj
+++ b/test/clojure/test_clojure/annotations.clj
@@ -12,7 +12,6 @@
   (:use clojure.test))
 
 (case (System/getProperty "java.specification.version")
-      "1.5" (load "annotations/java_5")
       "1.6" (load "annotations/java_6")
       nil)
 
diff --git a/test/clojure/test_clojure/annotations/java_5.clj b/test/clojure/test_clojure/annotations/java_5.clj
index 28daae1..060ea72 100644
--- a/test/clojure/test_clojure/annotations/java_5.clj
+++ b/test/clojure/test_clojure/annotations/java_5.clj
@@ -52,3 +52,17 @@
        expected-annotations
        (into #{} (map annotation->map (.getAnnotations (.getMethod Bar "foo" nil)))))))
 
+(gen-class :name foo.Bar
+           :extends clojure.lang.Box
+           :constructors {^{Deprecated true} [Object] [Object]}
+           :init init
+           :prefix "foo")
+
+(defn foo-init [obj]
+  [[obj] nil])
+
+(deftest test-annotations-on-constructor
+  (is (some #(instance? Deprecated %)
+            (for [ctor (.getConstructors (Class/forName "foo.Bar"))
+                  annotation (.getAnnotations ctor)]
+              annotation))))
diff --git a/test/clojure/test_clojure/annotations/java_6.clj b/test/clojure/test_clojure/annotations/java_6.clj
index 49d2404..f8e06ac 100644
--- a/test/clojure/test_clojure/annotations/java_6.clj
+++ b/test/clojure/test_clojure/annotations/java_6.clj
@@ -71,3 +71,17 @@
        expected-annotations
        (into #{} (map annotation->map (.getAnnotations (.getMethod Bar "foo" nil)))))))
 
+(gen-class :name foo.Bar
+           :extends clojure.lang.Box
+           :constructors {^{Deprecated true} [Object] [Object]}
+           :init init
+           :prefix "foo")
+
+(defn foo-init [obj]
+  [[obj] nil])
+
+(deftest test-annotations-on-constructor
+  (is (some #(instance? Deprecated %)
+            (for [ctor (.getConstructors (Class/forName "foo.Bar"))
+                  annotation (.getAnnotations ctor)]
+              annotation))))
diff --git a/test/clojure/test_clojure/api.clj b/test/clojure/test_clojure/api.clj
new file mode 100644
index 0000000..9718e55
--- /dev/null
+++ b/test/clojure/test_clojure/api.clj
@@ -0,0 +1,54 @@
+;   Copyright (c) Rich Hickey. All rights reserved.
+;   The use and distribution terms for this software are covered by the
+;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;   which can be found in the file epl-v10.html at the root of this distribution.
+;   By using this software in any fashion, you are agreeing to be bound by
+;   the terms of this license.
+;   You must not remove this notice, or any other, from this software.
+
+(ns clojure.test-clojure.api
+  (:require [clojure.test.generative :refer (defspec)]
+            [clojure.test-clojure.generators :as cgen])
+  (:import clojure.lang.IFn
+           clojure.java.api.Clojure
+           clojure.lang.Var))
+
+(set! *warn-on-reflection* true)
+
+(defn roundtrip
+  "Print an object and read it back with Clojure/read"
+  [o]
+  (binding [*print-length* nil
+            *print-dup* nil
+            *print-level* nil]
+    (Clojure/read (pr-str o))))
+
+(defn api-var-str
+  [^Var v]
+  (Clojure/var (str (.name (.ns v)))
+               (str (.sym v))))
+
+(defn api-var
+  [^Var v]
+  (Clojure/var (.name (.ns v))
+               (.sym v)))
+
+(defspec api-can-read
+  roundtrip
+  [^{:tag cgen/ednable} o]
+  (when-not (= o %)
+    (throw (ex-info "Value cannot roundtrip with Clojure/read" {:printed o :read %}))))
+
+(defspec api-can-find-var
+  api-var
+  [^{:tag cgen/var} v]
+  (when-not (= v %)
+    (throw (ex-info "Var cannot roundtrip through Clojure/var" {:from v :to %}))))
+
+(defspec api-can-find-var-str
+  api-var-str
+  [^{:tag cgen/var} v]
+  (when-not (= v %)
+    (throw (ex-info "Var cannot roundtrip strings through Clojure/var" {:from v :to %}))))
+
+
diff --git a/test/clojure/test_clojure/clojure_set.clj b/test/clojure/test_clojure/clojure_set.clj
index 8d511af..61a36b9 100644
--- a/test/clojure/test_clojure/clojure_set.clj
+++ b/test/clojure/test_clojure/clojure_set.clj
@@ -162,8 +162,9 @@
 
 (deftest test-rename-keys
   (are [x y] (= x y)
-    (set/rename-keys {:a "one" :b "two"} {:a :z}) {:z "one" :b "two"}
-    ))
+       (set/rename-keys {:a "one" :b "two"} {:a :z}) {:z "one" :b "two"}
+       (set/rename-keys {:a "one" :b "two"} {:a :z :c :y}) {:z "one" :b "two"}
+       (set/rename-keys {:a "one" :b "two" :c "three"} {:a :b :b :a}) {:a "two" :b "one" :c "three"}))
 
 (deftest test-index
   (are [x y] (= x y)
diff --git a/test/clojure/test_clojure/clojure_walk.clj b/test/clojure/test_clojure/clojure_walk.clj
index 7095522..bda124a 100644
--- a/test/clojure/test_clojure/clojure_walk.clj
+++ b/test/clojure/test_clojure/clojure_walk.clj
@@ -33,6 +33,8 @@
           4 5 [5] (list 4 [5])
           [1 2 {:a 3} (list 4 [5])]])))
 
+(defrecord Foo [a b c])
+
 (deftest walk
          "Checks that walk returns the correct result and type of collection"
          (let [colls ['(1 2 3)
@@ -40,7 +42,9 @@
                       #{1 2 3}
                       (sorted-set-by > 1 2 3)
                       {:a 1, :b 2, :c 3}
-                      (sorted-map-by > 1 10, 2 20, 3 30)]]
+                      (sorted-map-by > 1 10, 2 20, 3 30)
+                      (->Foo 1 2 3)
+                      (map->Foo {:a 1 :b 2 :c 3 :extra 4})]]
            (doseq [c colls]
              (let [walked (w/walk identity identity c)]
                (is (= c walked))
diff --git a/test/clojure/test_clojure/compilation.clj b/test/clojure/test_clojure/compilation.clj
index f8b27de..26ccedd 100644
--- a/test/clojure/test_clojure/compilation.clj
+++ b/test/clojure/test_clojure/compilation.clj
@@ -10,6 +10,9 @@
 
 
 (ns clojure.test-clojure.compilation
+  (:import (clojure.lang Compiler Compiler$CompilerException))
+  (:require [clojure.test.generative :refer (defspec)]
+            [clojure.data.generators :as gen])
   (:use clojure.test
         [clojure.test-helper :only (should-not-reflect should-print-err-message)]))
 
@@ -34,6 +37,9 @@
         (integer? (:line m)) true
         (> (:line m) 0) true
 
+        (integer? (:column m)) true
+        (> (:column m) 0) true
+
         (:macro m) true
         (:name m) 'when )))
 
@@ -54,26 +60,55 @@
 
 (deftest test-no-recur-across-try
   (testing "don't recur to function from inside try"
-    (is (thrown? Exception (eval '(fn [x] (try (recur 1)))))))
+    (is (thrown? Compiler$CompilerException
+                 (eval '(fn [x] (try (recur 1)))))))
   (testing "don't recur to loop from inside try"
-    (is (thrown? Exception (eval '(loop [x] (try (recur 1)))))))
+    (is (thrown? Compiler$CompilerException
+                 (eval '(loop [x 5]
+                          (try (recur 1)))))))
+  (testing "don't recur to loop from inside of catch inside of try"
+    (is (thrown? Compiler$CompilerException
+                 (eval '(loop [x 5]
+                          (try
+                            (catch Exception e
+                              (recur 1))))))))
+  (testing "don't recur to loop from inside of finally inside of try"
+    (is (thrown? Compiler$CompilerException
+                 (eval '(loop [x 5]
+                          (try
+                            (finally
+                              (recur 1))))))))
   (testing "don't get confused about what the recur is targeting"
-    (is (thrown? Exception (eval '(loop [x] (try (fn [x]) (recur 1)))))))
-  (testing "don't allow recur accross binding"
-    (is (thrown? Exception (eval '(fn [x] (binding [+ *] (recur 1)))))))
+    (is (thrown? Compiler$CompilerException
+                 (eval '(loop [x 5]
+                          (try (fn [x]) (recur 1)))))))
+  (testing "don't allow recur across binding"
+    (is (thrown? Compiler$CompilerException
+                 (eval '(fn [x] (binding [+ *] (recur 1)))))))
   (testing "allow loop/recur inside try"
-    (is (try
-          (eval '(try (loop [x 3] (if (zero? x) x (recur (dec x))))))
-          (catch Exception _))))
+    (is (= 0 (eval '(try (loop [x 3]
+                           (if (zero? x) x (recur (dec x)))))))))
+  (testing "allow loop/recur fully inside catch"
+    (is (= 3 (eval '(try
+                      (throw (Exception.))
+                      (catch Exception e
+                        (loop [x 0]
+                          (if (< x 3) (recur (inc x)) x))))))))
+  (testing "allow loop/recur fully inside finally"
+    (is (= "012" (eval '(with-out-str
+                          (try
+                            :return-val-discarded-because-of-with-out-str
+                            (finally (loop [x 0]
+                                       (when (< x 3)
+                                         (print x)
+                                         (recur (inc x)))))))))))
   (testing "allow fn/recur inside try"
-    (is (try
-          (eval '(try
-                   ((fn [x]
-                      (if (zero? x)
-                        x
-                        (recur (dec x))))
-                    3)))
-          (catch Exception _)))))
+    (is (= 0 (eval '(try
+                      ((fn [x]
+                         (if (zero? x)
+                           x
+                           (recur (dec x))))
+                       3)))))))
 
 ;; disabled until build box can call java from mvn
 #_(deftest test-numeric-dispatch
@@ -139,3 +174,71 @@
   
   (should-print-err-message #"(?s).*k is not matching primitive.*"
     #(loop [k (clojure.test-clojure.compilation/primfn)] (recur :foo))))
+
+#_(deftest CLJ-1154-use-out-after-compile
+  ;; This test creates a dummy file to compile, sets up a dummy
+  ;; compiled output directory, and a dummy output stream, and
+  ;; verifies the stream is still usable after compiling.
+  (spit "test/dummy.clj" "(ns dummy)")
+  (try
+    (let [compile-path (System/getProperty "clojure.compile.path")
+          tmp (java.io.File. "tmp")
+          new-out (java.io.OutputStreamWriter. (java.io.ByteArrayOutputStream.))]
+      (binding [clojure.core/*out* new-out]
+        (try
+          (.mkdir tmp)
+          (System/setProperty "clojure.compile.path" "tmp")
+          (clojure.lang.Compile/main (into-array ["dummy"]))
+          (println "this should still work without throwing an exception" )
+          (finally
+            (if compile-path
+              (System/setProperty "clojure.compile.path" compile-path)
+              (System/clearProperty "clojure.compile.path"))
+            (doseq [f (.listFiles tmp)]
+              (.delete f))
+            (.delete tmp)))))
+    (finally
+      (doseq [f (.listFiles (java.io.File. "test"))
+              :when (re-find #"dummy.clj" (str f))]
+        (.delete f)))))
+
+(deftest CLJ-1184-do-in-non-list-test
+  (testing "do in a vector throws an exception"
+    (is (thrown? Compiler$CompilerException
+                 (eval '[do 1 2 3]))))
+  (testing "do in a set throws an exception"
+    (is (thrown? Compiler$CompilerException
+                 (eval '#{do}))))
+
+  ;; compile uses a separate code path so we have to call it directly
+  ;; to test it
+  (letfn [(compile [s]
+            (spit "test/clojure/bad_def_test.clj" (str "(ns clojure.bad-def-test)\n" s))
+            (try
+             (binding [*compile-path* "test"]
+               (clojure.core/compile 'clojure.bad-def-test))
+             (finally
+               (doseq [f (.listFiles (java.io.File. "test/clojure"))
+                       :when (re-find #"bad_def_test" (str f))]
+                 (.delete f)))))]
+    (testing "do in a vector throws an exception in compilation"
+      (is (thrown? Compiler$CompilerException (compile "[do 1 2 3]"))))
+    (testing "do in a set throws an exception in compilation"
+      (is (thrown? Compiler$CompilerException (compile "#{do}"))))))
+
+(defn gen-name []
+  ;; Not all names can be correctly demunged. Skip names that contain
+  ;; a munge word as they will not properly demunge.
+  (let [munge-words (remove clojure.string/blank?
+                            (conj (map #(clojure.string/replace % "_" "")
+                                       (vals Compiler/CHAR_MAP)) "_"))]
+    (first (filter (fn [n] (not-any? #(>= (.indexOf n %) 0) munge-words))
+                   (repeatedly #(name (gen/symbol (constantly 10))))))))
+
+(defn munge-roundtrip [n]
+  (Compiler/demunge (Compiler/munge n)))
+
+(defspec test-munge-roundtrip
+  munge-roundtrip
+  [^{:tag clojure.test-clojure.compilation/gen-name} n]
+  (assert (= n %)))
diff --git a/test/clojure/test_clojure/control.clj b/test/clojure/test_clojure/control.clj
index 3a7d4c3..141cdf6 100644
--- a/test/clojure/test_clojure/control.clj
+++ b/test/clojure/test_clojure/control.clj
@@ -152,6 +152,21 @@
              (exception))
        ))
 
+(deftest test-if-some
+  (are [x y] (= x y)
+       1 (if-some [a 1] a)
+       false (if-some [a false] a)
+       nil (if-some [a nil] (exception))
+       3 (if-some [[a b] [1 2]] (+ a b))
+       1 (if-some [[a b] nil] b 1)
+       1 (if-some [a nil] (exception) 1)))
+
+(deftest test-when-some
+  (are [x y] (= x y)
+       1 (when-some [a 1] a)
+       2 (when-some [[a b] [1 2]] b)
+       false (when-some [a false] a)
+       nil (when-some [a nil] (exception))))
 
 (deftest test-cond
   (are [x y] (= x y)
@@ -367,7 +382,8 @@
       (is (= :diff (case x -1 :oops :diff)))
       (is (= :same (case y -1 :same :oops)))))
   (testing "test correct behavior on hash collision"
-    (is (== (hash 1) (hash 9223372039002259457N)))
+    ;; case uses Java .hashCode to put values into hash buckets.
+    (is (== (.hashCode 1) (.hashCode 9223372039002259457N)))
     (are [result input] (= result (case input
                                     1 :long
                                     9223372039002259457N :big
diff --git a/test/clojure/test_clojure/data.clj b/test/clojure/test_clojure/data.clj
index 9bab766..5a241e0 100644
--- a/test/clojure/test_clojure/data.clj
+++ b/test/clojure/test_clojure/data.clj
@@ -27,5 +27,6 @@
        [#{1} #{3} #{2}] (HashSet. [1 2]) (HashSet. [2 3])
        [nil nil [1 2]] [1 2] (into-array [1 2])
        [nil nil [1 2]] (into-array [1 2]) [1 2]
-       [{:a {:c [1]}} {:a {:c [0]}} {:a {:c [nil 2] :b 1}}] {:a {:b 1 :c [1 2]}} {:a {:b 1 :c [0 2]}}))
+       [{:a {:c [1]}} {:a {:c [0]}} {:a {:c [nil 2] :b 1}}] {:a {:b 1 :c [1 2]}} {:a {:b 1 :c [0 2]}}
+       [{:a nil} {:a false} {:b nil :c false}] {:a nil :b nil :c false} {:a false :b nil :c false}))
 
diff --git a/test/clojure/test_clojure/data_structures.clj b/test/clojure/test_clojure/data_structures.clj
index e3ecaa9..8515d86 100644
--- a/test/clojure/test_clojure/data_structures.clj
+++ b/test/clojure/test_clojure/data_structures.clj
@@ -10,7 +10,11 @@
 
 
 (ns clojure.test-clojure.data-structures
-  (:use clojure.test))
+  (:use clojure.test
+        [clojure.test.generative :exclude (is)])
+  (:require [clojure.test-clojure.generators :as cgen]
+            [clojure.data.generators :as gen]
+            [clojure.string :as string]))
 
 
 ;; *** Helper functions ***
@@ -19,6 +23,58 @@
   (seq (reduce disj (set s1) (set s2))))
 
 
+;; *** Generative ***
+(defspec subcollection-counts-are-consistent
+  identity
+  [^{:tag cgen/ednable-collection} coll]
+  (let [n (count coll)]
+    (dotimes [i n]
+      (is (= n
+             (+ i (count (nthnext coll i)))
+             (+ i (count (drop i coll))))))))
+
+(defn- transient? [x]
+  (instance? clojure.lang.ITransientCollection x))
+
+(defn gen-transient-action []
+  (gen/rand-nth [[#(conj! %1 %2) #(conj %1 %2) (gen/uniform -100 100)]
+                 [#(disj! %1 %2) #(disj %1 %2) (gen/uniform -100 100)]
+                 [persistent! identity]
+                 [identity transient]]))
+
+(defn gen-transient-actions []
+  (gen/reps #(gen/uniform 0 100) gen-transient-action))
+
+(defn assert-same-collection [a b]
+  (assert (= (count a) (count b) (.size a) (.size b)))
+  (assert (= a b))
+  (assert (= b a))
+  (assert (.equals ^Object a b))
+  (assert (.equals ^Object b a))
+  (assert (= (hash a) (hash b)))
+  (assert (= (.hashCode ^Object a) (.hashCode ^Object b)))
+  (assert (= a
+             (into (empty a) a)
+             (into (empty b) b)
+             (into (empty a) b)
+             (into (empty b) a))))
+
+(defn apply-actions [coll actions]
+  (reduce (fn [c [tfunc pfunc & args]]
+            (apply (if (transient? c) tfunc pfunc) c args))
+          coll
+          actions))
+
+(defn to-persistent [c]
+  (if (transient? c) (persistent! c) c))
+
+(defspec conj-persistent-transient
+  identity
+  [^{:tag clojure.test-clojure.data-structures/gen-transient-actions} actions]
+  (assert-same-collection
+   (to-persistent (apply-actions #{} actions))
+   (to-persistent (apply-actions #{} actions))))
+
 ;; *** General ***
 
 (defstruct equality-struct :a :b)
@@ -455,15 +511,10 @@
       (contains? (into-array [1 2 3]) 3) false
       (contains? (into-array [1 2 3]) -1) false)
 
-  ; 'contains?' operates constant or logarithmic time,
-  ; it WILL NOT perform a linear search for a value.
-  (are [x]  (= x false)
-      (contains? '(1 2 3) 0)
-      (contains? '(1 2 3) 1)
-      (contains? '(1 2 3) 3)
-      (contains? '(1 2 3) 10)
-      (contains? '(1 2 3) nil)
-      (contains? '(1 2 3) ()) ))
+  ; 'contains?' will not operate on non-associative things
+  (are [x]  (is (thrown? Exception (contains? x 1)))
+       '(1 2 3)
+       3))
 
 
 (deftest test-keys
@@ -579,6 +630,19 @@
          (get-in m [] 0) m
          (get-in m nil 0) m)))
 
+(deftest test-nested-map-destructuring
+  (let [sample-map {:a 1 :b {:a 2}}
+        {ao1 :a {ai1 :a} :b} sample-map
+        {ao2 :a {ai2 :a :as m1} :b :as m2} sample-map
+        {ao3 :a {ai3 :a :as m} :b :as m} sample-map
+        {{ai4 :a :as m} :b ao4 :a :as m} sample-map]
+    (are [i o] (and (= i 2)
+                    (= o 1))
+         ai1 ao1
+         ai2 ao2
+         ai3 ao3
+         ai4 ao4)))
+
 ;; *** Sets ***
 
 (deftest test-hash-set
@@ -628,7 +692,8 @@
       (hash-set nil 2) #{nil 2}
       (hash-set #{}) #{#{}}
       (hash-set 1 #{}) #{1 #{}}
-      (hash-set #{} 2) #{#{} 2} ))
+      (hash-set #{} 2) #{#{} 2}
+      (hash-set (Integer. -1)) (hash-set (Long. -1))))
 
 
 (deftest test-sorted-set
@@ -846,6 +911,9 @@
     (are [x y] (= x y)
       EMPTY EMPTY
       (into EMPTY (range 50)) (into EMPTY (range 50))
+      (conj EMPTY (Long. -1)) (conj EMPTY (Integer. -1))
+      (hash (conj EMPTY (Long. -1))) (hash (conj EMPTY (Integer. -1)))
+      (hash [1 2 3]) (hash (conj EMPTY 1 2 3))
       (range 5) (into EMPTY (range 5))
       (range 1 6) (-> EMPTY
                     (into (range 6))
@@ -860,3 +928,184 @@
                     (into (range 7))
                     pop))))
 
+
+(deftest test-duplicates
+  (let [equal-sets-incl-meta (fn [s1 s2]
+                               (and (= s1 s2)
+                                    (let [ss1 (sort s1)
+                                          ss2 (sort s2)]
+                                      (every? identity
+                                              (map #(and (= %1 %2)
+                                                         (= (meta %1) (meta %2)))
+                                                   ss1 ss2)))))
+        all-equal-sets-incl-meta (fn [& ss]
+                                   (every? (fn [[s1 s2]]
+                                             (equal-sets-incl-meta s1 s2))
+                                           (partition 2 1 ss)))
+        equal-maps-incl-meta (fn [m1 m2]
+                               (and (= m1 m2)
+                                    (equal-sets-incl-meta (set (keys m1))
+                                                          (set (keys m2)))
+                                    (every? #(= (meta (m1 %)) (meta (m2 %)))
+                                            (keys m1))))
+        all-equal-maps-incl-meta (fn [& ms]
+                                   (every? (fn [[m1 m2]]
+                                             (equal-maps-incl-meta m1 m2))
+                                           (partition 2 1 ms)))
+        cmp-first #(> (first %1) (first %2))
+        x1 (with-meta [1] {:me "x"})
+        y2 (with-meta [2] {:me "y"})
+        z3a (with-meta [3] {:me "z3a"})
+        z3b (with-meta [3] {:me "z3b"})
+        v4a (with-meta [4] {:me "v4a"})
+        v4b (with-meta [4] {:me "v4b"})
+        v4c (with-meta [4] {:me "v4c"})
+        w5a (with-meta [5] {:me "w5a"})
+        w5b (with-meta [5] {:me "w5b"})
+        w5c (with-meta [5] {:me "w5c"})]
+
+    ;; Sets
+    (is (thrown? IllegalArgumentException
+                 (read-string "#{1 2 3 4 1 5}")))
+    ;; If there are duplicate items when doing (conj #{} x1 x2 ...),
+    ;; the behavior is that the metadata of the first item is kept.
+    (are [s x] (all-equal-sets-incl-meta s
+                                         (apply conj #{} x)
+                                         (set x)
+                                         (apply hash-set x)
+                                         (apply sorted-set x)
+                                         (apply sorted-set-by cmp-first x))
+      #{x1 y2} [x1 y2]
+      #{x1 z3a} [x1 z3a z3b]
+      #{w5b}    [w5b w5a w5c]
+      #{z3a x1} [z3a z3b x1])
+
+    ;; Maps
+    (is (thrown? IllegalArgumentException
+                 (read-string "{:a 1, :b 2, :a -1, :c 3}")))
+    ;; If there are duplicate keys when doing (assoc {} k1 v1 k2 v2
+    ;; ...), the behavior is that the metadata of the first duplicate
+    ;; key is kept, but mapped to the last value with an equal key
+    ;; (where metadata of keys are not compared).
+    (are [h x] (all-equal-maps-incl-meta h
+                                         (apply assoc {} x)
+                                         (apply hash-map x)
+                                         (apply sorted-map x)
+                                         (apply sorted-map-by cmp-first x)
+                                         (apply array-map x))
+      {x1 2, z3a 4} [x1 2, z3a 4]
+      {x1 2, z3a 5} [x1 2, z3a 4, z3b 5]
+      {z3a 5}       [z3a 2, z3a 4, z3b 5]
+      {z3b 4, x1 5} [z3b 2, z3a 4, x1 5]
+      {z3b v4b, x1 5} [z3b v4a, z3a v4b, x1 5]
+      {x1 v4a, w5a v4c, v4a z3b, y2 2} [x1 v4a, w5a v4a, w5b v4b,
+                                        v4a z3a, y2 2, v4b z3b, w5c v4c])))
+
+
+(deftest test-assoc
+  (are [x y] (= x y)
+       [4] (assoc [] 0 4)
+       [5 -7] (assoc [] 0 5 1 -7)
+       {:a 1} (assoc {} :a 1)
+       {:a 2 :b -2} (assoc {} :b -2 :a 2))
+  (is (thrown? IllegalArgumentException (assoc [] 0 5 1)))
+  (is (thrown? IllegalArgumentException (assoc {} :b -2 :a))))
+
+(defn is-same-collection [a b]
+  (let [msg (format "(class a)=%s (class b)=%s a=%s b=%s"
+                    (.getName (class a)) (.getName (class b)) a b)]
+    (is (= (count a) (count b) (.size a) (.size b)) msg)
+    (is (= a b) msg)
+    (is (= b a) msg)
+    (is (.equals ^Object a b) msg)
+    (is (.equals ^Object b a) msg)
+    (is (= (hash a) (hash b)) msg)
+    (is (= (.hashCode ^Object a) (.hashCode ^Object b)) msg)))
+
+(deftest ordered-collection-equality-test
+  (let [empty-colls [ []
+                      '()
+                      (lazy-seq)
+                      clojure.lang.PersistentQueue/EMPTY
+                      (vector-of :long) ]]
+    (doseq [c1 empty-colls, c2 empty-colls]
+      (is-same-collection c1 c2)))
+  (let [colls1 [ [-3 :a "7th"]
+                 '(-3 :a "7th")
+                 (lazy-seq (cons -3
+                   (lazy-seq (cons :a
+                     (lazy-seq (cons "7th" nil))))))
+                 (into clojure.lang.PersistentQueue/EMPTY
+                       [-3 :a "7th"]) ]]
+    (doseq [c1 colls1, c2 colls1]
+      (is-same-collection c1 c2)))
+  (is-same-collection [-3 1 7] (vector-of :long -3 1 7)))
+
+(defn case-indendent-string-cmp [s1 s2]
+  (compare (string/lower-case s1) (string/lower-case s2)))
+
+(deftest set-equality-test
+  (let [empty-sets [ #{}
+                     (hash-set)
+                     (sorted-set)
+                     (sorted-set-by case-indendent-string-cmp) ]]
+    (doseq [s1 empty-sets, s2 empty-sets]
+      (is-same-collection s1 s2)))
+  (let [sets1 [ #{"Banana" "apple" "7th"}
+                (hash-set "Banana" "apple" "7th")
+                (sorted-set "Banana" "apple" "7th")
+                (sorted-set-by case-indendent-string-cmp "Banana" "apple" "7th") ]]
+    (doseq [s1 sets1, s2 sets1]
+      (is-same-collection s1 s2))))
+
+(deftest map-equality-test
+  (let [empty-maps [ {}
+                     (hash-map)
+                     (array-map)
+                     (sorted-map)
+                     (sorted-map-by case-indendent-string-cmp) ]]
+    (doseq [m1 empty-maps, m2 empty-maps]
+      (is-same-collection m1 m2)))
+  (let [maps1 [ {"Banana" "like", "apple" "love", "7th" "indifferent"}
+                (hash-map "Banana" "like", "apple" "love", "7th" "indifferent")
+                (array-map "Banana" "like", "apple" "love", "7th" "indifferent")
+                (sorted-map "Banana" "like", "apple" "love", "7th" "indifferent")
+                (sorted-map-by case-indendent-string-cmp
+                               "Banana" "like", "apple" "love", "7th" "indifferent") ]]
+    (doseq [m1 maps1, m2 maps1]
+      (is-same-collection m1 m2))))
+
+;; *** Collection hashes ***
+;; See: http://clojure.org/data_structures#hash
+
+(defn hash-ordered [collection]
+  (-> (reduce (fn [acc e] (unchecked-add-int (unchecked-multiply-int 31 acc) (hash e)))
+              1
+              collection)
+      (mix-collection-hash (count collection))))
+
+(defn hash-unordered [collection]
+  (-> (reduce unchecked-add-int 0 (map hash collection))
+      (mix-collection-hash (count collection))))
+
+(defn gen-elements
+  []
+  (gen/vec gen/anything))
+
+(defspec ordered-collection-hashes-match
+  identity
+  [^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
+  (let [v (vec elem)
+        l (apply list elem)]
+    (is (= (hash v)
+           (hash l)
+           (hash (map identity elem))
+           (hash-ordered elem)))))
+
+(defspec unordered-set-hashes-match
+  identity
+  [^{:tag clojure.test-clojure.data-structures/gen-elements} elem]
+  (let [unique-elem (distinct elem)
+        s (into #{} unique-elem)]
+    (is (= (hash s)
+           (hash-unordered unique-elem)))))
diff --git a/test/clojure/test_clojure/def.clj b/test/clojure/test_clojure/def.clj
index b0712db..500aaee 100644
--- a/test/clojure/test_clojure/def.clj
+++ b/test/clojure/test_clojure/def.clj
@@ -11,9 +11,54 @@
         clojure.test-clojure.protocols))
 
 (deftest defn-error-messages
-  (testing "bad arglist forms"
-    (is (fails-with-cause? IllegalArgumentException '#"Parameter declaration arg1 should be a vector"
-          (eval-in-temp-ns (defn foo (arg1 arg2)))))))
+  (testing "multiarity syntax invalid parameter declaration"
+    (is (fails-with-cause? 
+          IllegalArgumentException 
+          #"Parameter declaration arg1 should be a vector"
+          (eval-in-temp-ns (defn foo (arg1 arg2))))))
+
+  (testing "multiarity syntax invalid signature"
+    (is (fails-with-cause? 
+          IllegalArgumentException 
+          #"Invalid signature \[a b\] should be a list"
+          (eval-in-temp-ns (defn foo 
+                             ([a] 1)
+                             [a b])))))
+
+  (testing "assume single arity syntax"
+    (is (fails-with-cause? 
+          IllegalArgumentException 
+          #"Parameter declaration a should be a vector"
+          (eval-in-temp-ns (defn foo a)))))
+
+  (testing "bad name"
+    (is (fails-with-cause? 
+          IllegalArgumentException 
+          #"First argument to defn must be a symbol"
+          (eval-in-temp-ns (defn "bad docstring" testname [arg1 arg2])))))
+         
+  (testing "missing parameter/signature"
+    (is (fails-with-cause? 
+          IllegalArgumentException 
+          #"Parameter declaration missing"
+          (eval-in-temp-ns (defn testname)))))
+
+  (testing "allow trailing map"
+    (is (eval-in-temp-ns (defn a "asdf" ([a] 1) {:a :b}))))
+
+  (testing "don't allow interleaved map"
+    (is (fails-with-cause? 
+          IllegalArgumentException 
+          #"Invalid signature \{:a :b\} should be a list"
+          (eval-in-temp-ns (defn a "asdf" ([a] 1) {:a :b} ([] 1)))))))
+
+(deftest non-dynamic-warnings
+  (testing "no warning for **"
+    (is (empty? (with-err-print-writer
+                  (eval-in-temp-ns (defn ** ([a b] (Math/pow (double a) (double b)))))))))
+  (testing "warning for *hello*"
+    (is (not (empty? (with-err-print-writer
+                       (eval-in-temp-ns (def *hello* "hi"))))))))
 
 (deftest dynamic-redefinition
   ;; too many contextual things for this kind of caching to work...
diff --git a/test/clojure/test_clojure/delays.clj b/test/clojure/test_clojure/delays.clj
new file mode 100644
index 0000000..0de3341
--- /dev/null
+++ b/test/clojure/test_clojure/delays.clj
@@ -0,0 +1,21 @@
+(ns clojure.test-clojure.delays
+  (:use clojure.test))
+
+(deftest calls-once
+  (let [a (atom 0)
+        d (delay (swap! a inc))]
+    (is (= 0 @a))
+    (is (= 1 @d))
+    (is (= 1 @d))
+    (is (= 1 @a))))
+
+(deftest saves-exceptions
+  (let [f #(do (throw (Exception. "broken"))
+               1)
+        d (delay (f))
+        try-call #(try
+                    @d
+                    (catch Exception e e))
+        first-result (try-call)]
+    (is (instance? Exception first-result))
+    (is (identical? first-result (try-call)))))
diff --git a/test/clojure/test_clojure/edn.clj b/test/clojure/test_clojure/edn.clj
new file mode 100644
index 0000000..4d5996c
--- /dev/null
+++ b/test/clojure/test_clojure/edn.clj
@@ -0,0 +1,38 @@
+;   Copyright (c) Rich Hickey. All rights reserved.
+;   The use and distribution terms for this software are covered by the
+;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;   which can be found in the file epl-v10.html at the root of this distribution.
+;   By using this software in any fashion, you are agreeing to be bound by
+;   the terms of this license.
+;   You must not remove this notice, or any other, from this software.
+
+; Author: Stuart Halloway
+
+
+(ns clojure.test-clojure.edn
+  (:require [clojure.test.generative :refer (defspec)]
+            [clojure.test-clojure.generators :as cgen]
+            [clojure.edn :as edn]))
+
+(defn roundtrip
+  "Print an object and read it back as edn. Returns rather than throws
+   any exceptions."
+  [o]
+  (binding [*print-length* nil
+            *print-dup* nil
+            *print-level* nil]
+    (try
+     (-> o pr-str edn/read-string)
+     (catch Throwable t t))))
+
+(defspec types-that-should-roundtrip
+  roundtrip
+  [^{:tag cgen/ednable} o]
+  (when-not (= o %)
+    (throw (ex-info "Value cannot roundtrip, see ex-data" {:printed o :read %}))))
+
+(defspec types-that-should-not-roundtrip
+  roundtrip
+  [^{:tag cgen/non-ednable} o]
+  (when-not (instance? Throwable %)
+    (throw (ex-info "edn/read should have thrown, see ex-data" {:printed o :read %}))))
diff --git a/test/clojure/test_clojure/errors.clj b/test/clojure/test_clojure/errors.clj
index 67e51d6..597145c 100644
--- a/test/clojure/test_clojure/errors.clj
+++ b/test/clojure/test_clojure/errors.clj
@@ -16,6 +16,9 @@
 
 (defn f1 [a] a)
 
+;; Function name that includes many special characters to test demunge
+(defn f2:+><->!#%&*|b [x] x)
+
 (defmacro m0 [] `(identity 0))
 
 (defmacro m1 [a] `(inc ~a))
@@ -29,7 +32,10 @@
   (is (thrown-with-msg? ArityException #"Wrong number of args \(1\) passed to"
         (macroexpand `(m0 1))))
   (is (thrown-with-msg? ArityException #"Wrong number of args \(2\) passed to"
-        (macroexpand `(m1 1 2)))))
+        (macroexpand `(m1 1 2))))
+  (is (thrown-with-msg? ArityException #"\Q/f2:+><->!#%&*|b\E"
+        (f2:+><->!#%&*|b 1 2))
+        "ArityException messages should demunge function names"))
 
 (deftest assert-arg-messages
   ; used to ensure that error messages properly use local names for macros
@@ -45,3 +51,10 @@
     (is (thrown-with-msg? IllegalArgumentException
                           (re-pattern (format msg-regex-str *ns*))
                           (macroexpand form)))))
+
+(deftest extract-ex-data
+  (try
+   (throw (ex-info "example error" {:foo 1}))
+   (catch Throwable t
+     (is (= {:foo 1} (ex-data t)))))
+  (is (nil? (ex-data (RuntimeException. "example non ex-data")))))
diff --git a/test/clojure/test_clojure/fn.clj b/test/clojure/test_clojure/fn.clj
new file mode 100644
index 0000000..c85b155
--- /dev/null
+++ b/test/clojure/test_clojure/fn.clj
@@ -0,0 +1,55 @@
+;   Copyright (c) Rich Hickey. All rights reserved.
+;   The use and distribution terms for this software are covered by the
+;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;   which can be found in the file epl-v10.html at the root of this distribution.
+;   By using this software in any fashion, you are agreeing to be bound by
+;   the terms of this license.
+;   You must not remove this notice, or any other, from this software.
+
+; Author: Ambrose Bonnaire-Sergeant
+
+(ns clojure.test-clojure.fn
+  (:use clojure.test))
+
+(deftest fn-error-checking
+  (testing "bad arglist"
+    (is (fails-with-cause? java.lang.IllegalArgumentException 
+          #"Parameter declaration a should be a vector"
+          (eval '(fn "a" a)))))
+
+  (testing "treat first param as args"
+    (is (fails-with-cause? java.lang.IllegalArgumentException 
+          #"Parameter declaration a should be a vector"
+          (eval '(fn "a" [])))))
+
+  (testing "looks like listy signature, but malformed declaration"
+    (is (fails-with-cause? java.lang.IllegalArgumentException
+          #"Parameter declaration 1 should be a vector"
+          (eval '(fn (1))))))
+
+  (testing "checks each signature"
+    (is (fails-with-cause? java.lang.IllegalArgumentException
+          #"Parameter declaration a should be a vector"
+          (eval '(fn
+                   ([a] 1)
+                   ("a" 2))))))
+
+  (testing "correct name but invalid args"
+    (is (fails-with-cause? java.lang.IllegalArgumentException
+          #"Parameter declaration a should be a vector"
+          (eval '(fn a "a")))))
+
+  (testing "first sig looks multiarity, rest of sigs should be lists"
+    (is (fails-with-cause? java.lang.IllegalArgumentException 
+          #"Invalid signature \[a b\] should be a list"
+          (eval '(fn a
+                   ([a] 1)
+                   [a b])))))
+  
+  (testing "missing parameter declaration"
+    (is (fails-with-cause? java.lang.IllegalArgumentException 
+          #"Parameter declaration missing"
+          (eval '(fn a))))
+    (is (fails-with-cause? java.lang.IllegalArgumentException 
+          #"Parameter declaration missing"
+          (eval '(fn))))))
diff --git a/test/clojure/test_clojure/genclass.clj b/test/clojure/test_clojure/genclass.clj
index 2d95c17..8fe893b 100644
--- a/test/clojure/test_clojure/genclass.clj
+++ b/test/clojure/test_clojure/genclass.clj
@@ -13,6 +13,7 @@
   (:import [clojure.test_clojure.genclass.examples
             ExampleClass
             ExampleAnnotationClass
+            ProtectedFinalTester
             ArrayDefInterface
             ArrayGenInterface]
 
@@ -69,6 +70,13 @@
   (is (fails-with-cause? IllegalArgumentException #"Not a valid method name: has-hyphen"
         (@#'clojure.core/validate-generate-class-options {:methods '[[fine [] void] [has-hyphen [] void]]}))))
 
+(deftest protected-final-access
+  (let [obj (ProtectedFinalTester.)]
+    (testing "Protected final method visibility"
+      (is (thrown? IllegalArgumentException (.findSystemClass obj "java.lang.String"))))
+    (testing "Allow exposition of protected final method."
+      (is (= String (.superFindSystemClass obj "java.lang.String"))))))
+
 (deftest interface-array-type-hints
   (let [array-types       {:ints     (class (int-array 0))
                            :bytes    (class (byte-array 0))
diff --git a/test/clojure/test_clojure/genclass/examples.clj b/test/clojure/test_clojure/genclass/examples.clj
index 158622f..5b2a62a 100644
--- a/test/clojure/test_clojure/genclass/examples.clj
+++ b/test/clojure/test_clojure/genclass/examples.clj
@@ -41,6 +41,17 @@
                                                              java.lang.annotation.ElementType/PARAMETER]}
                            String] void]])
 
+(gen-class :name clojure.test_clojure.genclass.examples.ProtectedFinalTester
+           :extends java.lang.ClassLoader
+           :main false
+           :prefix "pf-"
+           :exposes-methods {findSystemClass superFindSystemClass})
+
+(defn pf-findSystemClass
+  "This function should never be called."
+  [_ name]
+  clojure.lang.RT)
+
 (definterface ArrayDefInterface
   ; primitive array sugar
   (^void takesByteArray [^bytes a])
diff --git a/test/clojure/test_clojure/generators.clj b/test/clojure/test_clojure/generators.clj
new file mode 100644
index 0000000..ea31974
--- /dev/null
+++ b/test/clojure/test_clojure/generators.clj
@@ -0,0 +1,132 @@
+(ns clojure.test-clojure.generators
+  (:require [clojure.data.generators :as gen])
+  (:refer-clojure :exclude [namespace]))
+
+(defn var-value-source
+  "Generates a scalar suitable for an initial var value."
+  []
+  (let [v (gen/scalar)]
+    (if (symbol? v)
+      `(quote ~v)
+      v)))
+
+(defn var-source
+  [n]
+  `(def ~(symbol (str "var" n))
+     ~(var-value-source)))
+
+(defn record-source
+  [n]
+  (let [rname (str "ExampleRecord" "-" n)
+        fldct (gen/geometric 0.1)]
+    `(defrecord ~(symbol rname) ~(vec (map #(symbol (str "f" %)) (range fldct))))))
+
+(defn generate-namespaces
+  "Returns a map with :nses, :vars, :records"
+  [{:keys [nses vars-per-ns records-per-ns]}]
+  (let [nses (mapv #(create-ns (symbol (str "clojure.generated.ns" %)))
+                   (range nses))
+        _ (doseq [ns nses] (binding [*ns* ns] (refer 'clojure.core)))
+        make-in-ns (fn [ns src] (binding [*ns* ns] (eval src)))
+        vars (->> (mapcat
+                   (fn [ns]
+                     (map
+                      #(make-in-ns ns (var-source %))
+                      (range vars-per-ns)))
+                   nses)
+                  (into []))
+        records (->> (mapcat
+                      (fn [ns]
+                        (map
+                         #(make-in-ns ns (record-source %))
+                         (range records-per-ns)))
+                      nses)
+                     (into []))]
+    {:nses nses
+     :vars vars
+     :records records}))
+
+(def shared-generation
+  (delay (generate-namespaces {:nses 5 :vars-per-ns 5 :records-per-ns 5})))
+
+(defn namespace
+  []
+  (gen/rand-nth (:nses @shared-generation)))
+
+(defn var
+  []
+  (gen/rand-nth (:vars @shared-generation)))
+
+(defn record
+  []
+  (gen/rand-nth (:records @shared-generation)))
+
+(def keyword-pool
+  (delay
+   (binding [gen/*rnd* (java.util.Random. 42)]
+     (into [] (repeatedly 1000 gen/keyword)))))
+
+(defn keyword-from-pool
+  []
+  (gen/rand-nth @keyword-pool))
+
+(def symbol-pool
+  (delay
+   (binding [gen/*rnd* (java.util.Random. 42)]
+     (into [] (repeatedly 1000 gen/symbol)))))
+
+(defn symbol-from-pool
+  []
+  (gen/rand-nth @keyword-pool))
+
+(def ednable-scalars
+  [(constantly nil)
+   gen/byte
+   gen/long
+   gen/boolean
+   gen/printable-ascii-char
+   gen/string
+   symbol-from-pool
+   keyword-from-pool
+   gen/uuid
+   gen/date
+   gen/ratio
+   gen/bigint
+   gen/bigdec])
+
+(defn- call-through
+  "Recursively call x until it doesn't return a function."
+  [x]
+  (if (fn? x)
+    (recur (x))
+    x))
+
+(defn ednable-scalar
+  []
+  (call-through (rand-nth ednable-scalars)))
+
+(def ednable-collections
+  [[gen/vec [ednable-scalars]]
+   [gen/set [ednable-scalars]]
+   [gen/hash-map [ednable-scalars ednable-scalars]]])
+
+(defn ednable-collection
+  []
+  (let [[coll args] (rand-nth ednable-collections)]
+    (apply coll (map rand-nth args))))
+
+(defn ednable
+  []
+  (gen/one-of ednable-scalar ednable-collection))
+
+(defn non-ednable
+  "Generate something that can be printed with *print-dup*, but
+   cannot be read back via edn/read."
+  []
+  (gen/one-of namespace var))
+
+(defn dup-readable
+  "Generate something that requires print-dup to be printed in
+   a roundtrippable way."
+  []
+  (gen/one-of namespace var))
diff --git a/test/clojure/test_clojure/java/io.clj b/test/clojure/test_clojure/java/io.clj
index a89ccdf..da51902 100644
--- a/test/clojure/test_clojure/java/io.clj
+++ b/test/clojure/test_clojure/java/io.clj
@@ -20,7 +20,8 @@
   (doto (File/createTempFile prefix suffix)
     (.deleteOnExit)))
 
-(deftest test-spit-and-slurp
+;; does not work on IBM JDK
+#_(deftest test-spit-and-slurp
   (let [f (temp-file "clojure.java.io" "test")
         content (apply str (concat "a" (repeat 500 "\u226a\ud83d\ude03")))]
     (spit f content)
@@ -105,8 +106,8 @@
        (bytes-should-equal (.getBytes s "UTF-8")
                            (.toByteArray o)
                            (str "combination " test opts))))))
-
-(deftest test-copy-encodings
+;; does not work on IBM JDK
+#_(deftest test-copy-encodings
   (doseq [enc [ "UTF-8" "UTF-16" "UTF-16BE" "UTF-16LE" ]]
     (testing (str "from inputstream " enc " to writer UTF-8")
       (let [{:keys [i s o w bs]} (data-fixture enc)]
@@ -126,6 +127,7 @@
        (File. "bar+baz") (URL. "file:bar+baz")
        (File. "bar baz qux") (URL. "file:bar%20baz%20qux")
        (File. "quux") (URI. "file:quux")
+       (File. "abcíd/foo.txt") (URL. "file:abc%c3%add/foo.txt")
        nil nil))
 
 (deftest test-resources-with-spaces
diff --git a/test/clojure/test_clojure/java_interop.clj b/test/clojure/test_clojure/java_interop.clj
index 55ab50f..3febeab 100644
--- a/test/clojure/test_clojure/java_interop.clj
+++ b/test/clojure/test_clojure/java_interop.clj
@@ -55,6 +55,16 @@
       Integer/MAX_VALUE
       (. Integer MAX_VALUE) ))
 
+(definterface I (a []))
+(deftype T [a] I (a [_] "method"))
+
+(deftest test-reflective-field-name-ambiguous
+  (let [t (->T "field")]
+    (is (= "method" (. ^T t a)))
+    (is (= "field" (. ^T t -a)))
+    (is (= "method" (. t a)))
+    (is (= "field" (. t -a)))
+    (is (thrown? IllegalArgumentException (. t -BOGUS)))))
 
 (deftest test-double-dot
   (is (= (.. System (getProperties) (get "os.name"))
@@ -102,8 +112,11 @@
       java.lang.Integer false
       java.lang.Long true
       java.lang.Character false
-      java.lang.String false ))
+      java.lang.String false )
 
+  ; test compiler macro
+  (is (let [Long String] (instance? Long "abc")))
+  (is (thrown? clojure.lang.ArityException (instance? Long))))
 
 ; set!
 
@@ -123,6 +136,11 @@
         (:alpha b) 255
         (:transparency b) 1
 
+        (:missing b) nil
+        (:missing b :default) :default
+        (get b :missing) nil
+        (get b :missing :default) :default
+
         (:class b) java.awt.Color )))
 
 
@@ -148,6 +166,12 @@
   (are [x y] (= x y)
       (bases java.lang.Math)
         (list java.lang.Object)
+      (bases java.util.Collection)
+        (list java.lang.Iterable)
+      (bases java.lang.Object)
+        nil
+      (bases java.lang.Comparable)
+        nil
       (bases java.lang.Integer)
         (list java.lang.Number java.lang.Comparable) ))
 
@@ -159,6 +183,19 @@
         #{java.lang.Number java.lang.Object
           java.lang.Comparable java.io.Serializable} ))
 
+(deftest test-proxy-super
+  (let [d (proxy [java.util.BitSet] []
+            (flip [bitIndex]
+              (try
+                (proxy-super flip bitIndex)
+                (catch IndexOutOfBoundsException e
+                  (throw (IllegalArgumentException. "replaced"))))))]
+    ;; normal call
+    (is (nil? (.flip d 0)))
+    ;; exception should use proxied form and return IllegalArg
+    (is (thrown? IllegalArgumentException (.flip d -1)))
+    ;; same behavior on second call
+    (is (thrown? IllegalArgumentException (.flip d -1)))))
 
 ; Arrays: [alength] aget aset [make-array to-array into-array to-array-2d aclone]
 ;   [float-array, int-array, etc]
@@ -294,7 +331,7 @@
 
 (test-to-passed-array-for vector)
 (test-to-passed-array-for list)
-(test-to-passed-array-for hash-set)
+;;(test-to-passed-array-for hash-set)
 (test-to-passed-array-for queue)
 
 (deftest test-into-array
@@ -437,8 +474,12 @@
       (double-array [1 2 3])
       (boolean-array [true false])
       (byte-array [(byte 1) (byte 2)])
+      (byte-array [1 2])
+      (byte-array 2 [1 2])
       (char-array [\a \b \c])
       (short-array [(short 1) (short 2)])
+      (short-array [1 2])
+      (short-array 2 [1 2])
       (make-array Integer/TYPE 3)
       (to-array [1 "a" :k])
       (into-array [1 2 3]) )
diff --git a/test/clojure/test_clojure/load.clj b/test/clojure/test_clojure/load.clj
deleted file mode 100644
index e2125a2..0000000
--- a/test/clojure/test_clojure/load.clj
+++ /dev/null
@@ -1,32 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-
-(ns clojure.test-clojure.load
-  (:use clojure.test))
-
-(deftest test-load
-  (testing "Should ignore self-loads without comment"
-    (is (nil? (require 'clojure.test-clojure.load.cyclic0))))
-  (testing "Should reject cyclic dependencies"
-    (testing "a->b->a"
-      (is (thrown-with-msg? Exception #".*Cyclic load dependency.*"
-            (require 'clojure.test-clojure.load.cyclic1))))
-    (testing "a->b->c->d->b"
-      (is (thrown-with-msg? Exception #".*Cyclic load dependency.*"
-            (require 'clojure.test-clojure.load.cyclic3))))))
-
-(deftest test-require-refer
-  (try
-    (binding [*ns* *ns*]
-      (ns clojure.test-clojure.require-scratch
-        (:require [clojure.set :refer [difference]]
-                  [clojure.walk :refer :all]))
-      (is (fn? (eval 'difference)))
-      (is (every? fn? (map eval '[postwalk-replace prewalk-replace walk]))))
-    (finally
-     (remove-ns 'clojure.test-clojure.require-scratch))))
\ No newline at end of file
diff --git a/test/clojure/test_clojure/load/cyclic0.clj b/test/clojure/test_clojure/load/cyclic0.clj
deleted file mode 100644
index 66a3cc1..0000000
--- a/test/clojure/test_clojure/load/cyclic0.clj
+++ /dev/null
@@ -1,12 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-;
-; Author: Stephen C. Gilardi
-
-(ns clojure.test-clojure.load.cyclic0
-  (:require clojure.test-clojure.load.cyclic0))
diff --git a/test/clojure/test_clojure/load/cyclic1.clj b/test/clojure/test_clojure/load/cyclic1.clj
deleted file mode 100644
index bf341f7..0000000
--- a/test/clojure/test_clojure/load/cyclic1.clj
+++ /dev/null
@@ -1,12 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-;
-; Author: Stephen C. Gilardi
-
-(ns clojure.test-clojure.load.cyclic1
-  (:require clojure.test-clojure.load.cyclic2))
diff --git a/test/clojure/test_clojure/load/cyclic2.clj b/test/clojure/test_clojure/load/cyclic2.clj
deleted file mode 100644
index 180a162..0000000
--- a/test/clojure/test_clojure/load/cyclic2.clj
+++ /dev/null
@@ -1,12 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-;
-; Author: Stephen C. Gilardi
-
-(ns clojure.test-clojure.load.cyclic2
-  (:require clojure.test-clojure.load.cyclic1))
diff --git a/test/clojure/test_clojure/load/cyclic3.clj b/test/clojure/test_clojure/load/cyclic3.clj
deleted file mode 100644
index 51a8e45..0000000
--- a/test/clojure/test_clojure/load/cyclic3.clj
+++ /dev/null
@@ -1,12 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-;
-; Author: Stephen C. Gilardi
-
-(ns clojure.test-clojure.load.cyclic3
-  (:require clojure.test-clojure.load.cyclic4))
diff --git a/test/clojure/test_clojure/load/cyclic4.clj b/test/clojure/test_clojure/load/cyclic4.clj
deleted file mode 100644
index d66a0fc..0000000
--- a/test/clojure/test_clojure/load/cyclic4.clj
+++ /dev/null
@@ -1,12 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-;
-; Author: Stephen C. Gilardi
-
-(ns clojure.test-clojure.load.cyclic4
-  (:require clojure.test-clojure.load.cyclic5))
diff --git a/test/clojure/test_clojure/load/cyclic5.clj b/test/clojure/test_clojure/load/cyclic5.clj
deleted file mode 100644
index e825b74..0000000
--- a/test/clojure/test_clojure/load/cyclic5.clj
+++ /dev/null
@@ -1,12 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-;
-; Author: Stephen C. Gilardi
-
-(ns clojure.test-clojure.load.cyclic5
-  (:require clojure.test-clojure.load.cyclic6))
diff --git a/test/clojure/test_clojure/load/cyclic6.clj b/test/clojure/test_clojure/load/cyclic6.clj
deleted file mode 100644
index d607f05..0000000
--- a/test/clojure/test_clojure/load/cyclic6.clj
+++ /dev/null
@@ -1,12 +0,0 @@
-;   Copyright (c) Rich Hickey. All rights reserved.
-;   The use and distribution terms for this software are covered by the
-;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
-;   which can be found in the file epl-v10.html at the root of this distribution.
-;   By using this software in any fashion, you are agreeing to be bound by
-;   the terms of this license.
-;   You must not remove this notice, or any other, from this software.
-;
-; Author: Stephen C. Gilardi
-
-(ns clojure.test-clojure.load.cyclic6
-  (:require clojure.test-clojure.load.cyclic4))
diff --git a/test/clojure/test_clojure/logic.clj b/test/clojure/test_clojure/logic.clj
index 73db4d4..c85855c 100644
--- a/test/clojure/test_clojure/logic.clj
+++ b/test/clojure/test_clojure/logic.clj
@@ -203,3 +203,10 @@
       ; Java objects
       (new java.util.Date) ))
 
+(deftest test-some?
+  (are [expected x] (= expected (some? x))
+       false nil
+       true false
+       true 0
+       true "abc"
+       true []))
diff --git a/test/clojure/test_clojure/macros.clj b/test/clojure/test_clojure/macros.clj
index 711130e..e832b7e 100644
--- a/test/clojure/test_clojure/macros.clj
+++ b/test/clojure/test_clojure/macros.clj
@@ -16,3 +16,54 @@
 ; ->
 ; defmacro definline macroexpand-1 macroexpand
 
+
+;; -> and ->> should not be dependent on the meaning of their arguments
+
+(defmacro c
+  [arg]
+  (if (= 'b (first arg))
+    :foo
+    :bar))
+
+(deftest ->test
+  (let [a 2, b identity]
+    (is (= (-> a b c)
+           (c (b a))))))
+
+(deftest ->>test
+  (let [a 2, b identity]
+    (is (= (->> a b c)
+           (c (b a))))))
+
+(deftest ->metadata-test
+  (testing "a trivial form"
+    (is (= {:hardy :har :har :-D}
+           (meta (macroexpand-1 (list `-> (with-meta
+                                            'quoted-symbol
+                                            {:hardy :har :har :-D})))))))
+  (testing "a nontrivial form"
+    (let [a (with-meta 'a {:foo :bar})
+          b (with-meta '(b c d) {:bar :baz})
+          e (with-meta 'e {:baz :quux})
+          expanded (macroexpand-1 (list `-> a b e))]
+      (is (= expanded '(e (b a c d))))
+      (is (= {:baz :quux} (meta (first expanded))))
+      (is (= {:bar :baz} (meta (second expanded))))
+      (is (= {:foo :bar} (meta (second (second expanded))))))))
+
+
+(deftest ->>metadata-test
+  (testing "a trivial form"
+    (is (= {:hardy :har :har :-D}
+           (meta (macroexpand-1 (list `->> (with-meta
+                                             'quoted-symbol
+                                             {:hardy :har :har :-D})))))))
+  (testing "a non-trivial form"
+    (let [a (with-meta 'a {:foo :bar})
+          b (with-meta '(b c d) {:bar :baz})
+          e (with-meta 'e {:baz :quux})
+          expanded (macroexpand-1 (list `->> a b e))]
+      (is (= expanded '(e (b c d a))))
+      (is (= {:baz :quux} (meta (first expanded))))
+      (is (= {:bar :baz} (meta (second expanded))))
+      (is (= {:foo :bar} (meta (last (second expanded))))))))
diff --git a/test/clojure/test_clojure/metadata.clj b/test/clojure/test_clojure/metadata.clj
index c993045..d0158ab 100644
--- a/test/clojure/test_clojure/metadata.clj
+++ b/test/clojure/test_clojure/metadata.clj
@@ -10,7 +10,8 @@
 
 (ns clojure.test-clojure.metadata
   (:use clojure.test
-        [clojure.test-helper :only (eval-in-temp-ns)]))
+        [clojure.test-helper :only (eval-in-temp-ns)])
+  (:require [clojure.set :as set]))
 
 (def public-namespaces
   '[clojure.core
@@ -88,3 +89,144 @@
         (is (eval-in-temp-ns
              (defn foo ^long [^long x] x)
              (def x (foo (inc 10)))))))))
+ 
+ (deftest fns-preserve-metadata-on-maps
+   (let [xm {:a 1 :b -7}
+         x (with-meta {:foo 1 :bar 2} xm)
+         ym {:c "foo"}
+         y (with-meta {:baz 4 :guh x} ym)]
+ 
+     (is (= xm (meta (:guh y))))
+     (is (= xm (meta (reduce #(assoc %1 %2 (inc %2)) x (range 1000)))))
+     (is (= xm (meta (-> x (dissoc :foo) (dissoc :bar)))))
+     (let [z (assoc-in y [:guh :la] 18)]
+       (is (= ym (meta z)))
+       (is (= xm (meta (:guh z)))))
+     (let [z (update-in y [:guh :bar] inc)]
+       (is (= ym (meta z)))
+       (is (= xm (meta (:guh z)))))
+     (is (= xm (meta (get-in y [:guh]))))
+     (is (= xm (meta (into x y))))
+     (is (= ym (meta (into y x))))
+     
+     (is (= xm (meta (merge x y))))
+     (is (= ym (meta (merge y x))))
+     (is (= xm (meta (merge-with + x y))))
+     (is (= ym (meta (merge-with + y x))))
+ 
+     (is (= xm (meta (select-keys x [:bar]))))
+     (is (= xm (meta (set/rename-keys x {:foo :new-foo}))))
+ 
+     ;; replace returns a seq when given a set.  Can seqs have
+     ;; metadata?
+     
+     ;; TBD: rseq, subseq, and rsubseq returns seqs.  If it is even
+     ;; possible to put metadata on a seq, does it make sense that the
+     ;; seqs returned by these functions should have the same metadata
+     ;; as the sorted collection on which they are called?
+     ))
+ 
+ (deftest fns-preserve-metadata-on-vectors
+   (let [xm {:a 1 :b -7}
+         x (with-meta [1 2 3] xm)
+         ym {:c "foo"}
+         y (with-meta [4 x 6] ym)]
+ 
+     (is (= xm (meta (y 1))))
+     (is (= xm (meta (assoc x 1 "one"))))
+     (is (= xm (meta (reduce #(conj %1 %2) x (range 1000)))))
+     (is (= xm (meta (pop (pop (pop x))))))
+     (let [z (assoc-in y [1 2] 18)]
+       (is (= ym (meta z)))
+       (is (= xm (meta (z 1)))))
+     (let [z (update-in y [1 2] inc)]
+       (is (= ym (meta z)))
+       (is (= xm (meta (z 1)))))
+     (is (= xm (meta (get-in y [1]))))
+     (is (= xm (meta (into x y))))
+     (is (= ym (meta (into y x))))
+ 
+     (is (= xm (meta (replace {2 "two"} x))))
+     (is (= [1 "two" 3] (replace {2 "two"} x)))
+ 
+     ;; TBD: Currently subvec drops metadata.  Should it preserve it?
+     ;;(is (= xm (meta (subvec x 2 3))))
+ 
+     ;; TBD: rseq returns a seq.  If it is even possible to put
+     ;; metadata on a seq, does it make sense that the seqs returned by
+     ;; these functions should have the same metadata as the sorted
+     ;; collection on which they are called?
+     ))
+ 
+ (deftest fns-preserve-metadata-on-sets
+   ;; TBD: Do tests independently for set, hash-set, and sorted-set,
+   ;; perhaps with a loop here.
+   (let [xm {:a 1 :b -7}
+         x (with-meta #{1 2 3} xm)
+         ym {:c "foo"}
+         y (with-meta #{4 x 6} ym)]
+ 
+     (is (= xm (meta (y #{3 2 1}))))
+     (is (= xm (meta (reduce #(conj %1 %2) x (range 1000)))))
+     (is (= xm (meta (-> x (disj 1) (disj 2) (disj 3)))))
+     (is (= xm (meta (into x y))))
+     (is (= ym (meta (into y x))))
+ 
+     (is (= xm (meta (set/select even? x))))
+     (let [cow1m {:what "betsy cow"}
+           cow1 (with-meta {:name "betsy" :id 33} cow1m)
+           cow2m {:what "panda cow"}
+           cow2 (with-meta {:name "panda" :id 34} cow2m)
+           cowsm {:what "all the cows"}
+           cows (with-meta #{cow1 cow2} cowsm)
+           cow-names (set/project cows [:name])
+           renamed (set/rename cows {:id :number})]
+       (is (= cowsm (meta cow-names)))
+       (is (= cow1m (meta (first (filter #(= "betsy" (:name %)) cow-names)))))
+       (is (= cow2m (meta (first (filter #(= "panda" (:name %)) cow-names)))))
+       (is (= cowsm (meta renamed)))
+       (is (= cow1m (meta (first (filter #(= "betsy" (:name %)) renamed)))))
+       (is (= cow2m (meta (first (filter #(= "panda" (:name %)) renamed))))))
+ 
+     ;; replace returns a seq when given a set.  Can seqs have
+     ;; metadata?
+ 
+     ;; union: Currently returns the metadata of the largest input set.
+     ;; This is an artifact of union's current implementation.  I doubt
+     ;; any explicit design decision was made to do so.  Like join,
+     ;; there doesn't seem to be much reason to prefer the metadata of
+     ;; one input set over another, if at least two input sets are
+     ;; given, but perhaps defining it to always return a set with the
+     ;; metadata of the first input set would be reasonable?
+ 
+     ;; intersection: Returns metadata of the smallest input set.
+     ;; Otherwise similar to union.
+ 
+     ;; difference: Seems to always return a set with metadata of first
+     ;; input set.  Seems reasonable.  Not sure we want to add a test
+     ;; for it, if it is an accident of the current implementation.
+ 
+     ;; join, index, map-invert: Currently always returns a value with
+     ;; no metadata.  This seems reasonable.
+     ))
+
+(deftest defn-primitive-args
+  (testing "Hinting the arg vector of a primitive-taking fn with a non-primitive type should not result in AbstractMethodError when invoked."
+    (testing "CLJ-850 is fixed when this case passes."
+      (is (= "foo"
+             (eval-in-temp-ns
+              (defn f ^String [^String s ^long i] s)
+              (f "foo" 1)))))
+    (testing "These cases should pass, even without a fix for CLJ-850."
+      (is (= "foo"
+             (eval-in-temp-ns
+              (defn f ^String [^String s] s)
+              (f "foo"))))
+      (is (= 1
+             (eval-in-temp-ns
+              (defn f ^long [^String s ^long i] i)
+              (f "foo" 1))))
+      (is (= 1
+             (eval-in-temp-ns
+              (defn f ^long [^long i] i)
+              (f 1)))))))
diff --git a/test/clojure/test_clojure/multimethods.clj b/test/clojure/test_clojure/multimethods.clj
index 01ff8a7..c75606d 100644
--- a/test/clojure/test_clojure/multimethods.clj
+++ b/test/clojure/test_clojure/multimethods.clj
@@ -158,3 +158,77 @@
     #(and %1 %2)
     (map true? (for ~@args))))
 
+(deftest basic-multimethod-test
+  (testing "Check basic dispatch"
+    (defmulti too-simple identity)
+    (defmethod too-simple :a [x] :a)
+    (defmethod too-simple :b [x] :b)
+    (defmethod too-simple :default [x] :default)
+    (is (= :a (too-simple :a)))
+    (is (= :b (too-simple :b)))
+    (is (= :default (too-simple :c))))
+  (testing "Remove a method works"
+    (remove-method too-simple :a)
+    (is (= :default (too-simple :a))))
+  (testing "Add another method works"
+    (defmethod too-simple :d [x] :d)
+    (is (= :d (too-simple :d)))))
+
+(deftest isA-multimethod-test
+  (testing "Dispatch on isA"
+    ;; Example from the multimethod docs.
+    (derive java.util.Map ::collection)
+    (derive java.util.Collection ::collection)
+    (defmulti foo class)
+    (defmethod foo ::collection [c] :a-collection)
+    (defmethod foo String [s] :a-string)
+    (is (= :a-collection (foo [])))
+    (is (= :a-collection (foo (java.util.HashMap.))))
+    (is (= :a-string (foo "bar")))))
+
+(deftest preferences-multimethod-test
+ (testing "Multiple method match dispatch error is caught"
+    ;; Example from the multimethod docs.
+    (derive ::rect ::shape)
+    (defmulti bar (fn [x y] [x y]))
+    (defmethod bar [::rect ::shape] [x y] :rect-shape)
+    (defmethod bar [::shape ::rect] [x y] :shape-rect)
+    (is (thrown? java.lang.IllegalArgumentException
+                 (bar ::rect ::rect))))
+ (testing "The prefers method returns empty table w/ no prefs"
+   (= {} (prefers bar)))
+ (testing "Adding a preference to resolve it dispatches correctly"
+   (prefer-method bar [::rect ::shape] [::shape ::rect])
+   (is (= :rect-shape (bar ::rect ::rect))))
+ (testing "The prefers method now returns the correct table"
+   (is (= {[::rect ::shape] #{[::shape ::rect]}} (prefers bar)))))
+
+(deftest remove-all-methods-test
+  (testing "Core function remove-all-methods works"
+    (defmulti simple1 identity)
+    (defmethod simple1 :a [x] :a)
+    (defmethod simple1 :b [x] :b)
+    (is (= {} (methods (remove-all-methods simple1))))))
+
+(deftest methods-test
+  (testing "Core function methods works"
+    (defmulti simple2 identity)
+    (defmethod simple2 :a [x] :a)
+    (defmethod simple2 :b [x] :b)
+    (is (= #{:a :b} (into #{} (keys (methods simple2)))))
+    (is (= :a ((:a (methods simple2)) 1)))
+    (defmethod simple2 :c [x] :c)
+    (is (= #{:a :b :c} (into #{} (keys (methods simple2)))))
+    (remove-method simple2 :a)
+    (is (= #{:b :c} (into #{} (keys (methods simple2)))))))
+
+(deftest get-method-test
+  (testing "Core function get-method works"
+    (defmulti simple3 identity)
+    (defmethod simple3 :a [x] :a)
+    (defmethod simple3 :b [x] :b)
+    (is (fn? (get-method simple3 :a)))
+    (is (= (:a ((get-method simple3 :a) 1))))
+    (is (fn? (get-method simple3 :b)))
+    (is (= (:b ((get-method simple3 :b) 1))))
+    (is (nil? (get-method simple3 :c)))))
diff --git a/test/clojure/test_clojure/numbers.clj b/test/clojure/test_clojure/numbers.clj
index b120497..3c8a3e7 100644
--- a/test/clojure/test_clojure/numbers.clj
+++ b/test/clojure/test_clojure/numbers.clj
@@ -13,7 +13,9 @@
 
 (ns clojure.test-clojure.numbers
   (:use clojure.test
-        clojure.template))
+        [clojure.test.generative :exclude (is)]
+        clojure.template)
+  (:require [clojure.data.generators :as gen]))
 
 
 ; TODO:
@@ -25,7 +27,7 @@
 
 
 (deftest Coerced-BigDecimal
-  (let [v (bigdec 3)]
+  (doseq [v [(bigdec 3) (bigdec (inc (bigint Long/MAX_VALUE)))]]
     (are [x] (true? x)
      (instance? BigDecimal v)
      (number? v)
@@ -33,10 +35,66 @@
      (not (float? v)))))
 
 (deftest BigInteger-conversions
-  (are [x] (biginteger x)
-    Long/MAX_VALUE
-    13178456923875639284562345789M
-    13178456923875639284562345789N))
+  (doseq [coerce-fn [bigint biginteger]]
+    (doseq [v (map coerce-fn [ Long/MAX_VALUE
+                              13178456923875639284562345789M
+                              13178456923875639284562345789N
+                              Float/MAX_VALUE
+                              (- Float/MAX_VALUE)
+                              Double/MAX_VALUE
+                              (- Double/MAX_VALUE)
+                              (* 2 (bigdec Double/MAX_VALUE)) ])]
+      (are [x] (true? x)
+        (integer? v)
+        (number? v)
+        (not (decimal? v))
+        (not (float? v))))))
+
+(defn all-pairs-equal [equal-var vals]
+  (doseq [val1 vals]
+    (doseq [val2 vals]
+      (is (equal-var val1 val2)
+          (str "Test that " val1 " (" (class val1) ") "
+               equal-var " " val2 " (" (class val2) ")")))))
+
+(defn all-pairs-hash-consistent-with-= [vals]
+  (doseq [val1 vals]
+    (doseq [val2 vals]
+      (when (= val1 val2)
+        (is (= (hash val1) (hash val2))
+            (str "Test that (hash " val1 ") (" (class val1) ") "
+                 " = (hash " val2 ") (" (class val2) ")"))))))
+
+(deftest equality-tests
+  ;; = only returns true for numbers that are in the same category,
+  ;; where category is one of INTEGER, FLOATING, DECIMAL, RATIO.
+  (all-pairs-equal #'= [(byte 2) (short 2) (int 2) (long 2)
+                        (bigint 2) (biginteger 2)])
+  (all-pairs-equal #'= [(float 2.0) (double 2.0)])
+  (all-pairs-equal #'= [2.0M 2.00M])
+  (all-pairs-equal #'= [(float 1.5) (double 1.5)])
+  (all-pairs-equal #'= [1.50M 1.500M])
+  (all-pairs-equal #'= [0.0M 0.00M])
+  (all-pairs-equal #'= [(/ 1 2) (/ 2 4)])
+
+  ;; No BigIntegers or floats in following tests, because hash
+  ;; consistency with = for them is out of scope for Clojure
+  ;; (CLJ-1036).
+  (all-pairs-hash-consistent-with-= [(byte 2) (short 2) (int 2) (long 2)
+                                     (bigint 2)
+                                     (double 2.0) 2.0M 2.00M])
+  (all-pairs-hash-consistent-with-= [(/ 3 2) (double 1.5) 1.50M 1.500M])
+  (all-pairs-hash-consistent-with-= [(double 0.0) 0.0M 0.00M])
+
+  ;; == tests for numerical equality, returning true even for numbers
+  ;; in different categories.
+  (all-pairs-equal #'== [(byte 0) (short 0) (int 0) (long 0)
+                         (bigint 0) (biginteger 0)
+                         (float 0.0) (double 0.0) 0.0M 0.00M])
+  (all-pairs-equal #'== [(byte 2) (short 2) (int 2) (long 2)
+                         (bigint 2) (biginteger 2)
+                         (float 2.0) (double 2.0) 2.0M 2.00M])
+  (all-pairs-equal #'== [(/ 3 2) (float 1.5) (double 1.5) 1.50M 1.500M]))
 
 (deftest unchecked-cast-num-obj
   (do-template [prim-array cast]
@@ -202,6 +260,17 @@
 
   (is (> (* 3 (int (/ Integer/MAX_VALUE 2.0))) Integer/MAX_VALUE)) )  ; no overflow
 
+(deftest test-multiply-longs-at-edge
+  (are [x] (= x 9223372036854775808N)
+       (*' -1 Long/MIN_VALUE)
+       (*' Long/MIN_VALUE -1)
+       (* -1N Long/MIN_VALUE)
+       (* Long/MIN_VALUE -1N)
+       (* -1 (bigint Long/MIN_VALUE))
+       (* (bigint Long/MIN_VALUE) -1))
+  (is (thrown? ArithmeticException (* Long/MIN_VALUE -1)))
+  (is (thrown? ArithmeticException (* -1 Long/MIN_VALUE))))
+
 (deftest test-ratios-simplify-to-ints-where-appropriate
   (testing "negative denominator (assembla #275)"
     (is (integer? (/ 1 -1/2)))
@@ -463,9 +532,25 @@ Math/pow overflows to Infinity."
        0 (bit-shift-right 2r10 -1) ; truncated to least 6-bits, 63
        1 (bit-shift-right (expt 2 32) 32)
        1 (bit-shift-right (expt 2 16) 10000) ; truncated to least 6-bits, 16
+       -1 (bit-shift-right -2r10 1)
        )
   (is (thrown? IllegalArgumentException (bit-shift-right 1N 1))))
 
+(deftest test-unsigned-bit-shift-right
+  (are [x y] (= x y)
+       2r0 (unsigned-bit-shift-right 2r1 1)
+       2r010 (unsigned-bit-shift-right 2r100 1)
+       2r001 (unsigned-bit-shift-right 2r100 2)
+       2r000 (unsigned-bit-shift-right 2r100 3)
+       2r0001011 (unsigned-bit-shift-right 2r00010111 1)
+       2r0001011 (apply unsigned-bit-shift-right [2r00010111 1])
+       0 (unsigned-bit-shift-right 2r10 -1) ; truncated to least 6-bits, 63
+       1 (unsigned-bit-shift-right (expt 2 32) 32)
+       1 (unsigned-bit-shift-right (expt 2 16) 10000) ; truncated to least 6-bits, 16
+       9223372036854775807 (unsigned-bit-shift-right -2r10 1)
+       )
+  (is (thrown? IllegalArgumentException (unsigned-bit-shift-right 1N 1))))
+
 (deftest test-bit-clear
   (is (= 2r1101 (bit-clear 2r1111 1)))
   (is (= 2r1101 (bit-clear 2r1101 1))))
@@ -563,3 +648,77 @@ Math/pow overflows to Infinity."
            min
            max))))
 
+(defn integer
+  "Distribution of integers biased towards the small, but
+   including all longs."
+  []
+  (gen/one-of #(gen/uniform -1 32) gen/byte gen/short gen/int gen/long))
+
+(defn longable?
+  [n]
+  (try
+   (long n)
+   true
+   (catch Exception _)))
+
+(defspec integer-commutative-laws
+  (partial map identity)
+  [^{:tag `integer} a ^{:tag `integer} b]
+  (if (longable? (+' a b))
+    (assert (= (+ a b) (+ b a)
+               (+' a b) (+' b a)
+               (unchecked-add a b) (unchecked-add b a)))
+    (assert (= (+' a b) (+' b a))))
+  (if (longable? (*' a b))
+    (assert (= (* a b) (* b a)
+               (*' a b) (*' b a)
+               (unchecked-multiply a b) (unchecked-multiply b a)))
+    (assert (= (*' a b) (*' b a)))))
+
+(defspec integer-associative-laws
+  (partial map identity)
+  [^{:tag `integer} a ^{:tag `integer} b ^{:tag `integer} c]
+  (if (every? longable? [(+' a b) (+' b c) (+' a b c)])
+    (assert (= (+ (+ a b) c) (+ a (+ b c))
+               (+' (+' a b) c) (+' a (+' b c))
+               (unchecked-add (unchecked-add a b) c) (unchecked-add a (unchecked-add b c))))
+    (assert (= (+' (+' a b) c) (+' a (+' b c))
+               (+ (+ (bigint a) b) c) (+ a (+ (bigint b) c)))))
+  (if (every? longable? [(*' a b) (*' b c) (*' a b c)])
+    (assert (= (* (* a b) c) (* a (* b c))
+               (*' (*' a b) c) (*' a (*' b c))
+               (unchecked-multiply (unchecked-multiply a b) c) (unchecked-multiply a (unchecked-multiply b c))))
+    (assert (= (*' (*' a b) c) (*' a (*' b c))
+               (* (* (bigint a) b) c) (* a (* (bigint b) c))))))
+
+(defspec integer-distributive-laws
+  (partial map identity)
+  [^{:tag `integer} a ^{:tag `integer} b ^{:tag `integer} c]
+  (if (every? longable? [(*' a (+' b c)) (+' (*' a b) (*' a c))
+                         (*' a b) (*' a c) (+' b c)])
+    (assert (= (* a (+ b c)) (+ (* a b) (* a c))
+               (*' a (+' b c)) (+' (*' a b) (*' a c))
+               (unchecked-multiply a (+' b c)) (+' (unchecked-multiply a b) (unchecked-multiply a c))))
+    (assert (= (*' a (+' b c)) (+' (*' a b) (*' a c))
+               (* a (+ (bigint b) c)) (+ (* (bigint a) b) (* (bigint a) c))))))
+
+(defspec addition-undoes-subtraction
+  (partial map identity)
+  [^{:tag `integer} a ^{:tag `integer} b]
+  (if (longable? (-' a b))
+    (assert (= a
+               (-> a (- b) (+ b))
+               (-> a (unchecked-subtract b) (unchecked-add b)))))
+  (assert (= a
+             (-> a (-' b) (+' b)))))
+
+(defspec quotient-and-remainder
+  (fn [a b] (sort [a b]))
+  [^{:tag `integer} a ^{:tag `integer} b]
+  (when-not (zero? (second %))
+    (let [[a d] %
+          q (quot a d)
+          r (rem a d)]
+      (assert (= a
+                 (+ (* q d) r)
+                 (unchecked-add (unchecked-multiply q d) r))))))
diff --git a/test/clojure/test_clojure/other_functions.clj b/test/clojure/test_clojure/other_functions.clj
index 3848a16..c23e599 100644
--- a/test/clojure/test_clojure/other_functions.clj
+++ b/test/clojure/test_clojure/other_functions.clj
@@ -88,8 +88,46 @@
          (keyword "foo") :foo)))
 
 ; complement
+
+(deftest test-complement
+  (let [not-contains? (complement contains?)]
+    (is (= true (not-contains? [2 3 4] 5)))
+    (is (= false (not-contains? [2 3 4] 2))))
+  (let [first-elem-not-1? (complement (fn [x] (= 1 (first x))))]
+    (is (= true (first-elem-not-1? [2 3])))
+    (is (= false (first-elem-not-1? [1 2])))))
+
 ; constantly
 
+(deftest test-constantly
+  (let [c0 (constantly 10)]
+    (are [x] (= 10 (c0 x))
+         nil
+         42
+         "foo")))
+;juxt
+
+(deftest test-juxt
+  ;; juxt for colls
+  (let [m0 {:a 1 :b 2}
+        a0 [1 2]]
+    (is (= [1 2] ((juxt :a :b) m0)))
+    (is (= [2 1] ((juxt fnext first) a0))))
+  ;; juxt for fns
+  (let [a1 (fn [a] (+ 2 a))
+        b1 (fn [b] (* 2 b))]
+    (is (= [5 6] ((juxt a1 b1) 3)))))
+
+;partial
+
+(deftest test-partial
+  (let [p0 (partial inc)
+        p1 (partial + 20)
+        p2 (partial conj [1 2])]
+    (is (= 41 (p0 40)))
+    (is (= 40 (p1 20)))
+    (is (= [1 2 3] (p2 3)))))
+
 ; every-pred
 (deftest test-every-pred
   (are [result expr] (= result expr)
diff --git a/test/clojure/test_clojure/parallel.clj b/test/clojure/test_clojure/parallel.clj
index fb98d60..357e961 100644
--- a/test/clojure/test_clojure/parallel.clj
+++ b/test/clojure/test_clojure/parallel.clj
@@ -27,3 +27,14 @@
   ;; regression fixed in r1218; was OutOfMemoryError
   (is (= '(1) (pmap inc [0]))))
 
+
+(def ^:dynamic *test-value* 1)
+
+(deftest future-fn-properly-retains-conveyed-bindings
+  (let [a (atom [])]
+    (binding [*test-value* 2]
+      @(future (dotimes [_ 3]
+                 ;; we need some binding to trigger binding pop
+                 (binding [*print-dup* false]
+                   (swap! a conj *test-value*))))
+      (is (= [2 2 2] @a)))))
diff --git a/test/clojure/test_clojure/pprint.clj b/test/clojure/test_clojure/pprint.clj
index 066d1b5..819c23d 100644
--- a/test/clojure/test_clojure/pprint.clj
+++ b/test/clojure/test_clojure/pprint.clj
@@ -10,7 +10,8 @@
 
 (ns clojure.test-clojure.pprint
   (:refer-clojure :exclude [format])
-  (:use [clojure.test :only (deftest are run-tests)]
+  (:require [clojure.string :as str])
+  (:use [clojure.test :only (deftest is are run-tests)]
         [clojure.test-helper :only [platform-newlines]]
         clojure.test-clojure.pprint.test-helper
         clojure.pprint))
diff --git a/test/clojure/test_clojure/pprint/test_cl_format.clj b/test/clojure/test_clojure/pprint/test_cl_format.clj
index 8a95104..53ae3f2 100644
--- a/test/clojure/test_clojure/pprint/test_cl_format.clj
+++ b/test/clojure/test_clojure/pprint/test_cl_format.clj
@@ -205,11 +205,87 @@
   (cl-format nil "~,1f" 0.99) "1.0"
   (cl-format nil "~,2f" 0.99) "0.99"
   (cl-format nil "~,3f" 0.99) "0.990"
+  (cl-format nil "~,3f" -0.099) "-0.099"
+  (cl-format nil "~,4f" -0.099) "-0.0990"
+  (cl-format nil "~,5f" -0.099) "-0.09900"
+  (cl-format nil "~,3f" 0.099) "0.099"
+  (cl-format nil "~,4f" 0.099) "0.0990"
+  (cl-format nil "~,5f" 0.099) "0.09900"
   (cl-format nil "~f" -1) "-1.0"
   (cl-format nil "~2f" -1) "-1."
   (cl-format nil "~3f" -1) "-1."
   (cl-format nil "~4f" -1) "-1.0"
   (cl-format nil "~8f" -1) "    -1.0"
+  (cl-format nil "~2f" -0.0099) "-0."
+  (cl-format nil "~3f" -0.0099) "-0."
+  (cl-format nil "~4f" -0.0099) "-.01"
+  (cl-format nil "~5f" -0.0099) "-0.01"
+  (cl-format nil "~6f" -0.0099) "-.0099"
+  (cl-format nil "~1f" 0.0099) "0."
+  (cl-format nil "~2f" 0.0099) "0."
+  (cl-format nil "~3f" 0.0099) ".01"
+  (cl-format nil "~4f" 0.0099) "0.01"
+  (cl-format nil "~5f" 0.0099) ".0099"
+  (cl-format nil "~6f" 0.0099) "0.0099"
+  (cl-format nil "~1f" -0.099) "-.1"
+  (cl-format nil "~2f" -0.099) "-.1"
+  (cl-format nil "~3f" -0.099) "-.1"
+  (cl-format nil "~4f" -0.099) "-0.1"
+  (cl-format nil "~5f" -0.099) "-.099"
+  (cl-format nil "~6f" -0.099) "-0.099"
+  (cl-format nil "~1f" 0.099) ".1"
+  (cl-format nil "~2f" 0.099) ".1"
+  (cl-format nil "~3f" 0.099) "0.1"
+  (cl-format nil "~4f" 0.099) ".099"
+  (cl-format nil "~5f" 0.099) "0.099"
+  (cl-format nil "~1f" -0.99) "-1."
+  (cl-format nil "~2f" -0.99) "-1."
+  (cl-format nil "~3f" -0.99) "-1."
+  (cl-format nil "~4f" -0.99) "-.99"
+  (cl-format nil "~5f" -0.99) "-0.99"
+  (cl-format nil "~1f" 0.99) "1."
+  (cl-format nil "~2f" 0.99) "1."
+  (cl-format nil "~3f" 0.99) ".99"
+  (cl-format nil "~4f" 0.99) "0.99"
+  (cl-format nil "~1f" 111.11111) "111."
+  (cl-format nil "~4f" 111.11111) "111."
+  (cl-format nil "~5f" 111.11111) "111.1"
+  (cl-format nil "~1f" -111.11111) "-111."
+  (cl-format nil "~5f" -111.11111) "-111."
+  (cl-format nil "~6f" -111.11111) "-111.1"
+  (cl-format nil "~1f" 555.55555) "556."
+  (cl-format nil "~4f" 555.55555) "556."
+  (cl-format nil "~5f" 555.55555) "555.6"
+  (cl-format nil "~8f" 555.55555) "555.5556"
+  (cl-format nil "~1f" -555.55555) "-556."
+  (cl-format nil "~5f" -555.55555) "-556."
+  (cl-format nil "~6f" -555.55555) "-555.6"
+  (cl-format nil "~8f" -555.55555) "-555.556"
+  (cl-format nil "~1f" 999.999) "1000."
+  (cl-format nil "~5f" 999.999) "1000."
+  (cl-format nil "~6f" 999.999) "1000.0"
+  (cl-format nil "~7f" 999.999) "999.999"
+  (cl-format nil "~8f" 999.999) " 999.999"
+  (cl-format nil "~1f" -999.999) "-1000."
+  (cl-format nil "~6f" -999.999) "-1000."
+  (cl-format nil "~7f" -999.999) "-1000.0"
+  (cl-format nil "~8f" -999.999) "-999.999"
+  (cl-format nil "~5,2f" 111.11111) "111.11"
+  (cl-format nil "~3,1f" -0.0099) "-.0"
+  (cl-format nil "~6,4f" -0.0099) "-.0099"
+  (cl-format nil "~6,5f" -0.0099) "-.00990"
+  (cl-format nil "~6,6f" -0.0099) "-.009900"
+  (cl-format nil "~6,4f" 0.0099) "0.0099"
+  (cl-format nil "~6,5f" 0.0099) ".00990"
+  (cl-format nil "~6,6f" 0.0099) ".009900"
+  (cl-format nil "~2,1f" 0.0099) ".0"
+  (cl-format nil "~6,2f" -111.11111) "-111.11"
+  (cl-format nil "~6,3f" -111.11111) "-111.111"
+  (cl-format nil "~8,5f" -111.11111) "-111.11111"
+  (cl-format nil "~12,10f" 1.23456789014) "1.2345678901"
+  (cl-format nil "~12,10f" 1.23456789016) "1.2345678902"
+  (cl-format nil "~13,10f" -1.23456789014) "-1.2345678901"
+  (cl-format nil "~13,10f" -1.23456789016) "-1.2345678902"
   (cl-format nil "~1,1f" 0.1) ".1")
 
 (simple-tests ampersand-tests
@@ -513,8 +589,28 @@
   (format nil "~6,2F|~6,2,1,'*F|~6,2,,'?F|~6F|~,2F|~F" 
           x x x x x x))
 
+;; big-pos-ratio is a ratio value that is larger than
+;; Double/MAX_VALUE, and has a non-terminating decimal representation
+;; if you attempt to represent it exactly.
+(def big-pos-ratio (/ (* 4 (bigint (. BigDecimal valueOf Double/MAX_VALUE))) 3))
+(def big-neg-ratio (- big-pos-ratio))
+;; tiny-pos-ratio is a ratio between 0 and Double/MIN_VALUE.
+(def tiny-pos-ratio (/ 1 (bigint (apply str (cons "1" (repeat 340 "0"))))))
+(def tiny-neg-ratio (- tiny-pos-ratio))
+
 (simple-tests cltl-F-tests
+  (cl-format false "~10,3f" 4/5) "     0.800"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3f" big-pos-ratio)) "239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3f" big-neg-ratio)) "-239692417981642093333333333333333300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.000"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3f" tiny-pos-ratio)) "     0.000"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3f" tiny-neg-ratio)) "    -0.000"
   (foo 3.14159)  "  3.14| 31.42|  3.14|3.1416|3.14|3.14159" 
+  (foo 314159/100000)
+                 "  3.14| 31.42|  3.14|3.1416|3.14|3.14159"
   (foo -3.14159) " -3.14|-31.42| -3.14|-3.142|-3.14|-3.14159" 
   (foo 100.0)    "100.00|******|100.00| 100.0|100.00|100.0" 
   (foo 1234.0)   "1234.00|******|??????|1234.0|1234.00|1234.0" 
@@ -527,7 +623,18 @@
 
 ;; Clojure doesn't support float/double differences in representation
 (simple-tests cltl-E-tests
+  (cl-format false "~10,3e" 4/5) "  8.000E-1"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3e" big-pos-ratio)) "2.397E+308"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3e" big-neg-ratio)) "-2.397E+308"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3e" tiny-pos-ratio)) "1.000E-340"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3e" tiny-neg-ratio)) "-1.000E-340"
   (foo-e 0.0314159) "  3.14E-2| 31.42$-03|+.003E+01|  3.14E-2"  ; Added this one 
+  (foo-e 314159/10000000)
+                    "  3.14E-2| 31.42$-03|+.003E+01|  3.14E-2"
   (foo-e 3.14159)  "  3.14E+0| 31.42$-01|+.003E+03|  3.14E+0" 
   (foo-e -3.14159) " -3.14E+0|-31.42$-01|-.003E+03| -3.14E+0"
   (foo-e 1100.0)   "  1.10E+3| 11.00$+02|+.001E+06|  1.10E+3" 
@@ -565,7 +672,18 @@
 
 ;; Clojure doesn't support float/double differences in representation
 (simple-tests cltl-G-tests
+  (cl-format false "~10,3g" 4/5)  " 0.800    "
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3g" big-pos-ratio)) "2.397E+308"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3g" big-neg-ratio)) "-2.397E+308"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3g" tiny-pos-ratio)) "1.000E-340"
+  (binding [*math-context* java.math.MathContext/DECIMAL128]
+    (cl-format false "~10,3g" tiny-neg-ratio)) "-1.000E-340"
   (foo-g 0.0314159) "  3.14E-2|314.2$-04|0.314E-01|  3.14E-2" 
+  (foo-g 314159/10000000)
+                    "  3.14E-2|314.2$-04|0.314E-01|  3.14E-2"
   (foo-g 0.314159)  "  0.31   |0.314    |0.314    | 0.31    " 
   (foo-g 3.14159)   "   3.1   | 3.14    | 3.14    |  3.1    " 
   (foo-g 31.4159)   "   31.   | 31.4    | 31.4    |  31.    " 
@@ -683,3 +801,25 @@ but it was called with an argument of type short-float.\n")
           '((hot dog) (hamburger) (ice cream) (french fries))) 
   "/hot .../hamburger")
 
+(simple-tests pprint-table-tests
+  (with-out-str
+    (print-table [:b :a]
+                 [{:a 1 :b {:a 'is-a} :c ["hi" "there"]}
+                  {:b 5 :a 7 :c "dog" :d -700}]))
+  "
+|        :b | :a |
+|-----------+----|
+| {:a is-a} |  1 |
+|         5 |  7 |
+"
+  (with-out-str
+    (print-table [:a :e :d :c]
+                 [{:a 54.7e17 :b {:a 'is-a} :c ["hi" "there"]}
+                  {:b 5 :a -2/3 :c "dog" :d 'panda}]))
+  "
+|      :a | :e |    :d |             :c |
+|---------+----+-------+----------------|
+| 5.47E18 |    |       | [\"hi\" \"there\"] |
+|    -2/3 |    | panda |            dog |
+"
+  )
diff --git a/test/clojure/test_clojure/pprint/test_pretty.clj b/test/clojure/test_clojure/pprint/test_pretty.clj
index fb3bd34..d5477d9 100644
--- a/test/clojure/test_clojure/pprint/test_pretty.clj
+++ b/test/clojure/test_clojure/pprint/test_pretty.clj
@@ -124,49 +124,28 @@ Usage: *hello*
   "'foo"
 )
 
-(simple-tests code-block-tests 
- (with-out-str
-   (with-pprint-dispatch code-dispatch 
-     (pprint 
-      '(defn cl-format 
-         "An implementation of a Common Lisp compatible format function"
-         [stream format-in & args]
-         (let [compiled-format (if (string? format-in) (compile-format format-in) format-in)
-               navigator (init-navigator args)]
-           (execute-format stream compiled-format navigator))))))
- "(defn cl-format
+(defmacro code-block
+  "Read a string then print it with code-dispatch and succeed if it comes out the same"
+  [test-name & blocks]
+  `(simple-tests ~test-name
+     ~@(apply concat
+              (for [block blocks]
+                `[(str/split-lines
+                   (with-out-str
+                     (with-pprint-dispatch code-dispatch
+                       (pprint (read-string ~block)))))
+                  (str/split-lines ~block)]))))
+
+(code-block code-block-tests
+  "(defn cl-format
   \"An implementation of a Common Lisp compatible format function\"
   [stream format-in & args]
   (let [compiled-format (if (string? format-in)
                           (compile-format format-in)
                           format-in)
         navigator (init-navigator args)]
-    (execute-format stream compiled-format navigator)))
-"
-
- (with-out-str
-   (with-pprint-dispatch code-dispatch 
-     (pprint 
-      '(defn pprint-defn [writer alis]
-         (if (next alis) 
-           (let [[defn-sym defn-name & stuff] alis
-                 [doc-str stuff] (if (string? (first stuff))
-                                   [(first stuff) (next stuff)]
-                                   [nil stuff])
-                 [attr-map stuff] (if (map? (first stuff))
-                                    [(first stuff) (next stuff)]
-                                    [nil stuff])]
-             (pprint-logical-block writer :prefix "(" :suffix ")"
-                                   (cl-format true "~w ~1I~@_~w" defn-sym defn-name)
-                                   (if doc-str
-                                     (cl-format true " ~_~w" doc-str))
-                                   (if attr-map
-                                     (cl-format true " ~_~w" attr-map))
-                                   ;; Note: the multi-defn case will work OK for malformed defns too
-                                   (cond
-                                    (vector? (first stuff)) (single-defn stuff (or doc-str attr-map))
-                                    :else (multi-defn stuff (or doc-str attr-map)))))
-           (pprint-simple-code-list writer alis))))))
+    (execute-format stream compiled-format navigator)))"
+
  "(defn pprint-defn [writer alis]
   (if (next alis)
     (let [[defn-sym defn-name & stuff] alis
@@ -190,9 +169,41 @@ Usage: *hello*
                                     stuff
                                     (or doc-str attr-map))
           :else (multi-defn stuff (or doc-str attr-map)))))
-    (pprint-simple-code-list writer alis)))
-")
-
+    (pprint-simple-code-list writer alis)))")
+
+(code-block ns-macro-test
+  "(ns slam.hound.stitch
+  (:use [slam.hound.prettify :only [prettify]]))"
+  
+  "(ns slam.hound.prettify
+  \"Format a namespace declaration using pretty print with custom dispatch.\"
+  (:use [clojure.pprint :only [cl-format code-dispatch formatter-out
+                               pprint pprint-logical-block
+                               pprint-newline with-pprint-dispatch
+                               write-out]]))"
+
+  "(ns autodoc.build-html
+  \"This is the namespace that builds the HTML pages themselves.
+It is implemented with a number of custom enlive templates.\"
+  {:skip-wiki true, :author \"Tom Faulhaber\"}
+  (:refer-clojure :exclude [empty complement])
+  (:import [java.util.jar JarFile]
+           [java.io File FileWriter BufferedWriter StringReader
+                    BufferedInputStream BufferedOutputStream
+                    ByteArrayOutputStream FileReader FileInputStream]
+           [java.util.regex Pattern])
+  (:require [clojure.string :as str])
+  (:use [net.cgrand.enlive-html :exclude (deftemplate)]
+        [clojure.java.io :only (as-file file writer)]
+        [clojure.java.shell :only (sh)]
+        [clojure.pprint :only (pprint cl-format pprint-ident
+                               pprint-logical-block set-pprint-dispatch
+                               get-pretty-writer fresh-line)]
+        [clojure.data.json :only (pprint-json)]
+        [autodoc.collect-info :only (contrib-info)]
+        [autodoc.params :only (params expand-classpath)])
+  (:use clojure.set clojure.java.io clojure.data clojure.java.browse
+        clojure.inspector clojure.zip clojure.stacktrace))")
 
 (defn tst-pprint
   "A helper function to pprint to a string with a restricted right margin"
@@ -283,26 +294,22 @@ Usage: *hello*
   (binding [*print-length* 8] (with-out-str (pprint [1 2 3 4 5 6])))
   "[1 2 3 4 5 6]\n"
 
-  ;; This set of tests isn't that great cause it assumes that the set remains
-  ;; ordered for printing. This is currently (1.3) true, but no future
-  ;; guarantees
-  (binding [*print-length* 1] (with-out-str (pprint #{1 2 3 4 5 6})))
+  (binding [*print-length* 1] (with-out-str (pprint (sorted-set 1 2 3 4 5 6))))
   "#{1 ...}\n"
-  (binding [*print-length* 2] (with-out-str (pprint #{1 2 3 4 5 6})))
+  (binding [*print-length* 2] (with-out-str (pprint (sorted-set 1 2 3 4 5 6))))
   "#{1 2 ...}\n"
-  (binding [*print-length* 6] (with-out-str (pprint #{1 2 3 4 5 6})))
+  (binding [*print-length* 6] (with-out-str (pprint (sorted-set 1 2 3 4 5 6))))
   "#{1 2 3 4 5 6}\n"
-  (binding [*print-length* 8] (with-out-str (pprint #{1 2 3 4 5 6})))
+  (binding [*print-length* 8] (with-out-str (pprint (sorted-set 1 2 3 4 5 6))))
   "#{1 2 3 4 5 6}\n"
 
-  ;; See above comment and apply to this map :)
-  (binding [*print-length* 1] (with-out-str (pprint {1 2, 3 4, 5 6, 7 8, 9 10, 11 12})))
+  (binding [*print-length* 1] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12))))
   "{1 2, ...}\n"
-  (binding [*print-length* 2] (with-out-str (pprint {1 2, 3 4, 5 6, 7 8, 9 10, 11 12})))
+  (binding [*print-length* 2] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12))))
   "{1 2, 3 4, ...}\n"
-  (binding [*print-length* 6] (with-out-str (pprint {1 2, 3 4, 5 6, 7 8, 9 10, 11 12})))
+  (binding [*print-length* 6] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12))))
   "{1 2, 3 4, 5 6, 7 8, 9 10, 11 12}\n"
-  (binding [*print-length* 8] (with-out-str (pprint {1 2, 3 4, 5 6, 7 8, 9 10, 11 12})))
+  (binding [*print-length* 8] (with-out-str (pprint (sorted-map 1 2, 3 4, 5 6, 7 8, 9 10, 11 12))))
   "{1 2, 3 4, 5 6, 7 8, 9 10, 11 12}\n"
 
 
@@ -316,3 +323,49 @@ Usage: *hello*
   "[1, 2, 3, 4, 5, 6]\n"
   )
 
+(defn- flush-alerting-writer
+  [o]
+  (let [flush-count-atom (atom 0)]
+    [
+      (proxy [java.io.BufferedWriter] [o]
+        (flush []
+          (proxy-super flush)
+          (swap! flush-count-atom inc)))
+      flush-count-atom]))
+
+(deftest test-flush-underlying-prn
+  []
+  (let [[out flush-count-atom] (flush-alerting-writer (java.io.StringWriter.))]
+    (binding [*out* out
+              *flush-on-newline* true]
+      (prn (range 50))
+      (prn (range 50)))
+    (is (= @flush-count-atom 2) "println flushes on newline")))
+
+(deftest test-flush-underlying-pprint
+  []
+  (let [[out flush-count-atom] (flush-alerting-writer (java.io.StringWriter.))]
+    (binding [*out* out
+              *flush-on-newline* true]
+      (pprint (range 50))
+      (pprint (range 50)))
+    (is (= @flush-count-atom 2) "pprint flushes on newline")))
+
+(deftest test-noflush-underlying-prn
+  []
+  (let [[out flush-count-atom] (flush-alerting-writer (java.io.StringWriter.))]
+    (binding [*out* out
+              *flush-on-newline* nil]
+      (prn (range 50))
+      (prn (range 50)))
+    (is (= @flush-count-atom 0) "println flushes on newline")))
+
+(deftest test-noflush-underlying-pprint
+  []
+  (let [[out flush-count-atom] (flush-alerting-writer (java.io.StringWriter.))]
+    (binding [*out* out
+              *flush-on-newline* nil]
+      (pprint (range 50))
+      (pprint (range 50)))
+    (is (= @flush-count-atom 0) "pprint flushes on newline")))
+
diff --git a/test/clojure/test_clojure/protocols.clj b/test/clojure/test_clojure/protocols.clj
index 1e7a199..6f14179 100644
--- a/test/clojure/test_clojure/protocols.clj
+++ b/test/clojure/test_clojure/protocols.clj
@@ -71,6 +71,15 @@
                      (baz [a b] "two-arg baz!"))]
       (is (= "two-arg baz!" (baz obj nil)))
       (is (thrown? AbstractMethodError (baz obj)))))
+  (testing "error conditions checked when defining protocols"
+    (is (thrown-with-msg?
+         Exception
+         #"Definition of function m in protocol badprotdef must take at least one arg."
+         (eval '(defprotocol badprotdef (m [])))))
+    (is (thrown-with-msg?
+         Exception
+         #"Function m in protocol badprotdef was redefined. Specify all arities in single definition."
+         (eval '(defprotocol badprotdef (m [this arg]) (m [this arg1 arg2]))))))
   (testing "you can redefine a protocol with different methods"
     (eval '(defprotocol Elusive (old-method [x])))
     (eval '(defprotocol Elusive (new-method [x])))
@@ -78,6 +87,28 @@
     (is (fails-with-cause? IllegalArgumentException #"No method of interface: .*\.Elusive found for function: old-method of protocol: Elusive \(The protocol method may have been defined before and removed\.\)"
           (eval '(old-method (reify Elusive (new-method [x] :new-method))))))))
 
+(deftype HasMarkers []
+  ExampleProtocol
+  (foo [this] "foo")
+  MarkerProtocol
+  MarkerProtocol2)
+
+(deftype WillGetMarker []
+  ExampleProtocol
+  (foo [this] "foo"))
+
+(extend-type WillGetMarker MarkerProtocol)
+
+(deftest marker-tests
+  (testing "That a marker protocol has no methods"
+    (is (= '() (method-names clojure.test_clojure.protocols.examples.MarkerProtocol))))
+  (testing "That types with markers are reportedly satifying them."
+    (let [hm (HasMarkers.)
+          wgm (WillGetMarker.)]
+      (is (satisfies? MarkerProtocol hm))
+      (is (satisfies? MarkerProtocol2 hm))
+      (is (satisfies? MarkerProtocol wgm)))))
+
 (deftype ExtendTestWidget [name])
 (deftype HasProtocolInline []
   ExampleProtocol
@@ -98,6 +129,11 @@
      {:foo (fn [this] (str "widget " (.name this)))})
     (is (= "widget z" (foo (ExtendTestWidget. "z"))))))
 
+(deftest record-marker-interfaces
+  (testing "record? and type? return expected result for IRecord and IType"
+    (let [r (TestRecord. 1 2)]
+      (is (record? r)))))
+
 (deftest illegal-extending
   (testing "you cannot extend a protocol to a type that implements the protocol inline"
     (is (fails-with-cause? IllegalArgumentException #".*HasProtocolInline already directly implements interface"
@@ -414,6 +450,10 @@
     (is (thrown? Exception (read-string "(let [s \"en\"] #java.util.Locale[(str 'en)])")))
     (is (thrown? Exception (read-string "#clojure.test_clojure.protocols.RecordToTestLiterals{(keyword \"a\") 42}"))))
   
+  (testing "that ctors can have whitespace after class name but before {"
+    (is (= (RecordToTestLiterals. 42)
+           (read-string "#clojure.test_clojure.protocols.RecordToTestLiterals   {:a 42}"))))
+
   (testing "that the correct errors are thrown with malformed literals"
     (is (thrown-with-msg?
           Exception
@@ -585,3 +625,12 @@
   (is (= :foo (sqtp :foo))))
 
 
+(defprotocol Dasherizer
+  (-do-dashed [this]))
+(deftype Dashed []
+  Dasherizer
+  (-do-dashed [this] 10))
+
+(deftest test-leading-dashes
+  (is (= 10 (-do-dashed (Dashed.))))
+  (is (= [10] (map -do-dashed [(Dashed.)]))))
diff --git a/test/clojure/test_clojure/protocols/examples.clj b/test/clojure/test_clojure/protocols/examples.clj
index b964475..9d962d5 100644
--- a/test/clojure/test_clojure/protocols/examples.clj
+++ b/test/clojure/test_clojure/protocols/examples.clj
@@ -8,6 +8,11 @@
   (^String baz [a] [a b] "method with multiple arities")
   (with-quux [a] "method name with a hyphen"))
 
+(defprotocol MarkerProtocol
+  "a protocol with no methods")
+
+(defprotocol MarkerProtocol2)
+
 (definterface ExampleInterface
   (hinted [^int i])
   (hinted [^String s]))
diff --git a/test/clojure/test_clojure/reader.clj b/test/clojure/test_clojure/reader.clj
index d6178dc..ff0c04d 100644
--- a/test/clojure/test_clojure/reader.clj
+++ b/test/clojure/test_clojure/reader.clj
@@ -21,7 +21,10 @@
   (:use [clojure.instant :only [read-instant-date
                                 read-instant-calendar
                                 read-instant-timestamp]])
-  (:import clojure.lang.BigInt
+  (:require clojure.walk
+            [clojure.test.generative :refer (defspec)]
+            [clojure.test-clojure.generators :as cgen])
+  (:import [clojure.lang BigInt Ratio]
            java.io.File
            java.util.TimeZone))
 
@@ -273,6 +276,10 @@
 
   (is (instance? BigDecimal -1.0M))
   (is (instance? BigDecimal -1.M))
+
+  (is (instance? Ratio 1/2))
+  (is (instance? Ratio -1/2))
+  (is (instance? Ratio +1/2))
 )
 
 ;; Characters
@@ -391,6 +398,41 @@
 
 ;; Metadata (^ or #^ (deprecated))
 
+(deftest t-line-column-numbers
+  (let [code "(ns reader-metadata-test
+  (:require [clojure.java.io
+             :refer (resource reader)]))
+
+(let [a 5]
+  ^:added-metadata
+  (defn add-5
+    [x]
+    (reduce + x (range a))))"
+        stream (clojure.lang.LineNumberingPushbackReader.
+                 (java.io.StringReader. code))
+        top-levels (take-while identity (repeatedly #(read stream false nil)))
+        expected-metadata '{ns {:line 1, :column 1}
+                            :require {:line 2, :column 3}
+                            resource {:line 3, :column 21}
+                            let {:line 5, :column 1}
+                            defn {:line 6, :column 3 :added-metadata true}
+                            reduce {:line 9, :column 5}
+                            range {:line 9, :column 17}}
+        verified-forms (atom 0)]
+    (doseq [form top-levels]
+      (clojure.walk/postwalk
+        #(when (list? %)
+           (is (= (expected-metadata (first %))
+                  (meta %)))
+           (is (->> (meta %)
+                 vals
+                 (filter number?)
+                 (every? (partial instance? Integer))))
+           (swap! verified-forms inc))
+        form))
+    ;; sanity check against e.g. reading returning ()
+    (is (= (count expected-metadata) @verified-forms))))
+
 (deftest t-Metadata
   (is (= (meta '^:static ^:awesome ^{:static false :bar :baz} sym) {:awesome true, :bar :baz, :static true})))
 
@@ -418,6 +460,14 @@
 
 (deftest t-read)
 
+(deftest division
+  (is (= clojure.core// /))
+  (binding [*ns* *ns*]
+    (eval '(do (ns foo
+                 (:require [clojure.core :as bar])
+                 (:use [clojure.test]))
+               (is (= clojure.core// bar//))))))
+
 (deftest Instants
   (testing "Instants are read as java.util.Date by default"
     (is (= java.util.Date (class #inst "2010-11-12T13:14:15.666"))))
@@ -501,3 +551,68 @@
   (is (= 4 (.version #uuid "550e8400-e29b-41d4-a716-446655440000")))
   (is (= (print-str #uuid "550e8400-e29b-41d4-a716-446655440000")
          "#uuid \"550e8400-e29b-41d4-a716-446655440000\"")))
+
+(deftest unknown-tag
+  (let [my-unknown (fn [tag val] {:unknown-tag tag :value val})
+        throw-on-unknown (fn [tag val] (throw (RuntimeException. (str "No data reader function for tag " tag))))
+        my-uuid (partial my-unknown 'uuid)
+        u "#uuid \"550e8400-e29b-41d4-a716-446655440000\""
+        s "#never.heard.of/some-tag [1 2]" ]
+    (binding [*data-readers* {'uuid my-uuid}
+              *default-data-reader-fn* my-unknown]
+      (testing "Unknown tag"
+        (is (= (read-string s)
+               {:unknown-tag 'never.heard.of/some-tag
+                :value [1 2]})))
+      (testing "Override uuid tag"
+        (is (= (read-string u)
+               {:unknown-tag 'uuid
+                :value "550e8400-e29b-41d4-a716-446655440000"}))))
+
+    (binding [*default-data-reader-fn* throw-on-unknown]
+      (testing "Unknown tag with custom throw-on-unknown"
+        (are [err msg form] (thrown-with-msg? err msg (read-string form))
+             Exception #"No data reader function for tag foo" "#foo [1 2]"
+             Exception #"No data reader function for tag bar/foo" "#bar/foo [1 2]"
+             Exception #"No data reader function for tag bar.baz/foo" "#bar.baz/foo [1 2]")))
+
+    (testing "Unknown tag out-of-the-box behavior (like Clojure 1.4)"
+      (are [err msg form] (thrown-with-msg? err msg (read-string form))
+           Exception #"No reader function for tag foo" "#foo [1 2]"
+           Exception #"No reader function for tag bar/foo" "#bar/foo [1 2]"
+           Exception #"No reader function for tag bar.baz/foo" "#bar.baz/foo [1 2]"))))
+
+
+(defn roundtrip
+  "Print an object and read it back. Returns rather than throws
+   any exceptions."
+  [o]
+  (binding [*print-length* nil
+            *print-dup* nil
+            *print-level* nil]
+    (try
+     (-> o pr-str read-string)
+     (catch Throwable t t))))
+
+(defn roundtrip-dup
+  "Print an object with print-dup and read it back.
+   Returns rather than throws any exceptions."
+  [o]
+  (binding [*print-length* nil
+            *print-dup* true
+            *print-level* nil]
+    (try
+     (-> o pr-str read-string)
+     (catch Throwable t t))))
+
+(defspec types-that-should-roundtrip
+  roundtrip
+  [^{:tag cgen/ednable} o]
+  (when-not (= o %)
+    (throw (ex-info "Value cannot roundtrip, see ex-data" {:printed o :read %}))))
+
+(defspec types-that-need-dup-to-roundtrip
+  roundtrip-dup
+  [^{:tag cgen/dup-readable} o]
+  (when-not (= o %)
+    (throw (ex-info "Value cannot roundtrip, see ex-data" {:printed o :read %}))))
diff --git a/test/clojure/test_clojure/reducers.clj b/test/clojure/test_clojure/reducers.clj
new file mode 100644
index 0000000..c2852cc
--- /dev/null
+++ b/test/clojure/test_clojure/reducers.clj
@@ -0,0 +1,91 @@
+;   Copyright (c) Rich Hickey. All rights reserved.
+;   The use and distribution terms for this software are covered by the
+;   Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
+;   which can be found in the file epl-v10.html at the root of this distribution.
+;   By using this software in any fashion, you are agreeing to be bound by
+;   the terms of this license.
+;   You must not remove this notice, or any other, from this software.
+
+;; Author: Tassilo Horn
+
+(ns clojure.test-clojure.reducers
+  (:require [clojure.core.reducers :as r]
+            [clojure.test.generative :refer (defspec)]
+            [clojure.data.generators :as gen])
+  (:use clojure.test))
+
+(defmacro defequivtest
+  ;; f is the core fn, r is the reducers equivalent, rt is the reducible ->
+  ;; coll transformer
+  [name [f r rt] fns]
+  `(deftest ~name
+     (let [c# (range -100 1000)]
+       (doseq [fn# ~fns]
+         (is (= (~f fn# c#)
+                (~rt (~r fn# c#))))))))
+
+(defequivtest test-map
+  [map r/map #(into [] %)]
+  [inc dec #(Math/sqrt (Math/abs %))])
+
+(defequivtest test-mapcat
+  [mapcat r/mapcat #(into [] %)]
+  [(fn [x] [x])
+   (fn [x] [x (inc x)])
+   (fn [x] [x (inc x) x])])
+
+(deftest test-mapcat-obeys-reduced
+  (is (= [1 "0" 2 "1" 3]
+        (->> (concat (range 100) (lazy-seq (throw (Exception. "Too eager"))))
+          (r/mapcat (juxt inc str))
+          (r/take 5)
+          (into [])))))
+
+(defequivtest test-reduce
+  [reduce r/reduce identity]
+  [+' *'])
+
+(defequivtest test-filter
+  [filter r/filter #(into [] %)]
+  [even? odd? #(< 200 %) identity])
+
+
+(deftest test-sorted-maps
+  (let [m (into (sorted-map)
+                '{1 a, 2 b, 3 c, 4 d})]
+    (is (= "1a2b3c4d" (reduce-kv str "" m))
+        "Sorted maps should reduce-kv in sorted order")
+    (is (= 1 (reduce-kv (fn [acc k v]
+                          (reduced (+ acc k)))
+                        0 m))
+        "Sorted maps should stop reduction when asked")))
+
+(deftest test-nil
+  (is (= {:k :v} (reduce-kv assoc {:k :v} nil)))
+  (is (= 0 (r/fold + nil))))
+
+(defn gen-num []
+  (gen/uniform 0 2000))
+
+(defn reduced-at-probe
+  [m p]
+  (reduce-kv (fn [_ k v] (when (== p k) (reduced :foo))) nil m))
+
+(defspec reduced-always-returns
+  (fn [probe to-end]
+    (let [len (+ probe to-end 1)
+          nums (range len)
+          m (zipmap nums nums)]
+      (reduced-at-probe m probe)))
+  [^{:tag `gen-num} probe ^{:tag `gen-num} to-end]
+  (assert (= :foo %)))
+
+(deftest test-fold-runtime-exception
+  (is (thrown? IndexOutOfBoundsException
+               (let [test-map-count 1234
+                     k-fail (rand-int test-map-count)]
+                 (r/fold (fn ([])
+                           ([ret [k v]])
+                           ([ret k v] (when (= k k-fail)
+                                        (throw (IndexOutOfBoundsException.)))))
+                         (zipmap (range test-map-count) (repeat :dummy)))))))
diff --git a/test/clojure/test_clojure/reflect.clj b/test/clojure/test_clojure/reflect.clj
index 416092a..4ade5ba 100644
--- a/test/clojure/test_clojure/reflect.clj
+++ b/test/clojure/test_clojure/reflect.clj
@@ -10,7 +10,7 @@
                                        :y-only y-only
                                        :common common}))))))
 
-(deftest compare-reflect-and-asm
+#_(deftest compare-reflect-and-asm
   (let [cl (.getContextClassLoader (Thread/currentThread))
         asm-reflector (AsmReflector. cl)
         java-reflector (JavaReflector. cl)]
@@ -18,7 +18,8 @@
                         java.lang.Object
                         java.io.FileInputStream
                         clojure.lang.Compiler
-                        clojure.lang.PersistentVector]]
+                        clojure.lang.PersistentVector
+                        java.lang.SuppressWarnings]]
       (nodiff (type-reflect classname :reflector asm-reflector)
               (type-reflect classname :reflector java-reflector)))))
 
diff --git a/test/clojure/test_clojure/repl.clj b/test/clojure/test_clojure/repl.clj
index 8921c2c..ac2170d 100644
--- a/test/clojure/test_clojure/repl.clj
+++ b/test/clojure/test_clojure/repl.clj
@@ -2,13 +2,25 @@
   (:use clojure.test
         clojure.repl
         [clojure.test-helper :only [platform-newlines]]
-        clojure.test-clojure.repl.example))
+        clojure.test-clojure.repl.example)
+  (:require [clojure.string :as str]))
+
+(deftest test-doc
+  (testing "with namespaces"
+    (is (= "clojure.pprint"
+           (second (str/split-lines (with-out-str (doc clojure.pprint))))))))
 
 (deftest test-source
   (is (= "(defn foo [])" (source-fn 'clojure.test-clojure.repl.example/foo)))
   (is (= (platform-newlines "(defn foo [])\n") (with-out-str (source clojure.test-clojure.repl.example/foo))))
   (is (nil? (source-fn 'non-existent-fn))))
 
+(deftest test-source-read-eval-unknown
+  (is (thrown? IllegalStateException (binding [*read-eval* :unknown] (source reduce)))))
+
+(deftest test-source-read-eval-false
+  (is (binding [*read-eval* false] (with-out-str (source reduce)))))
+
 (deftest test-dir
   (is (thrown? Exception (dir-fn 'non-existent-ns)))
   (is (= '[bar foo] (dir-fn 'clojure.test-clojure.repl.example)))
@@ -29,3 +41,17 @@
     (is (some #{'defmacro} (apropos 'defmacro)))
     (is (some #{'defmacro} (apropos 'efmac)))
     (is (= [] (apropos 'nothing-has-this-name)))))
+
+
+(defmacro call-ns 
+  "Call ns with a unique namespace name. Return the result of calling ns"
+  []  `(ns a#))
+(defmacro call-ns-sym 
+  "Call ns wih a unique namespace name. Return the namespace symbol."
+  [] `(do (ns a#) 'a#))
+
+(deftest test-dynamic-ns
+  (testing "a call to ns returns nil"
+   (is (= nil (call-ns))))
+  (testing "requiring a dynamically created ns should not throw an exception"
+    (is (= nil (let [a (call-ns-sym)] (require a))))))
diff --git a/test/clojure/test_clojure/rt.clj b/test/clojure/test_clojure/rt.clj
index a70df2d..38ffb27 100644
--- a/test/clojure/test_clojure/rt.clj
+++ b/test/clojure/test_clojure/rt.clj
@@ -9,6 +9,7 @@
 ; Author: Stuart Halloway
 
 (ns clojure.test-clojure.rt
+  (:require clojure.set)
   (:use clojure.test clojure.test-helper))
 
 (defn bare-rt-print
@@ -32,19 +33,35 @@
      (defn prefers [] (throw (RuntimeException. "rebound!")))))
   (testing "reflection cannot resolve field"
     (should-print-err-message
-     #"Reflection warning, .*:\d+ - reference to field blah can't be resolved\.\r?\n"
+     #"Reflection warning, .*:\d+:\d+ - reference to field blah can't be resolved\.\r?\n"
      (defn foo [x] (.blah x))))
-  (testing "reflection cannot resolve instance method"
+  (testing "reflection cannot resolve field on known class"
     (should-print-err-message
-     #"Reflection warning, .*:\d+ - call to zap can't be resolved\.\r?\n"
+     #"Reflection warning, .*:\d+:\d+ - reference to field blah on java\.lang\.String can't be resolved\.\r?\n"
+     (defn foo [^String x] (.blah x))))
+  (testing "reflection cannot resolve instance method because it is missing"
+    (should-print-err-message
+     #"Reflection warning, .*:\d+:\d+ - call to method zap on java\.lang\.String can't be resolved \(no such method\)\.\r?\n"
+     (defn foo [^String x] (.zap x 1))))
+  (testing "reflection cannot resolve instance method because it has incompatible argument types"
+    (should-print-err-message
+     #"Reflection warning, .*:\d+:\d+ - call to method getBytes on java\.lang\.String can't be resolved \(argument types: java\.util\.regex\.Pattern\)\.\r?\n"
+     (defn foo [^String x] (.getBytes x #"boom"))))
+  (testing "reflection cannot resolve instance method because it has unknown argument types"
+    (should-print-err-message
+     #"Reflection warning, .*:\d+:\d+ - call to method getBytes on java\.lang\.String can't be resolved \(argument types: unknown\)\.\r?\n"
+     (defn foo [^String x y] (.getBytes x y))))
+  (testing "reflection cannot resolve instance method because target class is unknown"
+    (should-print-err-message
+     #"Reflection warning, .*:\d+:\d+ - call to method zap can't be resolved \(target class is unknown\)\.\r?\n"
      (defn foo [x] (.zap x 1))))
   (testing "reflection cannot resolve static method"
     (should-print-err-message
-     #"Reflection warning, .*:\d+ - call to valueOf can't be resolved\.\r?\n"
+     #"Reflection warning, .*:\d+:\d+ - call to static method valueOf on java\.lang\.Integer can't be resolved \(argument types: java\.util\.regex\.Pattern\)\.\r?\n"
      (defn foo [] (Integer/valueOf #"boom"))))
   (testing "reflection cannot resolve constructor"
     (should-print-err-message
-     #"Reflection warning, .*:\d+ - call to java.lang.String ctor can't be resolved\.\r?\n"
+     #"Reflection warning, .*:\d+:\d+ - call to java\.lang\.String ctor can't be resolved\.\r?\n"
      (defn foo [] (String. 1 2 3)))))
 
 (def example-var)
diff --git a/test/clojure/test_clojure/sequences.clj b/test/clojure/test_clojure/sequences.clj
index e69fdbb..35eea37 100644
--- a/test/clojure/test_clojure/sequences.clj
+++ b/test/clojure/test_clojure/sequences.clj
@@ -204,7 +204,7 @@
       1.2 nil
       "abc" nil ))
 
-;Tests that the comparator is preservered
+;Tests that the comparator is preserved
 ;The first element should be the same in each set if preserved.
 (deftest test-empty-sorted
   (let [inv-compare (comp - compare)]
@@ -682,7 +682,11 @@
 
     (interleave [] [3 4]) ()
     (interleave [1 2] []) ()
-    (interleave [] []) () ))
+    (interleave [] []) ()
+
+    (interleave [1]) '(1)
+
+    (interleave) () ))
 
 
 (deftest test-zipmap
@@ -944,7 +948,9 @@
       (range -2 -2) ()
       (range -2 -5) ()
 
-      (range 3 9 0) ()
+      (take 3 (range 3 9 0)) '(3 3 3)
+      (take 3 (range 9 3 0)) '(9 9 9)
+      (range 0 0 0) ()
       (range 3 9 1) '(3 4 5 6 7 8)
       (range 3 9 2) '(3 5 7)
       (range 3 9 3) '(3 6)
@@ -1108,10 +1114,10 @@
        [1 2 3 4 5] `(1 2 3 4 5)
        ;maps
        [] {:a 1 :b 2}
-       [:a 1 :b 2] (seq {:a 1 :b 2})
+       [:a 1 :b 2] (sort-by key {:a 1 :b 2})
        [] {[:a :b] 1 :c 2}
-       [:a :b 1 :c 2] (seq {[:a :b] 1 :c 2})
-       [:a 1 2 :b 3] (seq {:a [1 2] :b 3})
+       [:a :b 1 :c 2] (sort-by val {[:a :b] 1 :c 2})
+       [:a 1 2 :b 3] (sort-by key {:a [1 2] :b 3})
        ;Strings
        [] "12345"
        [\1 \2 \3 \4 \5] (seq "12345")
diff --git a/test/clojure/test_clojure/special.clj b/test/clojure/test_clojure/special.clj
index f3a8164..58a9dd9 100644
--- a/test/clojure/test_clojure/special.clj
+++ b/test/clojure/test_clojure/special.clj
@@ -22,3 +22,44 @@
 ; var
 ; fn
 
+(deftest multiple-keys-in-destructuring
+  (let [foo (fn [& {:keys [x]}] x)
+        bar (fn [& options] (apply foo :x :b options))]
+    (is (= (bar) :b))
+    (is (= (bar :x :a) :a))))
+
+(deftest empty-list-with-:as-destructuring
+  (let [{:as x} '()]
+    (is (= {} x))))
+
+(deftest keywords-in-destructuring
+  (let [{:keys [:a :b]} {:a 1 :b 2}]
+    (is (= 1 a))
+    (is (= 2 b))))
+
+(deftest namespaced-keywords-in-destructuring
+  (let [{:keys [:a/b :c/d]} {:a/b 1 :c/d 2}]
+    (is (= 1 b))
+    (is (= 2 d))))
+
+(deftest namespaced-keys-in-destructuring
+  (let [{:keys [a/b c/d]} {:a/b 1 :c/d 2}]
+    (is (= 1 b))
+    (is (= 2 d))))
+
+(deftest namespaced-syms-in-destructuring
+  (let [{:syms [a/b c/d]} {'a/b 1 'c/d 2}]
+    (is (= 1 b))
+    (is (= 2 d))))
+
+(deftest keywords-not-allowed-in-let-bindings
+  (is (thrown-with-msg? Exception #"Unsupported binding key: :a"
+                        (eval '(let [:a 1] a))))
+  (is (thrown-with-msg? Exception #"Unsupported binding key: :a/b"
+                        (eval '(let [:a/b 1] b)))))
+
+(require '[clojure.string :as s])
+(deftest resolve-keyword-ns-alias-in-destructuring
+  (let [{:keys [::s/x ::s/y]} {:clojure.string/x 1 :clojure.string/y 2}]
+    (is (= x 1))
+    (is (= y 2))))
diff --git a/test/clojure/test_clojure/string.clj b/test/clojure/test_clojure/string.clj
index d6f6469..5253642 100644
--- a/test/clojure/test_clojure/string.clj
+++ b/test/clojure/test_clojure/string.clj
@@ -12,14 +12,36 @@
 
 (deftest t-replace
   (is (= "faabar" (s/replace "foobar" \o \a)))
+  (is (= "foobar" (s/replace "foobar" \z \a)))
   (is (= "barbarbar" (s/replace "foobarfoo" "foo" "bar")))
-  (is (= "FOObarFOO" (s/replace "foobarfoo" #"foo" s/upper-case))))
+  (is (= "foobarfoo" (s/replace "foobarfoo" "baz" "bar")))
+  (is (= "f$$d" (s/replace "food" "o" "$")))
+  (is (= "f\\\\d" (s/replace "food" "o" "\\")))
+  (is (= "barbarbar" (s/replace "foobarfoo" #"foo" "bar")))
+  (is (= "foobarfoo" (s/replace "foobarfoo" #"baz" "bar")))
+  (is (= "f$$d" (s/replace "food" #"o" (s/re-quote-replacement "$"))))
+  (is (= "f\\\\d" (s/replace "food" #"o" (s/re-quote-replacement "\\"))))
+  (is (= "FOObarFOO" (s/replace "foobarfoo" #"foo" s/upper-case)))
+  (is (= "foobarfoo" (s/replace "foobarfoo" #"baz" s/upper-case)))
+  (is (= "OObarOO" (s/replace "foobarfoo" #"f(o+)" (fn [[m g1]] (s/upper-case g1)))))
+  (is (= "baz\\bang\\" (s/replace "bazslashbangslash" #"slash" (constantly "\\")))))
 
 (deftest t-replace-first
+  (is (= "faobar" (s/replace-first "foobar" \o \a)))
+  (is (= "foobar" (s/replace-first "foobar" \z \a)))
+  (is (= "z.ology" (s/replace-first "zoology" \o \.)))
   (is (= "barbarfoo" (s/replace-first "foobarfoo" "foo" "bar")))
+  (is (= "foobarfoo" (s/replace-first "foobarfoo" "baz" "bar")))
+  (is (= "f$od" (s/replace-first "food" "o" "$")))
+  (is (= "f\\od" (s/replace-first "food" "o" "\\")))
   (is (= "barbarfoo" (s/replace-first "foobarfoo" #"foo" "bar")))
-  (is (= "z.ology" (s/replace-first "zoology" \o \.)))
-  (is (= "FOObarfoo" (s/replace-first "foobarfoo" #"foo" s/upper-case))))
+  (is (= "foobarfoo" (s/replace-first "foobarfoo" #"baz" "bar")))
+  (is (= "f$od" (s/replace-first "food" #"o" (s/re-quote-replacement "$"))))
+  (is (= "f\\od" (s/replace-first "food" #"o" (s/re-quote-replacement "\\"))))
+  (is (= "FOObarfoo" (s/replace-first "foobarfoo" #"foo" s/upper-case)))
+  (is (= "foobarfoo" (s/replace-first "foobarfoo" #"baz" s/upper-case)))
+  (is (= "OObarfoo" (s/replace-first "foobarfoo" #"f(o+)" (fn [[m g1]] (s/upper-case g1)))))
+  (is (= "baz\\bangslash" (s/replace-first "bazslashbangslash" #"slash" (constantly "\\")))))
 
 (deftest t-join
   (are [x coll] (= x (s/join coll))
@@ -45,14 +67,17 @@
 
 (deftest t-triml
   (is (= "foo " (s/triml " foo ")))
-  (is (= "" (s/triml "   "))))
+  (is (= "" (s/triml "   ")))
+  (is (= "bar" (s/triml "\u2002 \tbar"))))
 
 (deftest t-trimr
   (is (= " foo" (s/trimr " foo ")))
-  (is (= "" (s/trimr "   "))))
+  (is (= "" (s/trimr "   ")))
+  (is (= "bar" (s/trimr "bar\t \u2002"))))
 
 (deftest t-trim
-  (is (= "foo" (s/trim "  foo  \r\n"))))
+  (is (= "foo" (s/trim "  foo  \r\n")))
+  (is (= "bar" (s/trim "\u2000bar\t \u2002"))))
 
 (deftest t-upper-case
   (is (= "FOOBAR" (s/upper-case "Foobar"))))
@@ -65,6 +90,7 @@
        s/reverse [nil]
        s/replace [nil #"foo" "bar"]
        s/replace-first [nil #"foo" "bar"]
+       s/re-quote-replacement [nil]
        s/capitalize [nil]
        s/upper-case [nil]
        s/lower-case [nil]
@@ -85,6 +111,7 @@
        "baz::quux" s/replace-first ["baz--quux" #"--" "::"]
        "baz::quux" s/replace-first ["baz--quux" (StringBuffer. "--") (StringBuffer. "::")]
        "zim-zam" s/replace-first ["zim zam" #" " (StringBuffer. "-")]
+       "\\\\ \\$" s/re-quote-replacement ["\\ $"]
        "Pow" s/capitalize ["POW"]
        "BOOM" s/upper-case ["boom"]
        "whimper" s/lower-case ["whimPER"]
diff --git a/test/clojure/test_clojure/test.clj b/test/clojure/test_clojure/test.clj
index ae30b06..2349550 100644
--- a/test/clojure/test_clojure/test.clj
+++ b/test/clojure/test_clojure/test.clj
@@ -16,7 +16,8 @@
 
 
 (ns clojure.test-clojure.test
-  (:use clojure.test))
+  (:use clojure.test)
+  (:require [clojure.stacktrace :as stack]))
 
 (deftest can-test-symbol
   (let [x true]
@@ -72,6 +73,13 @@
   (is (re-find #"ab" "abbabba") "Should pass")
   (is (re-find #"cd" "abbabba") "Should fail"))
 
+(deftest clj-1102-empty-stack-trace-should-not-throw-exceptions
+  (let [empty-stack (into-array (Class/forName "java.lang.StackTraceElement")
+                                [])
+        t (doto (Exception.) (.setStackTrace empty-stack))]
+    (is (map? (#'clojure.test/file-and-line t 0)) "Should pass")
+    (is (string? (with-out-str (stack/print-stack-trace t))) "Should pass")))
+
 (deftest #^{:has-meta true} can-add-metadata-to-tests
   (is (:has-meta (meta #'can-add-metadata-to-tests)) "Should pass"))
 
diff --git a/test/clojure/test_clojure/test_fixtures.clj b/test/clojure/test_clojure/test_fixtures.clj
index 9078578..a41b294 100644
--- a/test/clojure/test_clojure/test_fixtures.clj
+++ b/test/clojure/test_clojure/test_fixtures.clj
@@ -33,10 +33,15 @@
 (defn inc-n-fixture [f]
   (binding [*n* (inc *n*)] (f)))
 
+(def side-effects (atom 0))
+(defn side-effecting-fixture [f]
+  (swap! side-effects inc)
+  (f))
+
 (use-fixtures :once fixture-a fixture-b)
 
-(use-fixtures :each fixture-c fixture-d inc-n-fixture)
-(use-fixtures :each fixture-c fixture-d inc-n-fixture)
+(use-fixtures :each fixture-c fixture-d inc-n-fixture side-effecting-fixture)
+(use-fixtures :each fixture-c fixture-d inc-n-fixture side-effecting-fixture)
 
 (deftest can-use-once-fixtures
   (is (= 3 *a*))
@@ -47,4 +52,22 @@
   (is (= 11 *d*)))
 
 (deftest use-fixtures-replaces
-  (is (= *n* 1)))
\ No newline at end of file
+  (is (= *n* 1)))
+
+(deftest can-run-a-single-test-with-fixtures
+  ;; We have to use a side-effecting fixture to test that the fixtures are
+  ;; running, in order to distinguish fixtures run because of our call to
+  ;; test-vars below from the same fixtures running prior to this test
+  (let [side-effects-so-far @side-effects
+        reported (atom [])]
+    (binding [report (fn [m] (swap! reported conj (:type m)))]
+      (test-vars [#'can-use-each-fixtures]))
+    (is (= [:begin-test-var :pass :pass :end-test-var] @reported))
+    (is (= (inc side-effects-so-far) @side-effects))))
+
+(defn should-not-trigger-fixtures [])
+
+(deftest a-var-lacking-test-meta-should-not-trigger-fixtures
+  (let [side-effects-so-far @side-effects]
+    (test-vars [#'should-not-trigger-fixtures])
+    (is (= side-effects-so-far @side-effects))))
diff --git a/test/clojure/test_clojure/transients.clj b/test/clojure/test_clojure/transients.clj
index 1545c10..11c831d 100644
--- a/test/clojure/test_clojure/transients.clj
+++ b/test/clojure/test_clojure/transients.clj
@@ -23,4 +23,18 @@
     (is (= [0 {}] (let [ks (concat (range 7) [(hash-obj 42) (hash-obj 42)])
                         m (zipmap ks ks)
                         dm (persistent! (reduce dissoc! (transient m) (keys m)))]
-                    [(count dm) dm])))))
\ No newline at end of file
+                    [(count dm) dm])))))
+
+(deftest test-disj!
+  (testing "disjoin multiple items in one call"
+    (is (= #{5 20} (-> #{5 10 15 20} transient (disj! 10 15) persistent!)))))
+
+(deftest empty-transient
+  (is (= false (.contains (transient #{}) :bogus-key))))
+
+(deftest persistent-assoc-on-collision
+  (testing "Persistent assoc on a collision node which underwent a transient dissoc"
+    (let [a (reify Object (hashCode [_] 42))
+          b (reify Object (hashCode [_] 42))]
+      (is (= (-> #{a b} transient (disj! a) persistent! (conj a))
+            (-> #{a b} transient (disj! a) persistent! (conj a)))))))
diff --git a/test/clojure/test_clojure/try_catch.clj b/test/clojure/test_clojure/try_catch.clj
old mode 100755
new mode 100644
diff --git a/test/clojure/test_clojure/vars.clj b/test/clojure/test_clojure/vars.clj
index 8212657..b1d9e2d 100644
--- a/test/clojure/test_clojure/vars.clj
+++ b/test/clojure/test_clojure/vars.clj
@@ -89,3 +89,12 @@
         (throw (Exception. "simulated failure in with-redefs")))))
     (is (= :temp @p))
     (is (= :original stub-me))))
+
+(def ^:dynamic dynamic-var 1)
+
+(deftest test-with-redefs-inside-binding
+  (binding [dynamic-var 2]
+    (is (= 2 dynamic-var))
+    (with-redefs [dynamic-var 3]
+      (is (= 2 dynamic-var))))
+  (is (= 1 dynamic-var)))
\ No newline at end of file
diff --git a/test/clojure/test_clojure/vectors.clj b/test/clojure/test_clojure/vectors.clj
index 8d76e3a..1b293ec 100644
--- a/test/clojure/test_clojure/vectors.clj
+++ b/test/clojure/test_clojure/vectors.clj
@@ -79,6 +79,11 @@
            vs-32 vs
            vs nil))))
 
+(deftest test-primitive-subvector-reduce
+  ;; regression test for CLJ-1082
+  (is (== 60 (let [prim-vec (into (vector-of :long) (range 1000))]
+               (reduce + (subvec prim-vec 10 15))))))
+
 (deftest test-vec-compare
   (let [nums      (range 1 100)
         ; randomly replaces a single item with the given value
@@ -322,7 +327,9 @@
            (vector-of ""))))
   (testing "vector-like (vector-of :type x1 x2 x3 … xn)"
     (are [vec gvec] (and (instance? clojure.core.Vec gvec)
-                         (= (into (vector-of :int) vec) gvec))
+                         (= (into (vector-of :int) vec) gvec)
+                         (= vec gvec)
+                         (= (hash vec) (hash gvec)))
          [1] (vector-of :int 1)
          [1 2] (vector-of :int 1 2)
          [1 2 3] (vector-of :int 1 2 3)
@@ -353,6 +360,12 @@
            (vector-of :int 1 2 "3")
            (vector-of :int "1" "2" "3")))))
 
+(deftest empty-vector-equality
+  (let [colls [[] (vector-of :long) '()]]
+    (doseq [c1 colls, c2 colls]
+      (is (= c1 c2))
+      (is (.equals c1 c2)))))
+
 (defn =vec
   [expected v] (and (vector? v) (= expected v)))
 
@@ -370,3 +383,11 @@
   (are [r c1] (=vec r (filterv even? c1))
        [] [1 3 5]
        [2 4] [1 2 3 4 5]))
+
+(deftest test-subvec
+  (let [v1 (vec (range 100))
+        v2 (subvec v1 50 57)]
+    (is (thrown? IndexOutOfBoundsException (v2 -1)))
+    (is (thrown? IndexOutOfBoundsException (v2 7)))
+    (is (= (v1 50) (v2 0)))
+    (is (= (v1 56) (v2 6)))))

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/clojure.git



More information about the pkg-java-commits mailing list