Merge remote-tracking branch 'origin/master' into HEAD

BUG:67765277
diff --git a/.git.pre-commit b/.git.pre-commit
new file mode 100755
index 0000000..6f5361f
--- /dev/null
+++ b/.git.pre-commit
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+# This file will be used as .git/hooks/pre-commit .
+# However, it should be edited as .git.pre-commit .
+# You can install it by running: ant prep
+
+# Fail if any command fails
+set -e
+
+# "ant -e check-style" would check every file; on commit we only need to
+# check files that changed.
+# Need to keep checked files in sync with formatted.java.files in
+# build.xml. Otherwise `ant reformat` might not reformat a file that this
+# hook complains about.
+CHANGED_JAVA_FILES=`git diff --staged --name-only --diff-filter=ACM | grep '\.java$' | grep -v '/jdk/' | grep -v 'stubparser/' | grep -v '/eclipse/' ` || true
+# echo CHANGED_JAVA_FILES "'"${CHANGED_JAVA_FILES}"'"
+if [ ! -z "$CHANGED_JAVA_FILES" ]; then
+    (((cd .run-google-java-format && git pull -q) || git clone -q https://github.com/plume-lib/run-google-java-format.git .run-google-java-format) || true)
+    ## For debugging:
+    # echo "CHANGED_JAVA_FILES: ${CHANGED_JAVA_FILES}"
+    python .run-google-java-format/check-google-java-format.py --aosp ${CHANGED_JAVA_FILES}
+fi
+
+# This is to handle non-.java files, since the above already handled .java files.
+# May need to remove files that are allowed to have trailing whitespace.
+CHANGED_STYLE_FILES=`git diff --staged --name-only --diff-filter=ACM | grep -v '.png$' | grep -v '.xcf$'` || true
+if [ ! -z "$CHANGED_STYLE_FILES" ]; then
+    # echo "CHANGED_STYLE_FILES: ${CHANGED_STYLE_FILES}"
+    grep -q '[[:blank:]]$' ${CHANGED_STYLE_FILES} 2>&1 && (echo "Some files have trailing whitespace:" && grep -l '[[:blank:]]$' ${CHANGED_STYLE_FILES} && exit 1)
+fi
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aeb94e7
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,71 @@
+bin
+# *.class
+*_Generated.class
+*_temp.class
+*~
+TAGS
+
+annotation-file-utilities/annotation-file-format.aux
+annotation-file-utilities/annotation-file-format.blg
+annotation-file-utilities/annotation-file-format.dvi
+annotation-file-utilities/annotation-file-format.haux
+annotation-file-utilities/annotation-file-format.html
+annotation-file-utilities/annotation-file-format.htoc
+annotation-file-utilities/annotation-file-format.log
+annotation-file-utilities/annotation-file-format.out
+annotation-file-utilities/annotation-file-format.pdf
+annotation-file-utilities/annotation-file-format.ps
+annotation-file-utilities/annotation-file-format.toc
+#annotation-file-utilities/annotation-file-utilities.jar
+annotation-file-utilities/annotation-tools.zip
+annotation-file-utilities/design.aux
+annotation-file-utilities/design.log
+annotation-file-utilities/design.pdf
+
+annotation-file-utilities/javadoc
+
+annotation-file-utilities/tests/abbreviated/*.diff
+annotation-file-utilities/tests/abbreviated/*.log
+annotation-file-utilities/tests/abbreviated/*.output
+annotation-file-utilities/tests/ad-hoc/bridge/A.class
+annotation-file-utilities/tests/ad-hoc/bridge/C.class
+annotation-file-utilities/tests/ad-hoc/bridge/C.jaif
+annotation-file-utilities/tests/source-extension/*.diff
+annotation-file-utilities/tests/source-extension/*.log
+annotation-file-utilities/tests/source-extension/*.output
+annotation-file-utilities/tests/Makefile.user
+annotation-file-utilities/tests/system-test/out1
+annotation-file-utilities/tests/system-test/out3
+annotation-file-utilities/tests/system-test/out4.class
+annotation-file-utilities/tests/system-test/out2.diff
+annotation-file-utilities/tests/system-test/out2.jaif
+annotation-file-utilities/tests/system-test/out5.diff
+annotation-file-utilities/tests/system-test/out5.jaif
+annotation-file-utilities/tests/*.diff
+annotation-file-utilities/tests/*.log
+annotation-file-utilities/tests/*.output
+
+asmx/output
+
+scene-lib/javadoc
+scene-lib/reports
+scene-lib/test/annotations/tests/executable/example-output.jaif
+scene-lib/test/annotations/tests/executable/example-stdout.jaif
+
+*.class-via-classfile-scene.txt
+*.class-correct-scene.txt
+*.class-from-indexfile.txt
+*.class-generated-scene.txt
+
+.metadata
+
+annotation-file-utilities/figures/*.eps
+annotation-file-utilities/figures/*.pdf
+annotation-file-utilities/figures/*.png
+annotation-file-utilities/*.png
+
+.run-google-java-format
+
+# User customizations
+Makefile.user
+build.properties
diff --git a/.travis-build-without-test.sh b/.travis-build-without-test.sh
new file mode 100755
index 0000000..26f89c6
--- /dev/null
+++ b/.travis-build-without-test.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+ROOT=$TRAVIS_BUILD_DIR/..
+
+# Fail the whole script if any command fails
+set -e
+
+export SHELLOPTS
+
+SLUGOWNER=${TRAVIS_REPO_SLUG%/*}
+if [[ "$SLUGOWNER" == "" ]]; then
+  SLUGOWNER=typetools
+fi
+
+
+# jsr308-langtools
+if [ -d ../jsr308-langtools ] ; then
+    (cd ../jsr308-langtools && hg pull && hg update)
+else
+    set +e
+    echo "Running: hg identify https://bitbucket.org/${SLUGOWNER}/jsr308-langtools &>-"
+    hg identify https://bitbucket.org/${SLUGOWNER}/jsr308-langtools &>-
+    if [ "$?" -ne 0 ]; then
+        SLUGOWNER=typetools
+    fi
+    set -e
+    echo "Running:  (cd .. && hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools)"
+    (cd .. && (hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools || hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools))
+    echo "... done: (cd .. && hg clone https://bitbucket.org/${SLUGOWNER}/jsr308-langtools)"
+fi
+(cd ../jsr308-langtools/ && ./.travis-build-without-test.sh)
+
+## Compile
+echo "running \"ant compile\" for annotation-tools"
+ant compile
diff --git a/.travis-build.sh b/.travis-build.sh
new file mode 100755
index 0000000..d1d5c91
--- /dev/null
+++ b/.travis-build.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+# Fail the whole script if any command fails
+set -e
+
+export SHELLOPTS
+
+./.travis-build-without-test.sh
+
+ant all
+
+ant check-style
+
+# TODO: when codebase is reformatted
+# ant check-format
+
+# TODO: When we require Java 8 for compilation:
+# ant html-validate
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..c7c09e5
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,24 @@
+language: java
+
+script: ./.travis-build.sh
+
+sudo: false
+
+jdk:
+  - oraclejdk8
+
+jobs:
+  include:
+    - stage: trigger-downstream
+      jdk: oraclejdk8
+      script: |
+        echo "TRAVIS_BRANCH=$TRAVIS_BRANCH TRAVIS_PULL_REQUEST=$TRAVIS_PULL_REQUEST"
+        if [[ ($TRAVIS_BRANCH == master) &&
+              ($TRAVIS_PULL_REQUEST == false) ]] ; then
+          curl -LO https://raw.github.com/mernst/plume-lib/master/bin/trigger-travis.sh
+          SLUGOWNER=${TRAVIS_REPO_SLUG%/*}
+          sh trigger-travis.sh ${SLUGOWNER} checker-framework $TRAVISTOKEN
+        fi
+
+git:
+  depth: 9
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..89de354
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..89de354
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..8ba6c6e
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,16 @@
+name: "annotation_tools"
+description:
+    "Annotation File Utilities, used for inserting external annotations to source files"
+
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://github.com/typetools/annotation-tools"
+  }
+  url {
+    type: ARCHIVE
+    value: "https://checkerframework.org/annotation-file-utilities/annotation-tools-3.6.45.zip"
+  }
+  version: "3.6.45"
+  last_upgrade_date { year: 2017 month: 9 day: 28 }
+}
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..cfdec88
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,3 @@
+pszczepaniak@google.com
+narayan@google.com
+
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..9095538
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,38 @@
+Annotation Tools README file
+----------------------------
+
+This directory contains the Annotation Tools.
+When distributed, this is known as the Annotation File Utilities, which is
+one of its components; see the annotation-file-utilities subdirectory.
+
+The Annotation File Utilities homepage is:
+  https://checkerframework.org/annotation-file-utilities/
+and it also appears in this directory as:
+  annotation-file-utilities/annotation-file-utilities.html
+
+
+asmx
+----
+
+asmx contains modifications to asm to allow it to read and write JSR
+308 annotations to/from class files.
+
+The most modified classes are (in org.objectweb.asm):
+ * ClassReader
+ * ClassWriter
+ * ExtendedAnnotationVisitor
+ * ExtendedAnnotationWriter
+
+Most of the changes are delimited by
+//jaime
+// end jaime
+
+asmx was branched off of asm 2.2.2, available at either of these locations:
+  http://forge.objectweb.org/project/download.php?group_id=23&file_id=5769
+  http://download.forge.objectweb.org/asm/asm-2.2.2.tar.gz
+To see the changes, diff the current source files against the 2.2.2
+files.
+
+The diffs are complicated by the fact that in a few cases, a post-2.2.2
+version of a file was added to asmx.
+One example is file src/org/objectweb/asm/optimizer/shrink.properties.
diff --git a/README.version b/README.version
new file mode 100644
index 0000000..49ead1b
--- /dev/null
+++ b/README.version
@@ -0,0 +1,11 @@
+URL: https://github.com/typetools/annotation-tools
+Version: 3.6.45
+License: MIT
+Description: Annotation File Utilities, used for inserting external annotations to source files
+
+Local Modifications:
+    Check-in annotation-file-utilities/annotation-file-utilities.jar from
+    https://checkerframework.org/annotation-file-utilities/annotation-tools-3.6.45.zip
+    Remove annotation-file-utilities/annotation-file-utilities.jar from .gitignore yet.
+
+
diff --git a/annotation-file-utilities/.classpath b/annotation-file-utilities/.classpath
new file mode 100644
index 0000000..a5bc5ce
--- /dev/null
+++ b/annotation-file-utilities/.classpath
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry combineaccessrules="false" kind="src" path="/jsr308-langtools"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/asmx"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/scene-lib"/>
+	<classpathentry kind="lib" path="lib/guava-20.0.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+        <classpathentry combineaccessrules="false" kind="src" path="/plume-lib"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/annotation-file-utilities/.project b/annotation-file-utilities/.project
new file mode 100644
index 0000000..e028bcb
--- /dev/null
+++ b/annotation-file-utilities/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>annotation-file-utilities</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/annotation-file-utilities/README.txt b/annotation-file-utilities/README.txt
new file mode 100644
index 0000000..30a9741
--- /dev/null
+++ b/annotation-file-utilities/README.txt
@@ -0,0 +1,66 @@
+Annotation File Utilities README file
+
+For user documentation, see file annotation-file-utilities.html .
+
+===========================================================================
+
+Contents
+
+The contents of this directory are:
+
+annotation-file-utilities.html
+  Annotation File Utilities documentation.
+  Most users should only have to read this file.
+annotation-file-format.{html,pdf}
+  Describes the annotation file format.
+scripts/
+  Contains Unix and Windows programs for transferring annotations among
+  Java, class, and annotation files.
+annotation-file-utilities.jar
+  Java library used by the programs.
+build.xml, src/, lib/, tests/
+  For developers only:  buildfile, source code, libraries, tests.
+
+===========================================================================
+
+Notes
+
+To build annotation-file-format.{html,pdf} your BIBINPUTS environment
+variable must be set like so:
+
+export BIBINPUTS=.:/path/to/plume/bib
+
+plume-bib is available at https://github.com/mernst/plume-bib .
+
+===========================================================================
+
+Making a release
+
+To make a release (a distribution):
+
+Be sure to run these instructions in an account that uses JDK 7, not JDK 8.
+
+Write a description of the most significant changes in:
+    changelog.html
+It may be helpful to examine the changes since the last release:
+  git log v3.5.3..
+  git diff v3.5.3..
+
+Manually update the version number and date in the following places:
+    annotation-file-utilities.html  (in "Installation" section)
+    changelog.html
+    annotations/scene-lib/src/annotations/io/classfile/ClassFileReader.java
+
+Create and post to the web the entire distribution:
+  ant -e web
+
+Run
+  checklink -q -r https://checkerframework.org/annotation-file-utilities/
+and if there are any problems, re-make the distribution.
+
+Tag the release, for example:
+  git tag v3.5.3
+
+Send email to: checker-framework-discuss@googlegroups.com
+
+===========================================================================
diff --git a/annotation-file-utilities/annotation-file-format.bbl b/annotation-file-utilities/annotation-file-format.bbl
new file mode 100644
index 0000000..6589adc
--- /dev/null
+++ b/annotation-file-utilities/annotation-file-format.bbl
@@ -0,0 +1,26 @@
+\newcommand{\etalchar}[1]{$^{#1}$}
+\begin{thebibliography}{DDE{\etalchar{+}}11}
+
+\bibitem[Che]{CF}
+{Checker Framework} website.
+\newblock \url{https://checkerframework.org/}.
+
+\bibitem[DDE{\etalchar{+}}11]{DietlDEMS2011}
+Werner Dietl, Stephanie Dietzel, Michael~D. Ernst, K{\i}van{\c{c}} Mu{\c{s}}lu,
+  and Todd Schiller.
+\newblock Building and using pluggable type-checkers.
+\newblock In {\em ICSE 2011, Proceedings of the 33rd International Conference
+  on Software Engineering}, pages 681--690, Waikiki, Hawaii, USA, May 2011.
+
+\bibitem[Ern13]{JSR308-webpage-201310}
+Michael~D. Ernst.
+\newblock {Type Annotations} specification ({JSR} 308).
+\newblock \url{https://checkerframework.org/jsr308/}, October 2013.
+
+\bibitem[LBR06]{LeavensBR2006:JML}
+Gary~T. Leavens, Albert~L. Baker, and Clyde Ruby.
+\newblock Preliminary design of {JML}: A behavioral interface specification
+  language for {Java}.
+\newblock {\em ACM SIGSOFT Software Engineering Notes}, 31(3), March 2006.
+
+\end{thebibliography}
diff --git a/annotation-file-utilities/annotation-file-format.bib b/annotation-file-utilities/annotation-file-format.bib
new file mode 100644
index 0000000..cfcba2f
--- /dev/null
+++ b/annotation-file-utilities/annotation-file-format.bib
@@ -0,0 +1,11 @@
+@Misc{ASM,
+  key = "ASM website",
+  title =    "{ASM} website",
+  howpublished = "\url{http://asm.ow2.org/}",
+}
+
+@Misc{CF,
+  key = "Checker Framework website",
+  title =    "{Checker Framework} website",
+  howpublished = "\url{https://checkerframework.org/}",
+}
diff --git a/annotation-file-utilities/annotation-file-format.tex b/annotation-file-utilities/annotation-file-format.tex
new file mode 100644
index 0000000..5825722
--- /dev/null
+++ b/annotation-file-utilities/annotation-file-format.tex
@@ -0,0 +1,1467 @@
+\documentclass{article}
+\usepackage{fancyvrb}
+\usepackage{graphicx}
+\usepackage{fullpage}
+\usepackage{relsize}
+\usepackage{url}
+\usepackage{hevea}
+\usepackage[shortcuts]{extdash}
+\usepackage{textcomp}
+% \usepackage{verbdef}
+
+\def\topfraction{.9}
+\def\dbltopfraction{\topfraction}
+\def\floatpagefraction{\topfraction}     % default .5
+\def\dblfloatpagefraction{\topfraction}  % default .5
+\def\textfraction{.1}
+
+%HEVEA \footerfalse    % Disable hevea advertisement in footer
+
+\newcommand{\code}[1]{\ifmmode{\mbox{\relax\ttfamily{#1}}}\else{\relax\ttfamily #1}\fi}
+%% Hevea version omits "\smaller"
+%HEVEA \renewcommand{\code}[1]{\ifmmode{\mbox{\ttfamily{#1}}}\else{\ttfamily #1}\fi}
+
+\newcommand{\includeimage}[2]{
+\begin{center}
+\ifhevea\imgsrc{#1.png}\else
+\resizebox{!}{#2}{\includegraphics{figures/#1}}
+\vspace{-1.5\baselineskip}
+\fi
+\end{center}}
+
+% Add line between figure and text
+\makeatletter
+\def\topfigrule{\kern3\p@ \hrule \kern -3.4\p@} % the \hrule is .4pt high
+\def\botfigrule{\kern-3\p@ \hrule \kern 2.6\p@} % the \hrule is .4pt high
+\def\dblfigrule{\kern3\p@ \hrule \kern -3.4\p@} % the \hrule is .4pt high
+\makeatother
+
+
+\title{Annotation File Format Specification}
+% Hevea ignores \date, so move the date into \author
+\author{\url{https://checkerframework.org/annotation-file-utilities/} \\
+\today}
+\date{}
+
+\begin{document}
+
+\maketitle
+
+%HEVEA \setcounter{tocdepth}{2}
+\tableofcontents
+
+\section{Purpose:  External storage of annotations\label{purpose}}
+
+Java annotations are meta-data about Java program elements, as in
+``\code{@Deprecated class Date \{ \ldots\ \}}''.
+Ordinarily, Java annotations are written in the source code of a
+\code{.java} Java source file.  When \code{javac} compiles the source code,
+it inserts the annotations in the resulting \code{.class} file (as
+``attributes'').
+
+Sometimes, it is convenient to specify the annotations outside the source
+code or the \code{.class} file.
+\begin{itemize}
+%BEGIN LATEX
+\itemsep 0pt \parskip 0pt
+%END LATEX
+\item
+  When source code is not available, a textual file provides a format for
+  writing and storing annotations that is much easier to read and modify
+  than a \code{.class} file.  Even if the eventual purpose is to insert the
+  annotations in the \code{.class} file, the annotations must be specified
+  in some textual format first.
+\item
+  Even when source code is available, sometimes it should not be changed,
+  yet annotations must be stored somewhere for use by tools.
+\item
+  A textual file for annotations can eliminate code clutter.  A developer
+  performing some specialized task (such as code verification,
+  parallelization, etc.)\ can store annotations in an annotation file without
+  changing the main version of the source code.  (The developer's private
+  version of the code could contain the annotations, but the developer
+  could move them to the separate file before committing changes.)
+\item
+  Tool writers may find it more convenient to use a textual file, rather
+  than writing a Java or \code{.class} file parser.
+\item
+  When debugging annotation-processing tools, a textual file format
+  (extracted from the Java or \code{.class} files) is easier to read and
+  is easier for use in testing.
+\end{itemize}
+
+All of these uses require an external, textual file format for Java annotations.
+The external file format should be easy for people to create, read, and
+modify.
+%
+An ``annotation file'' serves this purpose by specifying a set of
+Java annotations.
+The Annotation File Utilities
+(\url{https://checkerframework.org/annotation-file-utilities/}) are a set
+of tools that process annotation files.
+
+The file format discussed in this document supports both standard Java SE 5
+declaration annotations and also the type annotations that are introduced by Java SE 8.
+The file format provides a simple syntax to represent the structure of a Java
+program. For annotations in method bodies of \code{.class} files the annotation
+file closely follows
+section ``Class File Format Extensions'' of the JSR 308 design document~\cite{JSR308-webpage-201310},
+which explains how the annotations are stored in the \code{.class}
+file.
+In that sense, the current design is extremely low-level, and users
+probably would not want to write the files by hand (but they might fill in a
+template that a tool generated automatically).  As future work, we should
+design a more
+user-friendly format that permits Java signatures to be directly specified.
+For \code{.java} source files, the file format provides a separate, higher-level
+syntax for annotations in method bodies.
+
+
+
+%% I don't like this, as it may force distributing logically connected
+%% elements all over a file system.  Users should be permitted, but not
+%% forced, to adopt such a file structure. -MDE
+%   Each file corresponds to exactly one
+% ``.class'' file, so (for instance) inner classes are written in
+% separate annotation files, named in the same ``{\tt
+% OuterClass\$InnerClass}'' pattern as the ``.class'' file.
+
+
+By convention, an annotation file ends with ``\code{.jaif}'' (for ``Java
+annotation index file''), but this is not required.
+
+
+% \verbdef\lineend|"\n"|
+
+%BEGIN LATEX
+\DefineShortVerb{\|}
+\SaveVerb{newline}|\n|
+\UndefineShortVerb{\|}
+\newcommand{\lineend}{\bnflit{\UseVerb{newline}}}
+%END LATEX
+%HEVEA \newcommand{\bs}{\char"5C}
+%HEVEA \newcommand{\lineend}{\bnflit{\bs{}n}}
+
+% literal
+\newcommand{\bnflit}[1]{\textrm{``}\textbf{#1}\textrm{''}}
+% non-terminal
+\newcommand{\bnfnt}[1]{\textsf{\emph{#1}}}
+% comment
+\newcommand{\bnfcmt}{\rm \# }
+% alternative
+\newcommand{\bnfor}{\ensuremath{|}}
+
+
+\section{Grammar\label{grammar}}
+
+This section describes the annotation file format in detail by presenting it in
+the form of a grammar. Section~\ref{grammar-conventions} details the conventions
+of the grammar. Section~\ref{java-file-grammar} shows how to represent the
+basic structure of a Java program (classes, methods, etc.) in an annotation
+file. Section~\ref{annotations-grammar} shows how to add annotations to an
+annotation file.
+
+\subsection{Grammar conventions\label{grammar-conventions}}
+
+Throughout this document, ``name'' is any valid Java simple name or
+binary name, ``type'' is any valid type, and ``value'' is any
+valid Java constant, and quoted strings are literal values.
+%
+The Kleene qualifiers ``*'' (zero or more), ``?'' (zero or one), and ``+''
+(one or more) denote plurality of a grammar element.
+%
+A vertical bar (``\bnfor'') separates alternatives.
+Parentheses (``()'') denote grouping, and square brackets (``[]'')
+denote optional syntax, which is equivalent to ``( \ldots\ )\ ?''\ but more concise.
+We use the hash/pound/octothorpe symbol (``\#'') for comments within the grammar.
+
+In the annotation file,
+besides its use as token separator,
+whitespace (excluding
+newlines) is optional with one exception: no space is permitted
+between an ``@'' character and a subsequent name. Indentation is
+ignored, but is encouraged to maintain readability of the hierarchy of
+program elements in the class (see the example in Section~\ref{example}).
+
+Comments can be written throughout the annotation file using the double-slash
+syntax employed by Java for single-line comments: anything following
+two adjacent slashes (``//'') until the first newline is a comment.
+This is omitted from the grammar for simplicity.
+Block comments (``/* \ldots\ */'') are not allowed.
+
+The line end symbol \lineend{} is used for all the different line end
+conventions, that is, Windows- and Unix-style newlines are supported.
+
+
+\subsection{Java file grammar\label{java-file-grammar}}
+
+This section shows how to represent the basic structure of a Java program
+(classes, methods, etc.) in an annotation file. For Java elements that can
+contain annotations, this section will reference grammar productions contained
+in Section~\ref{annotations-grammar}, which describes how annotations are used
+in an annotation file.
+
+An annotation file has the same basic structure as a Java program. That is,
+there are packages, classes, fields and methods.
+
+The annotation file may omit certain program elements --- for instance, it
+may mention only some of the packages in a program, or only some of the
+classes in a package, or only some of the fields or methods of a class.
+Program elements that do not appear in the annotation file are treated as
+unannotated.
+
+
+\subsubsection{Package definitions\label{package-definitions}}
+
+At the root of an annotation file is one or more package definitions.
+A package definition describes a package containing a list of annotation
+definitions and classes.  A package definition also contains any
+annotations on the package (such as those from a
+\code{package-info.java} file).
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{annotation-file} ::= \\
+\qquad    \bnfnt{package-definition}+ \\
+\\
+\bnfnt{package-definition} ::= \\
+\qquad    \bnflit{package} ( \bnflit{:} ) \bnfor{} ( \bnfnt{name} \bnflit{:} \bnfnt{decl-annotation}* ) \lineend \\
+\qquad    ( \bnfnt{annotation-definition} \bnfor{} \bnfnt{class-definition} ) *
+\end{tabbing}
+
+\noindent
+Use a package line of \code{package:} for the default package. Note that
+annotations on the default package are not allowed.
+
+
+\subsubsection{Class definitions\label{class-definitions}}
+
+A class definition describes the annotations present on a class declaration,
+as well fields and methods of the class. It is organized according to
+the hierarchy of fields and methods in the class.
+Note that we use \bnfnt{class-definition} also for interfaces, enums, and
+annotation types (to specify annotations in an existing annotation type, not to
+be confused with \bnfnt{annotation-definition}s described in
+Section~\ref{annotation-definitions}, which defines annotations to be used
+throughout an annotation file); for syntactic simplicity, we use \bnflit{class}
+for
+all such definitions.
+% TODO: add test cases for this.
+
+Inner classes are treated as ordinary classes whose names happen to contain
+\code{\$} signs and must be defined at the top level of a class definition file.
+(To change this, the grammar would have to be extended with a closing
+delimiter for classes; otherwise, it would be ambiguous whether a
+field or method appearing after an inner class definition belonged to the
+inner class or the outer class.) The syntax for inner class names is the same as
+is used by the \code{javac} compiler. A good way to get an idea of the inner
+class names for a class is to compile the class and look at the filenames of the
+\code{.class} files that are produced.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{class-definition} ::= \\
+\qquad    \bnflit{class} \bnfnt{name} \bnflit{:} \bnfnt{decl-annotation}* \lineend  \\
+% TODO: is the order really important? eg. can fields and methods not
+% be mixed?
+\qquad        \bnfnt{typeparam-definition}* \\
+\qquad        \bnfnt{typeparam-bound}* \\
+\qquad        \bnfnt{extends}* \\
+\qquad        \bnfnt{implements}* \\
+\qquad        \bnfnt{field-definition}*  \\
+\qquad        \bnfnt{staticinit}* \\
+\qquad        \bnfnt{instanceinit}* \\
+\qquad        \bnfnt{method-definition}*
+\end{tabbing}
+
+\noindent
+Annotations on the \bnflit{class} line are annotations on the class declaration,
+not the class name.
+
+
+\paragraph{Type parameter definitions}
+
+The \bnfnt{typeparam-definition} production defines annotations on the
+declaration of a type parameter, such as on \code{K} and \code{T} in
+
+\begin{verbatim}
+public class Class<K> {
+    public <T> void m() {
+        ...
+    }
+}
+\end{verbatim}
+
+or on the type parameters on the left-hand side of a member reference,
+as on \code{String} in \code{List<String>::size}.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{typeparam-definition} ::= \\
+\qquad    \bnfcmt The integer is the zero-based type parameter index. \\
+\qquad    \bnflit{typeparam} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend \\
+\qquad    \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\paragraph{Type Parameter Bounds}
+
+The \bnfnt{typeparam-bound} production defines annotations on a bound of a
+type variable declaration, such as on \code{Number} and \code{Date} in
+
+\begin{verbatim}
+public class Class<K extends Number> {
+    public <T extends Date> void m() {
+        ...
+    }
+}
+\end{verbatim}
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{typeparam-bound} ::= \\
+% The bound should really be a sub-element of the typeparam!
+\qquad    \bnfcmt The integers are respectively the parameter and bound indexes of \\
+\qquad    \bnfcmt the type parameter bound~\cite{JSR308-webpage-201310}. \\
+\qquad    \bnflit{bound} \bnfnt{integer} \bnflit{\&} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend \\
+\qquad    \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\paragraph{Implements and extends}
+
+The \bnfnt{extends} and \bnfnt{implements} productions
+define annotations on the names of classes a class \code{extends} or
+\code{implements}.
+(Note: For interface declarations, \bnfnt{implements} rather than
+\bnfnt{extends} defines annotations on the names of extended
+interfaces.)
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{extends} ::= \\
+\qquad    \bnflit{extends} \bnflit{:} \bnfnt{type-annotation}* \lineend  \\
+\qquad        \bnfnt{compound-type}* \\
+\\
+\bnfnt{implements} ::= \\
+\qquad    \bnfcmt The integer is the zero-based index of the implemented interface. \\
+\qquad    \bnflit{implements} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend  \\
+\qquad        \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\paragraph{Static and instance initializers}
+
+The \bnfnt{staticinit} and \bnfnt{instanceinit} productions
+define annotations on code within static or instance initializer blocks.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{staticinit} ::= \\
+\qquad    \bnfcmt The integer is the zero-based index of the implemented interface. \\
+\qquad    \bnflit{staticinit} \bnflit{*} \bnfnt{integer} \bnflit{:} \lineend  \\
+\qquad        \bnfnt{compound-type}*
+\\
+\bnfnt{instanceinit} ::= \\
+\qquad    \bnfcmt The integer is the zero-based index of the implemented interface. \\
+\qquad    \bnflit{instanceinit} \bnflit{*} \bnfnt{integer} \bnflit{:} \lineend  \\
+\qquad        \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\subsubsection{Field definitions\label{field-definitons}}
+
+A field definition can have annotations on the declaration, the type of the
+field, or --- if in source code --- the field's initialization.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{field-definition} ::= \\
+\qquad    \bnflit{field} \bnfnt{name} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\
+\qquad        \bnfnt{type-annotations}* \\
+\qquad        \bnfnt{expression-annotations}*
+\end{tabbing}
+
+\noindent
+Annotations on the \bnflit{field} line are on the field declaration, not the
+type of the field.
+
+The \bnfnt{expression-annotations} production specifies annotations on the
+initialization expression of a field. If a field is initialized at declaration
+then in bytecode the initialization is moved to the constructor when the class
+is compiled. Therefore for bytecode, annotations on the initialization
+expression go in the constructor (see Section~\ref{method-definitions}), rather
+than the field definition. Source code annotations for the field initialization
+expression are valid on the field definition.
+
+
+\subsubsection{Method definitions\label{method-definitions}}
+
+A method definition can have annotations on the method declaration, in the
+method header (return type, parameters, etc.), as well as the method body.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{method-definition} ::= \\
+\qquad    \bnflit{method} \bnfnt{method-key} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\
+\qquad        \bnfnt{typeparam-definition}* \\
+\qquad        \bnfnt{typeparam-bound}* \\
+\qquad        \bnfnt{return-type}? \\
+\qquad        \bnfnt{receiver-definition}? \\
+\qquad        \bnfnt{parameter-definition}* \\
+% TODO: method throws
+\qquad        \bnfnt{variable-definition}* \\
+\qquad        \bnfnt{expression-annotations}*
+\end{tabbing}
+
+\noindent
+The annotations on the \bnflit{method} line are on the method declaration, not
+on the return value. The \bnfnt{method-key} consists of the name followed by the
+signature in JVML format. For example, the following method
+
+\begin{verbatim}
+boolean foo(int[] i, String s) {
+  ...
+}
+\end{verbatim}
+
+\noindent
+has the \bnfnt{method-key}:
+
+\begin{verbatim}
+foo([ILjava/lang/String;)Z
+\end{verbatim}
+
+Note that the
+signature is the erased signature of the method and does not contain generic
+type information, but does contain the return type. Using \code{javap -s} makes
+it easy to find the signature. The method keys ``\code{<init>}'' and
+``\code{<clinit>}'' are used to name instance (constructor) and class (static)
+initialization methods.  (The name of the constructor---that is, the final
+element of the class name---can be used in place of ``\code{<init>}''.)
+For both instance and class initializers, the ``return type'' part of the
+signature should be \code{V} (for \code{void}).
+
+% TODO: exception types in catch clause
+% TODO: .class literals
+% TODO: type arguments in constructor and method calls
+
+
+\paragraph{Return type}
+
+A return type defines the annotations on the return type of a method
+declaration.  It is also used for the result of a constructor.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{return-type} ::=  \\
+\qquad    \bnflit{return:} \bnfnt{type-annotation}* \lineend \\
+\qquad        \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\paragraph{Receiver definition}
+
+A receiver definition defines the annotations on the type of the receiver
+parameter in a method declaration.  A method receiver is the implicit formal
+parameter, \code{this}, used in non-static methods.  For source code insertion,
+the receiver parameter will be inserted if it does not already exist.
+
+Only inner classes have a receiver.  A top-level constructor does not have
+a receiver, though it does have a result.  The type of a constructor result
+is represented as a return type.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{receiver-definition} ::=  \\
+\qquad    \bnflit{receiver:} \bnfnt{type-annotation}* \lineend \\
+\qquad    \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\paragraph{Parameter definition}
+
+A formal parameter definition defines the annotations on a method formal
+parameter declaration and the type of a method formal parameter, but
+\emph{not} the receiver formal parameter.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{parameter-definition} ::= \\
+\qquad    \bnfcmt The integer is the zero-based index of the formal parameter in the method. \\
+\qquad    \bnflit{parameter} \bnfnt{integer} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\
+\qquad    \bnfnt{type-annotations}*
+\end{tabbing}
+
+\noindent
+The annotations on the \bnflit{parameter} line are on the formal parameter
+declaration, not on the type of the parameter. A parameter index of 0 is the
+first formal parameter. The receiver parameter is not index 0. Use the
+\bnfnt{receiver-definition} production to annotate the receiver parameter.
+
+
+\subsection{Bytecode Locations\label{bytecode-locations}}
+
+Certain elements in the body of a method or the initialization expression of a
+field can be annotated. The \bnfnt{expression-annotations} rule describes the
+annotations that can be added to a method body or a field initialization
+expression:
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{expression-annotations} ::= \\
+\qquad    \bnfnt{typecast}* \\
+\qquad    \bnfnt{instanceof}* \\
+\qquad    \bnfnt{new}* \\
+\qquad    \bnfnt{call}* \\
+\qquad    \bnfnt{reference}* \\
+\qquad    \bnfnt{lambda}* \\
+\qquad    \bnfnt{source-insert-typecast}* \\
+\qquad    \bnfnt{source-insert-annotation}*
+\end{tabbing}
+
+\noindent
+Additionally, a variable declaration in a method body can be annotated with the
+\bnfnt{variable-definition} rule, which appears below.
+
+Because of the differences between Java source code and \code{.class} files,
+the syntax for specifying code locations is different for \code{.class} files
+and source code. For \code{.class} files we use a syntax called ``bytecode
+offsets''. For source code we use a different syntax called ``source code
+indexes''. These are both described below.
+
+If you wish to be able to insert a given code annotation in both a \code{.class} file and a source
+code file, the annotation file must redundantly specify the annotation's bytecode offset and source
+code index. This can be done in a single \code{.jaif} file or two separate
+\code{.jaif} files. It is not necessary to include
+redundant information to insert annotations on signatures in both \code{.class}
+files and source code.
+
+Additionally, a new typecast with annotations (rather than an annotation added to an
+existing typecast) can be inserted into source code. This uses a third
+syntax that is described below under ``AST paths''.
+A second way to insert a typecast is by specifying just an annotation, not
+a full typecast (\code{insert-annotation} instead of
+\code{insert-typecast}).  In this case, the source annotation insertion
+tool generates a full typecast if Java syntax requires one.
+
+
+\subsubsection{Bytecode offsets\label{bytecode-offsets}}
+
+For locations in bytecode, the
+annotation file uses offsets into the bytecode array of the class file to
+indicate the specific expression to which the annotation refers.  Because
+different compilation strategies yield different \code{.class} files, a
+tool that maps such annotations from an annotation file into source code must
+have access to the specific \code{.class} file that was used to generate
+the annotation file.   The
+\code{javap -v} command is an effective technique to discover bytecode
+offsets.  Non-expression annotations such as those on methods,
+fields, classes, etc., do not use a bytecode offset.
+
+
+\subsubsection{Source code indexes\label{source-code-indexes}}
+
+For locations in source code, the annotation file indicates the kind of
+expression, plus a zero-based index to indicate which occurrence of that kind of
+expression. For example,
+
+\begin{verbatim}
+public void method() {
+    Object o1 = new @A String();
+    String s = (@B String) o1;
+    Object o2 = new @C Integer(0);
+    Integer i = (@D Integer) o2;
+}
+\end{verbatim}
+
+\noindent
+\code{@A} is on new, index 0. \code{@B} is on typecast, index 0. \code{@C} is on
+new, index 1. \code{@D} is on typecast, index 1.
+
+Source code indexes only include occurrences in the class that exactly matches
+the name of the enclosing \bnfnt{class-definition} rule. Specifically,
+occurrences in nested classes are not included. Use a new
+\bnfnt{class-definition} rule with the name of the nested class for source code
+insertions in a nested class.
+
+
+\subsubsection{Code locations grammar\label{code-grammar}}
+
+For each kind of expression, the grammar contains a separate location rule.
+This location rule contains the bytecode offset syntax followed by the
+source code index syntax.
+
+The grammar uses \bnflit{\#} for bytecode offsets and \bnflit{*} for source code indexes.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{variable-location} ::= \\
+\qquad    \bnfcmt Bytecode offset: the integers are respectively the index, start, and length \\
+\qquad    \bnfcmt fields of the annotations on this variable~\cite{JSR308-webpage-201310}. \\
+\qquad    (\bnfnt{integer} \bnflit{\#} \bnfnt{integer} \bnflit{+} \bnfnt{integer}) \\
+\qquad    \bnfcmt Source code index: the \bnfnt{name} is the identifier of the local variable. \\
+\qquad    \bnfcmt The \bnfnt{integer} is the optional zero-based index of the intended local \\
+\qquad    \bnfcmt variable within all local variables with the given \bnfnt{name}. \\
+\qquad    \bnfcmt The default value for the index is zero. \\
+\qquad    \bnfor{} (\bnfnt{name} [\bnflit{*} \bnfnt{integer}]) \\
+\\
+\bnfnt{variable-definition} ::= \\
+\qquad    \bnfcmt The annotations on the \bnflit{local} line are on the variable declaration, \\
+\qquad    \bnfcmt not the type of the variable. \\
+\qquad    \bnflit{local} \bnfnt{variable-location} \bnflit{:} \bnfnt{decl-annotation}* \lineend \\
+\qquad    \bnfnt{type-annotations}* \\
+\\
+\bnfnt{typecast-location} ::= \\
+\qquad    \bnfcmt Bytecode offset: the first integer is the offset field and the optional \\
+\qquad    \bnfcmt second integer is the type index of an intersection type~\cite{JSR308-webpage-201310}. \\
+\qquad    \bnfcmt The type index defaults to zero if not specified. \\
+\qquad    (\bnflit{\#} \bnfnt{integer} [ \bnflit{,} \bnfnt{integer} ]) \\
+\qquad    \bnfcmt Source code index: the first integer is the zero-based index of the typecast \\
+\qquad    \bnfcmt within the method and the optional second integer is the type index of an \\
+\qquad    \bnfcmt intersection type~\cite{JSR308-webpage-201310}. The type index defaults to zero if not specified. \\
+\qquad    \bnfor{} (\bnflit{*} \bnfnt{integer} [ \bnflit{,} \bnfnt{integer} ]) \\
+\\
+\bnfnt{typecast} ::= \\
+\qquad    \bnflit{typecast} \bnfnt{typecast-location} \bnflit{:} \bnfnt{type-annotation}* \lineend \\
+\qquad    \bnfnt{compound-type}* \\
+\\
+\bnfnt{instanceof-location} ::= \\
+\qquad    \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\
+\qquad    (\bnflit{\#} \bnfnt{integer}) \\
+\qquad    \bnfcmt Source code index: the integer is the zero-based index of the \code{instanceof} \\
+\qquad    \bnfcmt within the method. \\
+\qquad    \bnfor{} (\bnflit{*} \bnfnt{integer}) \\
+\\
+\bnfnt{instanceof} ::= \\
+\qquad    \bnflit{instanceof} \bnfnt{instanceof-location} \bnflit{:} \bnfnt{type-annotation}* \lineend \\
+\qquad    \bnfnt{compound-type}* \\
+\\
+\bnfnt{new-location} ::= \\
+\qquad    \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\
+\qquad    (\bnflit{\#} \bnfnt{integer}) \\
+\qquad    \bnfcmt Source code index: the integer is the zero-based index of the object or array \\
+\qquad    \bnfcmt creation within the method. \\
+\qquad    \bnfor{} (\bnflit{*} \bnfnt{integer}) \\
+\\
+\bnfnt{new} ::= \\
+\qquad    \bnflit{new} \bnfnt{new-location} \bnflit{:} \bnfnt{type-annotation}* \lineend  \\
+\qquad    \bnfnt{compound-type}*
+\\
+\bnfnt{call-location} ::= \\
+\qquad    \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\
+\qquad    (\bnflit{\#} \bnfnt{integer}) \\
+\qquad    \bnfcmt Source code index: the integer is the zero-based index of the method call \\
+\qquad    \bnfcmt within the field or method definition. \\
+\qquad    \bnfor{} (\bnflit{*} \bnfnt{integer}) \\
+\\
+\bnfnt{call} ::= \\
+\qquad    \bnflit{call} \bnfnt{call-location} \bnflit{:} \lineend \\
+\qquad    \bnfnt{typearg-definition}* \\
+\\
+\bnfnt{reference-location} ::= \\
+\qquad    \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\
+\qquad    (\bnflit{\#} \bnfnt{integer}) \\
+\qquad    \bnfcmt Source code index: the integer is the zero-based index of the member \\
+\qquad    \bnfcmt reference~\cite{JSR308-webpage-201310}. \\
+\qquad    \bnfor{} (\bnflit{*} \bnfnt{integer}) \\
+\\
+\bnfnt{reference} ::= \\
+\qquad    \bnflit{reference} \bnfnt{reference-location} \bnflit{:} \bnfnt{type-annotation}* \lineend \\
+\qquad    \bnfnt{compound-type}* \\
+\qquad    \bnfnt{typearg-definition}* \\
+\\
+\bnfnt{lambda-location} ::= \\
+\qquad    \bnfcmt Bytecode offset: the integer is the offset field of the annotation~\cite{JSR308-webpage-201310}. \\
+\qquad    (\bnflit{\#} \bnfnt{integer}) \\
+\qquad    \bnfcmt Source code index: the integer is the zero-based index of the lambda \\
+\qquad    \bnfcmt expression~\cite{JSR308-webpage-201310}. \\
+\qquad    \bnfor{} (\bnflit{*} \bnfnt{integer}) \\
+\\
+\bnfnt{lambda} ::= \\
+\qquad    \bnflit{lambda} \bnfnt{lambda-location} \bnflit{:} \lineend \\
+%\qquad        \bnfnt{return-type}? \\
+\qquad        \bnfnt{parameter-definition}* \\
+\qquad        \bnfnt{variable-definition}* \\
+\qquad        \bnfnt{expression-annotations}*
+\\
+\qquad \= \kill
+\bnfnt{typearg-definition} ::= \\
+\qquad    \bnfcmt The integer is the zero-based type argument index. \\
+\qquad    \bnflit{typearg} \bnfnt{integer} \bnflit{:} \bnfnt{type-annotation}* \lineend \\
+\qquad    \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\subsubsection{AST paths\label{ast-paths}}
+
+A path through the AST (abstract
+syntax tree) specifies an arbitrary expression in source code to modify.
+AST paths can be used in the \code{.jaif} file to specify a location to
+insert either a bare annotation (\bnflit{insert-annotation}) or a cast
+(\bnflit{insert-typecast}).
+
+For a cast insertion, the \code{.jaif} file specifies the type to cast to.
+The annotations on the \bnflit{insert-typecast} line will be inserted on
+the outermost type of the type to cast to. If the type to cast to is a compound
+type then annotations on parts of the compound type are specified with the
+\bnfnt{compound-type} rule. If there are no annotations on
+the \bnflit{insert-typecast} line then a cast with no annotations will be
+inserted or, if compound type annotations are specified, a cast with annotations
+only on the compound types will be inserted.
+
+Note that the type specified on the \bnflit{insert-typecast} line cannot contain
+any qualified type names. For example, use \code{Entry<String, Object>} instead
+of \code{Map.Entry<java.lang.String, java.lang.Object>}.
+
+\begin{tabbing}
+\bnfnt{source-insert-typecast} ::= \\
+\qquad    \bnfcmt \bnfnt{ast-path} is described below. \\
+\qquad    \bnfcmt \bnfnt{type} is the un-annotated type to cast to. \\
+\qquad    \bnflit{insert-typecast} \bnfnt{ast-path}\bnflit{:} \bnfnt{type-annotation}* \bnfnt{type} \lineend \\
+\qquad        \bnfnt{compound-type}*
+\end{tabbing}
+
+An AST path represents a traversal through the AST. AST paths can only be
+used in \bnfnt{field-definition}s and \bnfnt{method-definition}s.
+An AST path starts with the first element under the definition. For
+methods this is \code{Block} and for fields this is \code{Variable}.
+
+An AST path is composed of one or more AST entries, separated by commas. Each
+AST entry is composed of a tree kind, a child selector, and an optional
+argument. An example AST entry is:
+
+\begin{verbatim}
+Block.statement 1
+\end{verbatim}
+
+The tree kind is \code{Block}, the child selector is \code{statement} and the
+argument is \code{1}.
+
+The available tree kinds correspond to the Java AST tree nodes (from the package
+\code{com.sun.source.tree}), but with ``Tree'' removed from the name. For
+example, the class \code{com.sun.source.tree.BlockTree} is represented as
+\code{Block}. The child selectors correspond to the method names of the given
+Java AST tree node, with ``get'' removed from the beginning of the method name
+and the first letter lowercased. In cases where the child selector method
+returns a list, the method name is made singular and the AST entry also contains
+an argument to select the index of the list to take. For example, the method
+\code{com\-.sun\-.source\-.tree\-.Block\-Tree\-.get\-Statements()} is represented as
+\code{Block.statement} and requires an argument to select the statement to take.
+
+The following is an example of an entire AST path:
+
+\begin{verbatim}
+Block.statement 1, Switch.case 1, Case.statement 0, ExpressionStatement.expression,
+    MethodInvocation.argument 0
+\end{verbatim}
+
+Since the above example starts with a \code{Block} it belongs in a
+\bnfnt{method-definition}. This AST path would select an expression that is in
+statement 1 of the method, case 1 of the switch statement, statement 0 of the
+case, and argument 0 of a method call (\code{ExpressionStatement} is just a
+wrapper around an expression that can also be a statement).
+
+The following is an example of an annotation file with AST paths used to specify
+where to insert casts.
+
+\begin{verbatim}
+package p:
+annotation @A:
+
+class ASTPathExample:
+
+field a:
+    insert-typecast Variable.initializer, Binary.rightOperand: @A Integer
+
+method m()V:
+    insert-typecast Block.statement 0, Variable.initializer: @A Integer
+    insert-typecast Block.statement 1, Switch.case 1, Case.statement 0,
+        ExpressionStatement.expression, MethodInvocation.argument 0: @A Integer
+\end{verbatim}
+
+And the matching source code:
+
+\begin{verbatim}
+package p;
+
+public class ASTPathExample {
+
+    private int a = 12 + 13;
+
+    public void m() {
+        int x = 1;
+        switch (x + 2) {
+            case 1:
+                System.out.println(1);
+                break;
+            case 2:
+                System.out.println(2 + x);
+                break;
+            default:
+                System.out.println(-1);
+        }
+    }
+}
+\end{verbatim}
+
+The following is the output, with the casts inserted.
+
+\begin{verbatim}
+package p;
+import p.A;
+
+public class ASTPathExample {
+
+    private int a = 12 + ((@A Integer) (13));
+
+    public void m() {
+        int x = ((@A Integer) (1));
+        switch (x + 2) {
+            case 1:
+                System.out.println(1);
+                break;
+            case 2:
+                System.out.println(((@A Integer) (2 + x)));
+                break;
+            default:
+                System.out.println(-1);
+        }
+    }
+}
+\end{verbatim}
+
+Using \code{insert-annotation} instead of \code{insert-typecast} yields
+almost the same result --- it also inserts a cast.  The sole difference
+is the inability to specify the type in the cast expression.  If you use
+\code{insert-annotation}, then the annotation inserter infers the type,
+which is \code{int} in this case.
+
+Note that a cast can be inserted on any expression, not
+just the deepest expression in the AST. For example, a cast could be inserted on
+the expression \code{i + j}, the identifier \code{i}, and/or the identifier \code{j}.
+
+To help create correct AST paths it may be useful to view the AST of a class.
+The Checker Framework has a processor to do this. The following command will
+output indented AST nodes for the entire input program.
+
+\begin{verbatim}
+javac -processor org.checkerframework.common.util.debug.TreeDebug ASTPathExample.java
+\end{verbatim}
+
+The following is the grammar for AST paths.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{ast-path} ::= \\
+\qquad    \bnfnt{ast-entry} [ \bnflit{,} \bnfnt{ast-entry} ]+ \\
+\\
+\bnfnt{ast-entry} ::= \\
+\qquad    \bnfnt{annotated-type} \\
+\qquad    \bnfor{} \bnfnt{annotation} \\
+\qquad    \bnfor{} \bnfnt{array-access} \\
+\qquad    \bnfor{} \bnfnt{array-type} \\
+\qquad    \bnfor{} \bnfnt{assert} \\
+\qquad    \bnfor{} \bnfnt{assignment} \\
+\qquad    \bnfor{} \bnfnt{binary} \\
+\qquad    \bnfor{} \bnfnt{block} \\
+\qquad    \bnfor{} \bnfnt{case} \\
+\qquad    \bnfor{} \bnfnt{catch} \\
+\qquad    \bnfor{} \bnfnt{compound-assignment} \\
+\qquad    \bnfor{} \bnfnt{conditional-expression} \\
+\qquad    \bnfor{} \bnfnt{do-while-loop} \\
+\qquad    \bnfor{} \bnfnt{enhanced-for-loop} \\
+\qquad    \bnfor{} \bnfnt{expression-statement} \\
+\qquad    \bnfor{} \bnfnt{for-loop} \\
+\qquad    \bnfor{} \bnfnt{if} \\
+\qquad    \bnfor{} \bnfnt{instance-of} \\
+\qquad    \bnfor{} \bnfnt{intersection-type} \\
+\qquad    \bnfor{} \bnfnt{labeled-statement} \\
+\qquad    \bnfor{} \bnfnt{lambda-expression} \\
+\qquad    \bnfor{} \bnfnt{member-reference} \\
+\qquad    \bnfor{} \bnfnt{member-select} \\
+\qquad    \bnfor{} \bnfnt{method-invocation} \\
+\qquad    \bnfor{} \bnfnt{new-array} \\
+\qquad    \bnfor{} \bnfnt{new-class} \\
+\qquad    \bnfor{} \bnfnt{parameterized-type} \\
+\qquad    \bnfor{} \bnfnt{parenthesized} \\
+\qquad    \bnfor{} \bnfnt{return} \\
+\qquad    \bnfor{} \bnfnt{switch} \\
+\qquad    \bnfor{} \bnfnt{synchronized} \\
+\qquad    \bnfor{} \bnfnt{throw} \\
+\qquad    \bnfor{} \bnfnt{try} \\
+\qquad    \bnfor{} \bnfnt{type-cast} \\
+\qquad    \bnfor{} \bnfnt{type-parameter} \\
+\qquad    \bnfor{} \bnfnt{unary} \\
+\qquad    \bnfor{} \bnfnt{union-type} \\
+\qquad    \bnfor{} \bnfnt{variable-type} \\
+\qquad    \bnfor{} \bnfnt{while-loop} \\
+\qquad    \bnfor{} \bnfnt{wildcard-tree} \\
+\\
+\bnfnt{annotated-type} :: = \\
+\qquad    \bnflit{AnnotatedType} \bnflit{.} ( ( \bnflit{annotation} \bnfnt{integer} ) \bnfor{} \bnflit{underlyingType} ) \\
+\\
+\bnfnt{annotation} ::= \\
+\qquad    \bnflit{Annotation} \bnflit{.} ( \bnflit{type} \bnfor{} \bnflit{argument} \bnfnt{integer} ) \\
+\\
+\bnfnt{array-access} ::= \\
+\qquad    \bnflit{ArrayAccess} \bnflit{.} ( \bnflit{expression} \bnfor{} \bnflit{index} ) \\
+\\
+\bnfnt{array-type} ::= \\
+\qquad    \bnflit{ArrayType} \bnflit{.} \bnflit{type} \\
+\\
+\bnfnt{assert} ::= \\
+\qquad    \bnflit{Assert} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{detail} ) \\
+\\
+\bnfnt{assignment} ::= \\
+\qquad    \bnflit{Assignment} \bnflit{.} ( \bnflit{variable} \bnfor{} \bnflit{expression} ) \\
+\\
+\bnfnt{binary} ::= \\
+\qquad    \bnflit{Binary} \bnflit{.} ( \bnflit{leftOperand} \bnfor{} \bnflit{rightOperand} ) \\
+\\
+\bnfnt{block} ::= \\
+\qquad    \bnflit{Block} \bnflit{.} \bnflit{statement} \bnfnt{integer} \\
+\\
+\bnfnt{case} ::= \\
+\qquad    \bnflit{Case} \bnflit{.} ( \bnflit{expression} \bnfor{} ( \bnflit{statement} \bnfnt{integer} ) ) \\
+\\
+\bnfnt{catch} ::= \\
+\qquad    \bnflit{Catch} \bnflit{.} ( \bnflit{parameter} \bnfor{} \bnflit{block} ) \\
+\\
+\bnfnt{compound-assignment} ::= \\
+\qquad    \bnflit{CompoundAssignment} \bnflit{.} ( \bnflit{variable} \bnfor{} \bnflit{expression} ) \\
+\\
+\bnfnt{conditional-expression} ::= \\
+\qquad    \bnflit{ConditionalExpression} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{trueExpression} \bnfor{} \bnflit{falseExpression} ) \\
+\\
+\bnfnt{do-while-loop} ::= \\
+\qquad    \bnflit{DoWhileLoop} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{statement} ) \\
+\\
+\bnfnt{enhanced-for-loop} ::= \\
+\qquad    \bnflit{EnhancedForLoop} \bnflit{.} ( \bnflit{variable} \bnfor{} \bnflit{expression} \bnfor{} \bnflit{statement} ) \\
+\\
+\bnfnt{expression-statement} ::= \\
+\qquad    \bnflit{ExpressionStatement} \bnflit{.} \bnflit{expression} \\
+\\
+\bnfnt{for-loop} ::= \\
+\qquad    \bnflit{ForLoop} \bnflit{.} ( ( \bnflit{initializer} \bnfnt{integer} ) \bnfor{} \bnflit{condition} \bnfor{} ( \bnflit{update} \bnfnt{integer} )  \bnfor{} \bnflit{statement} ) \\
+\\
+\bnfnt{if} ::= \\
+\qquad    \bnflit{If} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{thenStatement} \bnfor{} \bnflit{elseStatement} ) \\
+\\
+\bnfnt{instance-of} ::= \\
+\qquad    \bnflit{InstanceOf} \bnflit{.} ( \bnflit{expression} \bnfor{} \bnflit{type} ) \\
+\\
+\bnfnt{intersection-type} ::= \\
+\qquad    \bnflit{IntersectionType} \bnflit{.} \bnflit{bound} \bnfnt{integer} \\
+\\
+\bnfnt{labeled-statement} ::= \\
+\qquad    \bnflit{LabeledStatement} \bnflit{.} \bnflit{statement} \\
+\\
+\bnfnt{lambda-expression} ::= \\
+\qquad    \bnflit{LambdaExpression} \bnflit{.} ( ( \bnflit{parameter} \bnfnt{integer} ) \bnfor{} \bnflit{body} ) \\
+\\
+\bnfnt{member-reference} ::= \\
+\qquad    \bnflit{MemberReference} \bnflit{.} ( \bnflit{qualifierExpression} \bnfor{} ( \bnflit{typeArgument} \bnfnt{integer} ) ) \\
+\\
+\bnfnt{member-select} ::= \\
+\qquad    \bnflit{MemberSelect} \bnflit{.} \bnflit{expression} \\
+\\
+\bnfnt{method-invocation} ::= \\
+\qquad    \bnflit{MethodInvocation} \bnflit{.} ( ( \bnflit{typeArgument} \bnfnt{integer} ) \bnfor{} \bnflit{methodSelect} \\
+\qquad    \bnfor{} ( \bnflit{argument} \bnfnt{integer} ) ) \\
+\\
+\bnfnt{new-array} ::= \\
+\qquad    \bnflit{NewArray} \bnflit{.} ( \bnflit{type} \bnfor{} ( \bnflit{dimension} \bnfor{} \bnflit{initializer} ) \bnfnt{integer} ) \\
+\\
+\bnfnt{new-class} ::= \\
+\qquad    \bnflit{NewClass} \bnflit{.} ( \bnflit{enclosingExpression} \bnfor{} ( \bnflit{typeArgument} \bnfnt{integer} ) \bnfor{} \bnflit{identifier} \\
+\qquad    \bnfor{} ( \bnflit{argument} \bnfnt{integer} ) \bnfor{} \bnflit{classBody} ) \\
+\\
+\bnfnt{parameterized-type} ::= \\
+\qquad    \bnflit{ParameterizedType} \bnflit{.} ( \bnflit{type} \bnfor{} ( \bnflit{typeArgument} \bnfnt{integer} ) ) \\
+\\
+\bnfnt{parenthesized} ::= \\
+\qquad    \bnflit{Parenthesized} \bnflit{.} \bnflit{expression} \\
+\\
+\bnfnt{return} ::= \\
+\qquad    \bnflit{Return} \bnflit{.} \bnflit{expression} \\
+\\
+\bnfnt{switch} ::= \\
+\qquad    \bnflit{Switch} \bnflit{.} ( \bnflit{expression} \bnfor{} ( \bnflit{case} \bnfnt{integer} ) ) \\
+\\
+\bnfnt{synchronized} ::= \\
+\qquad    \bnflit{Synchronized} \bnflit{.} ( \bnflit{expression} \bnfor{} \bnflit{block} ) \\
+\\
+\bnfnt{throw} ::= \\
+\qquad    \bnflit{Throw} \bnflit{.} \bnflit{expression} \\
+\\
+\bnfnt{try} ::= \\
+\qquad    \bnflit{Try} \bnflit{.} ( \bnflit{block} \bnfor{} ( \bnflit{catch} \bnfnt{integer} ) \bnfor{} \bnflit{finallyBlock} \bnfor{} ( \bnflit{resource} \bnfnt{integer} ) ) \\
+\\
+\bnfnt{type-cast} ::= \\
+\qquad    \bnflit{TypeCast} \bnflit{.} ( \bnflit{type} \bnfor{} \bnflit{expression} ) \\
+\\
+\bnfnt{type-parameter} ::= \\
+\qquad    \bnflit{TypeParameter} \bnflit{.} \bnflit{bound} \bnfnt{integer} \\
+\\
+\bnfnt{unary} ::= \\
+\qquad    \bnflit{Unary} \bnflit{.} \bnflit{expression} \\
+\\
+\bnfnt{union-type} ::= \\
+\qquad    \bnflit{UnionType} \bnflit{.} \bnflit{typeAlternative} \bnfnt{integer} \\
+\\
+\bnfnt{variable} ::= \\
+\qquad    \bnflit{Variable} \bnflit{.} ( \bnflit{type} \bnfor{} \bnflit{initializer} ) \\
+\\
+\bnfnt{while-loop} ::= \\
+\qquad    \bnflit{WhileLoop} \bnflit{.} ( \bnflit{condition} \bnfor{} \bnflit{statement} ) \\
+\\
+\bnfnt{wildcard} ::= \\
+\qquad    \bnflit{Wildcard} \bnflit{.} \bnflit{bound} \\
+\\
+\end{tabbing}
+
+
+\subsection{Annotations\label{annotations-grammar}}
+
+This section describes the details of how annotations are defined, how
+annotations are used, and the different kinds of annotations in an annotation
+file.
+
+
+\subsubsection{Annotation definitions\label{annotation-definitions}}
+
+An annotation definition describes the annotation's fields and their
+types, so that they may be referenced in a compact way throughout the
+annotation file. Any annotation that is used in an annotation file
+% either on a program element or as a field of another annotation definition.
+must be defined before use.
+(This requirement makes it impossible to define, in an
+annotation file, an annotation that is meta-annotated with itself.)
+The two exceptions to this rule are the \code{@java.lang.annotation.Target} and
+\code{@java.lang.annotation.Retention} meta-annotations. These meta-annotations
+are often used in annotation definitions so for ease of use are they not required to
+be defined themselves.
+In the annotation file, the annotation definition appears within the
+package that defines the annotation.  The annotation may be applied to
+elements of any package.
+
+Note that these annotation definitions should not be confused with the
+\code{@interface} syntax used in a Java source file to declare an annotation. An
+annotation definition in an annotation file is only used internally. An
+annotation definition in an annotation file will often mirror an
+\code{@interface} annotation declaration in a Java source file in order to use
+that annotation in an annotation file.
+
+% TODO, see https://github.com/typetools/annotation-tools/issues/25
+% The Annotation File Utilities can read annotation definitions from the
+% classpath, so it is optional to define them in the annotation file.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{annotation-definition} ::= \\
+\qquad    \bnfcmt The \bnfnt{decl-annotation}s are the meta-annotations on this annotation. \\
+\qquad    \bnflit{annotation} \bnflit{@}\bnfnt{name}
+    \bnflit{:} \bnfnt{decl-annotation}* \lineend  \\
+\qquad    \bnfnt{annotation-field-definition}* \\
+\\
+\bnfnt{annotation-field-definition} ::= \\
+\qquad    \bnfnt{annotation-field-type} \bnfnt{name} \lineend \\
+\\
+\bnfnt{annotation-field-type} ::= \\
+\qquad    \bnfcmt \bnfnt{primitive-type} is any Java primitive type (\code{int}, \code{boolean}, etc.). \\
+\qquad    \bnfcmt These are described in detail in Section~\ref{types-and-values}. \\
+\qquad    (\bnfnt{primitive-type} \bnfor{} \bnflit{String} \bnfor{} \bnflit{Class}
+    \bnfor{} (\bnflit{enum} \bnfnt{name}) \bnfor{} (\bnflit{annotation-field} \bnfnt{name})) \bnflit{[]}? \\
+\qquad        \bnfor{} \bnflit{unknown[]} \lineend
+\end{tabbing}
+
+
+\subsubsection{Annotation uses\label{annotation-uses}}
+
+Java SE 8 has two kinds of annotations: ``declaration annotations'' and ``type
+annotations''. Declaration annotations can be written only on method formal
+parameters and the declarations of packages, classes, methods, fields, and local
+variables. Type annotations can be written on any use of a type, and on type
+parameter declarations. Type annotations must be meta-annotated
+with \code{ElementType.TYPE\_USE} and/or \code{ElementType.TYPE\_PARAMETER}.
+These meta-annotations are described in more detail in the JSR 308
+specification~\cite{JSR308-webpage-201310}.
+
+The previous rules have used two productions for annotation uses in an
+annotation file: \bnfnt{decl-annotation} and \bnfnt{type-annotation}.
+The \bnfnt{decl-annotation} and \bnfnt{type-annotation} productions use the same
+syntax to specify an annotation. These two different rules exist only to show
+which type of annotation is valid in a given location. A declaration annotation
+must be used where the \bnfnt{decl-annotation} production is used and a type
+annotation must be used where the \bnfnt{type-annotation} production is used.
+
+The syntax for an annotation is the same as in a Java source file.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{decl-annotation} ::= \\
+\qquad    \bnfcmt \bnfnt{annotation} must be a declaration annotation. \\
+\qquad    \bnfnt{annotation} \\
+\\
+\bnfnt{type-annotation} ::= \\
+\qquad    \bnfcmt \bnfnt{annotation} must be a type annotation. \\
+\qquad    \bnfnt{annotation} \\
+\\
+\bnfnt{annotation} ::= \\
+\qquad    \bnfcmt The name may be the annotation's simple name, unless the file \\
+\qquad    \bnfcmt contains definitions for two annotations with the same simple name. \\
+\qquad    \bnfcmt In this case, the fully-qualified name of the annotation name is required. \\
+% TODO:
+% Perhaps we could add that if a class is in the same package
+% as an annotation it may always use the simple name (even if there's another
+% annotation with the same simple name in another package)? - MP 06/28
+\qquad    \bnflit{@}\bnfnt{name} [ \bnflit{(} \bnfnt{annotation-field} [ \bnflit{,} \bnfnt{annotation-field} ]+ \bnflit{)} ] \\
+\\
+\bnfnt{annotation-field} ::= \\
+\qquad    \bnfcmt In Java, if a single-field annotation has a field named \\
+\qquad    \bnfcmt ``\code{value}'', then that field name may be elided in uses of the\\
+\qquad    \bnfcmt annotation:   ``\code{@A(12)}'' rather than ``\code{@A(value=12)}''. \\
+\qquad    \bnfcmt The same convention holds in an annotation file. \\
+\qquad    \bnfnt{name} \bnflit{=} \bnfnt{value}
+\end{tabbing}
+
+\noindent
+Certain Java elements allow both declaration and type annotations (for example,
+formal method parameters). For these elements, the \bnfnt{type-annotations}
+rule is used to differentiate between the declaration annotations and the type
+annotations.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{type-annotations} ::= \\
+\qquad    \bnfcmt holds the type annotations, as opposed to the declaration annotations. \\
+\qquad        \bnflit{type:} \bnfnt{type-annotation}* \lineend \\
+\qquad        \bnfnt{compound-type}*
+\end{tabbing}
+
+
+\paragraph{Compound type annotations}
+
+A compound type is a parameterized, wildcard, array, or nested type. Annotations
+may be on any type in a compound type. In order to specify the location of an
+annotation within a compound type we use a ``type path''. A
+type path is composed one or more pairs of type kind and type argument index.
+
+\begin{tabbing}
+\qquad \= \kill
+\bnfnt{type-kind} ::= \\
+\qquad    \bnflit{0} \bnfcmt annotation is deeper in this array type \\
+\qquad    \bnfor{} \bnflit{1} \bnfcmt annotation is deeper in this nested type \\
+\qquad    \bnfor{} \bnflit{2} \bnfcmt annotation is on the bound of this wildcard type argument \\
+\qquad    \bnfor{} \bnflit{3} \bnfcmt annotation is on the i'th type argument of this parameterized type \\
+\\
+\bnfnt{type-path} ::= \\
+\qquad    \bnfcmt The \bnfnt{integer} is the type argument index. \\
+\qquad    \bnfnt{type-kind} \bnflit{,} \bnfnt{integer} [ \bnflit{,} \bnfnt{type-kind} \bnflit{,} \bnfnt{integer} ]* \\
+\\
+\bnfnt{compound-type} ::= \\
+\qquad    \bnflit{inner-type} \bnfnt{type-path} \bnflit{:} \bnfnt{annotation}* \lineend
+\end{tabbing}
+
+\noindent
+The type argument index used in the \bnfnt{type-path} rule must be \bnflit{0} unless the \bnfnt{type-kind} is
+\bnflit{3}. In this case, the type argument index selects which type argument
+of a parameterized type to use.
+
+\urldef\cftp\url|https://checkerframework.org/jsr308/specification/java-annotation-design.html#class-file:ext:type_path|
+Type paths are explained in more detail, with many examples to ease
+understanding, in Section 3.4 of the JSR 308 Specification.\footnotemark
+\footnotetext{\cftp}
+
+
+\section{Example\label{example}}
+
+Consider the code of Figure~\ref{fig:java-example}.
+Figure~\ref{fig:annotation-file-examples} shows two legal annotation files
+each of which represents its annotations.
+
+
+\begin{figure}
+\begin{verbatim}
+package p1;
+
+import p2.*; // for the annotations @A through @D
+import java.util.*;
+
+public @A(12) class Foo {
+
+    public int bar;             // no annotation
+    private @B List<@C String> baz;
+
+    public Foo(@D("spam") Foo this, @B List<@C String> a) {
+        @B List<@C String> l = new LinkedList<@C String>();
+        l = (@B List<@C String>)l;
+    }
+}
+\end{verbatim}
+\caption{Example Java code with annotations.}
+\label{fig:java-example}
+\end{figure}
+
+
+\begin{figure}
+\begin{tabular}{|c|c|}
+\hline
+\begin{minipage}[t]{.5\textwidth}
+\begin{verbatim}
+package p2:
+annotation @A:
+    int value
+annotation @B:
+annotation @C:
+annotation @D:
+    String value
+
+package p1:
+class Foo: @A(value=12)
+
+    field bar:
+
+    field baz: @B
+        inner-type 0: @C
+
+    method <init>(
+      Ljava/util/List;)V:
+        parameter 0: @B
+            inner-type 0: @C
+        receiver: @D(value="spam")
+        local 1 #3+5: @B
+            inner-type 0: @C
+        typecast #7: @B
+            inner-type 0: @C
+        new #0:
+            inner-type 0: @C
+\end{verbatim}
+\end{minipage}
+&
+\begin{minipage}[t]{.45\textwidth}
+\begin{verbatim}
+package p2:
+annotation @A
+    int value
+
+package p2:
+annotation @B
+
+package p2:
+annotation @C
+
+package p2:
+annotation @D
+    String value
+
+package p1:
+class Foo: @A(value=12)
+
+package p1:
+class Foo:
+    field baz: @B
+
+package p1:
+class Foo:
+    field baz:
+        inner-type 0: @C
+
+// ... definitions for p1.Foo.<init>
+// omitted for brevity
+\end{verbatim}
+\end{minipage}
+\\
+\hline
+\end{tabular}
+
+\caption{Two distinct annotation files each corresponding to the code of
+  Figure~\ref{fig:java-example}.}
+\label{fig:annotation-file-examples}
+\end{figure}
+
+
+\section{Types and values\label{types-and-values}}
+
+The Java language permits several types for annotation fields: primitives,
+\code{String}s, \code{java.lang.Class} tokens (possibly parameterized),
+enumeration constants, annotations, and one-dimensional arrays of these.
+
+These \textbf{types} are represented in an annotation file as follows:
+\begin{itemize}
+\item Primitive: the name of the primitive type, such as \code{boolean}.
+\item String: \code{String}.
+\item Class token: \code{Class}; the parameterization, if any, is not
+represented in annotation files.
+\item Enumeration constant: \code{enum} followed by the binary name of
+the enumeration class, such as \code{enum java.lang.Thread\$State}.
+\item Annotation: \code{@} followed by the binary name of the annotation type.
+\item Array: The representation of the element type followed by \code{[]}, such
+as \code{String[]}, with one exception: an annotation definition may specify
+a field type as \code{unknown[]} if, in all occurrences of that annotation in
+the annotation file, the field value is a zero-length array.\footnotemark
+\footnotetext{There is a design flaw in the format of array field values in a
+class file.  An array does not itself specify an element type; instead, each
+element specifies its type.  If the annotation type \code{X} has an array field
+\code{arr} but \code{arr} is zero-length in every \code{@X} annotation in the
+class file, there is no way to determine the element type of \code{arr} from the
+class file.  This exception makes it possible to define \code{X} when the class
+file is converted to an annotation file.}
+\end{itemize}
+
+Annotation field \textbf{values} are represented in an annotation file as follows:
+\begin{itemize}
+\item Numeric primitive value: literals as they would appear in Java source
+code.
+\item Boolean: \code{true} or \code{false}.
+\item Character: A single character or escape sequence in single quotes, such
+as \code{'A'} or \code{'\char`\\''}.
+\item String: A string literal as it would appear in source code, such as
+\code{"\char`\\"Yields falsehood when quined\char`\\" yields falsehood when quined."}.
+\item Class token: The binary name of the class (using \code{\$} for
+inner classes) or the name of the primitive type or \code{void}, possibly
+followed by \code{[]}s representing array layers, followed by \code{.class}.
+Examples: \code{java.lang.Integer[].class}, \code{java.util.Map\$Entry.class},
+and \code{int.class}.
+\item Enumeration constant: the name of the enumeration constant, such as
+\code{RUNNABLE}.
+\item Array: a sequence of elements inside \code{\char`\{\char`\}} with a comma
+between each pair of adjacent elements; a comma following the last element is
+optional as in Java.  Also as in Java, the braces may be omitted if the
+array has only one element.
+Examples: \code{\char`\{1\char`\}}, \code{1},
+\code{\char`\{true, false,\char`\}} and \code{\char`\{\char`\}}.
+\end{itemize}
+
+The following example annotation file shows how types and values are represented.
+
+\begin{verbatim}
+package p1:
+
+annotation @ClassInfo:
+    String remark
+    Class favoriteClass
+    Class favoriteCollection // it's probably Class<? extends Collection>
+                             // in source, but no parameterization here
+    char favoriteLetter
+    boolean isBuggy
+    enum p1.DebugCategory[] defaultDebugCategories
+    @p1.CommitInfo lastCommit
+
+annotation @CommitInfo:
+    byte[] hashCode
+    int unixTime
+    String author
+    String message
+
+class Foo: @p1.ClassInfo(
+    remark="Anything named \"Foo\" is bound to be good!",
+    favoriteClass=java.lang.reflect.Proxy.class,
+    favoriteCollection=java.util.LinkedHashSet.class,
+    favoriteLetter='F',
+    isBuggy=true,
+    defaultDebugCategories={DEBUG_TRAVERSAL, DEBUG_STORES, DEBUG_IO},
+    lastCommit=@p1.CommitInfo(
+        hashCode={31, 41, 59, 26, 53, 58, 97, 92, 32, 38, 46, 26, 43, 38, 32, 79},
+        unixTime=1152109350,
+        author="Joe Programmer",
+        message="First implementation of Foo"
+    )
+)
+\end{verbatim}
+
+
+\section{Alternative formats\label{alternative-formats}}
+
+We mention multiple alternatives to the format described in this document.
+Each of them has its own merits.
+In the future, the other formats could be implemented, along with tools for
+converting among them.
+% Then, we can see which of the formats programmers prefer in practice.
+
+
+
+An alternative to the format described in this document would be XML\@.
+% It would be easy to use an XML format to augment the one proposed here, but
+XML does not seem to provide any compelling advantages.  Programmers
+interact with annotation files in two ways:  textually (when reading, writing,
+and editing annotation files) and programmatically (when writing
+annotation-processing tools).  Textually, XML can be
+very hard to read; style sheets mitigate this
+problem, but editing XML files remains tedious and error-prone.
+Programmatically, a layer of abstraction (an API) is needed in any event, so it
+makes little difference what the underlying textual representation is.
+XML files are easier to parse, but the parsing code only needs to be
+written once and is abstracted away by an API to the data structure.
+
+
+Another alternative is a format like the \code{.spec}/\code{.jml} files
+of JML~\cite{LeavensBR2006:JML}.  The format is similar to Java code, but
+all method bodies are empty, and users can annotate the public members of a
+class.  This is easy for Java programmers to read and understand.  (It is a
+bit more complex to implement, but that is not particularly germane.)
+Because it does not permit complete specification of a class's annotations
+(it does not permit annotation of method bodies), it is not appropriate for
+certain tools, such as type inference tools.  However, it might be desirable
+to adopt such a format for public members, and to use the format
+described in this document primarily for method bodies.
+
+
+The Checker Framework~\cite{DietlDEMS2011,CF} uses two additional formats for
+annotations. The first format is called ``stub files.'' A stub file is similar
+to the \code{.spec}/\code{.jml} files described in the previous paragraph. It
+uses Java syntax, only allows annotations on method headers and does not require
+method bodies. A stub file is used to add annotations to method headers of
+existing Java classes. For example, the Checker Framework uses stub files to add
+annotations to method headers of libraries (such as the JDK) without modifying
+the source code or bytecode of the library. A single stub file can contain
+multiple packages and classes. This format only allows annotations on method
+headers, not class headers, fields, and method bodies like in a \code{.jaif}
+file. Further, stub files are only used by the Checker Framework at run time,
+they cannot be used to insert annotations into a source or classfile.
+
+
+The Checker Framework also uses a format called an ``annotated JDK.'' The
+annotated JDK is a \code{.jar} file containing the JDK with annotations. It is
+created with the Annotation File Utilities, but the annotations are stored in a
+format similar to a stub file, instead of in a \code{.jaif} file. The annotated
+JDK starts with a source file for each file in the JDK to be annotated. Like a
+stub file, each source file only contains method headers with annotations. The
+annotated JDK also supports annotations in the class header. To build the
+annotated JDK \code{.jar} file, the source files are compiled, then the
+\code{extract-annotations} script is run on them to generate a \code{.jaif} file
+for each source file. The \code{insert-annotations} script then inserts the
+annotations contained in each \code{.jaif} file into the corresponding JDK class
+file. These are then packaged up into a single \code{.jar} file. Like a stub
+files, the annotated JDK is easier to read and write since it uses Java syntax.
+However, the annotated JDK requires a different file for each original Java
+source file. It does not allow annotations on fields and in method bodies. The
+annotated JDK also only contains annotations in the JDK and not other Java
+files.
+
+
+
+\bibliographystyle{alpha}
+\bibliography{annotation-file-format,bibstring-unabbrev,types,ernst,invariants,generals,alias,crossrefs}
+
+\end{document}
+
+% LocalWords:  java javac OuterClass InnerClass TODO Kleene MP subannotations
+% LocalWords:  enum arr quined int pt instanceof RUNTIME JVML ILjava boolean
+% LocalWords:  programmatically jml ernst jaif whitespace 0pt decl enums
+%  LocalWords:  filenames typeparam javap init clinit ast un lowercased io
+%  LocalWords:  ExpressionStatement AnnotatedType underlyingType ArrayType
+%  LocalWords:  ArrayAccess leftOperand rightOperand CompoundAssignment
+%  LocalWords:  ConditionalExpression trueExpression falseExpression
+%  LocalWords:  DoWhileLoop EnhancedForLoop ForLoop thenStatement NewArray
+%  LocalWords:  elseStatement InstanceOf LabeledStatement LambdaExpression
+%  LocalWords:  MemberReference qualifierExpression typeArgument NewClass
+%  LocalWords:  MemberSelect MethodInvocation methodSelect classBody
+%  LocalWords:  enclosingExpression ParameterizedType finallyBlock AScene
+%  LocalWords:  TypeCast UnionType typeAlternative WhileLoop ElementType
+%  LocalWords:  AClass AMethod AElement objectweb anno tations parseScene
+%  LocalWords:  CriterionList isSatisifiedBy CriteriaList afu getPositions
+%  LocalWords:  InPackageCriterion InClassCriterion InMethodCriterion
+%  LocalWords:  ParamCriterion inserter RUNNABLE ASM src asm
diff --git a/annotation-file-utilities/annotation-file-utilities.html b/annotation-file-utilities/annotation-file-utilities.html
new file mode 100644
index 0000000..8bd8874
--- /dev/null
+++ b/annotation-file-utilities/annotation-file-utilities.html
@@ -0,0 +1,642 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Annotation File Utilities</title>
+</head>
+<body>
+<h1>Annotation File Utilities</h1>
+
+<p>Contents:</p>
+<!-- start toc.  do not edit; run html-update-toc instead -->
+    <ul>
+      <li><a href="#motivation">Motivation</a>
+        <ul>
+          <li><a href="#jaif-file">External storage of annotations</a></li>
+          <li><a href="#annotation-file-utilities-description">Annotation File Utilities</a></li>
+        </ul></li>
+      <li><a href="#installation">Installation</a>
+        <ul>
+          <li><a href="#viewing-source">Building from source</a></li>
+        </ul></li>
+      <li><a href="#using">Using the Annotation File Utilities</a>
+        <ul>
+          <li><a href="#insert-annotations">Insert-annotations</a></li>
+          <li><a href="#extract-annotations">Extract-annotations</a></li>
+          <li><a href="#insert-annotations-to-source">Insert-annotations-to-source</a></li>
+        </ul></li>
+      <li><a href="#implementation">Design and Implementation Details</a>
+        <ul>
+          <li><a href="#asmx">Asmx</a></li>
+          <li><a href="#scene-lib">Scene-lib</a>
+            <ul>
+              <li><a href="#bytecode-insertion">Bytecode Insertion</a></li>
+              <li><a href="#bytecode-extraction">Bytecode Extraction</a></li>
+            </ul></li>
+          <li><a href="#source-insertion">Annotation-file-utilities</a></li>
+        </ul></li>
+      <li><a href="#feedback">Feedback and bug reports</a>
+        <ul>
+          <li><a href="#changelog">Changelog</a></li>
+        </ul></li>
+    </ul>
+<!-- end toc -->
+
+
+
+<hr />
+<h2 id="motivation">Motivation</h2>
+
+<p>
+Java annotations are meta-data about Java program elements, as in
+&ldquo;<code><mark>@Deprecated</mark> class Date
+{&nbsp;...&nbsp;}</code>&rdquo; or &ldquo;<code>List&lt;<mark>@NonNull</mark> String&gt;</code>&rdquo;.
+Ordinarily, Java annotations are
+written in the
+source code of a <code>.java</code> Java source file.  When
+<code>javac</code> compiles the source code, it inserts the annotations in
+the resulting <code>.class</code> file (as
+&ldquo;attributes&rdquo;).
+</p>
+
+<!--
+  I've moved text from here to the annotation-file-format document, but feel
+  free to improve that document.
+-->
+
+<h3 id="jaif-file">External storage of annotations</h3>
+
+<p>
+Sometimes, it is convenient to specify
+the annotations outside the source code or the <code>.class</code> file.
+The document
+&ldquo;Annotation File Format Specification&rdquo; (<a
+    href="annotation-file-format.pdf">PDF</a>, <a
+    href="annotation-file-format.html">HTML</a>)
+defines a textual format for annotations,
+and it also motivates reasons why such a file format is necessary in
+addition to the <code>.java</code> and <code>.class</code> formats.  The
+file format supports both the declaration annotations and type annotations.
+</p>
+
+<p>
+An annotation file
+conventionally has the extension <code>.jaif</code> (for Java Annotation Index
+File).
+The <a href="#scene-lib"><code>scene-lib</code></a> sub-project provides
+API methods for building and manipulating annotation files.
+</p>
+
+
+<h3 id="annotation-file-utilities-description">Annotation File Utilities</h3>
+
+<p>
+Programmers need to be able to transfer annotations
+between the three possible locations for annotations &mdash; source code, class files,
+and annotation files.   Programmers will want to extract
+annotations from source and class files to an annotation file in order to easily
+read annotations, while various tools will only read annotations from
+source and class files.  The Annotation File Utilities provide three tools
+to read and write annotation files.
+</p>
+
+<ul>
+  <li> <a href="#insert-annotations"><code>insert-annotations</code></a> reads annotations from an annotation file
+       and inserts them into a class file</li>
+  <li> <a href="#extract-annotations"><code>extract-annotations</code></a> reads annotations from a class file
+       and writes them out to an annotation file</li>
+  <li> <a href="#insert-annotations-to-source"><code>insert-annotations-to-source</code></a> reads annotations from an
+       annotation file and inserts them into a Java source file</li>
+</ul>
+
+<p>
+A diagram showing the interactions betweeen these tools is below.
+</p>
+
+<div>
+<img src="figures/tool-relations.svg" alt="Relationships between AFU tools" />
+</div>
+
+<p>
+There is no
+<code>extract-annotations-from-source</code> tool:  one can
+compile the source code and then use
+<code>extract-annotations</code> to read the annotations from the class
+file.
+</p>
+
+
+<hr />
+<h2 id="installation">Installation</h2>
+
+<p>
+The following instructions assume either a Linux or Windows system using a command-line environment.
+</p>
+
+<p>
+The current release is Annotation File Utilities version
+<!-- afu-version -->3.6.44, 03 Aug 2017<!-- /afu-version -->.
+</p>
+
+<ol>
+<li>
+  Download
+  <a href="annotation-tools-3.6.44.zip"><!-- annotation-tools-zip-version -->annotation-tools-3.6.44.zip<!-- /annotation-tools-zip-version --></a>.
+</li>
+
+<li>
+  Creade a directory named
+  <code>annotation-tools</code> by
+  unpacking the distribution zipfile (a standard place to do this is in a
+  directory <code>~/jsr308/</code>):
+
+  <pre><code>unzip annotation-tools-3.6.44.zip</code></pre>
+
+  The <code>annotation-tools</code> directory must be a sibling of the <code>jsr308-langtools</code>
+  directory (available at <a href="https://checkerframework.org/jsr308/">https://checkerframework.org/jsr308/</a>).
+  Alternatively, Unix (including Linux and MacOS) users may set the <code>LANGTOOLS</code> environment variable to
+  the location of the <code>jsr308-langtools</code> directory:
+
+  <pre><code>export LANGTOOLS=<em>/path/to</em>/jsr308-langtools</code></pre>
+</li>
+
+<li>
+Add the <code>annotation-file-utilities</code> directory to your path.
+
+<ul>
+<li>
+For <b>Unix</b> (including Linux and MacOS), add the directory to your PATH
+environment variable.  If your shell is sh or bash, add to your
+<code>~/.bashrc</code> or <code>~/.bash_profile</code> file:
+<pre><code>export PATH=${PATH}:<em>/path/to</em>/annotation-tools/annotation-file-utilities/scripts</code></pre>
+<!-- Omitted to save space and simplify instructions
+For csh/tcsh, add to ~/.tcshrc or ~/.cshrc or ~/.login:
+<pre><code>setenv PATH=${PATH}:/path/to/annotation-file-utilities/</code></pre>
+-->
+</li>
+<li>
+For <b>Windows</b>, add the directory to your
+<code>PATH</code> system
+variable by going to
+
+<pre><code> Control Panel -> System -> Advanced -> Environment Variables </code></pre>
+
+From there, find the <code>PATH</code> variable under &ldquo;System variables&rdquo;
+and append to it the directory <code><em>path\to</em>\annotatation-tools\annotation-file-utilities\scripts</code>.
+</li>
+</ul>
+</li>
+</ol>
+
+
+<!-- I think this is obvious, so I have commented it out -MDE. -->
+<!--
+<p>
+To update the annotation file utilities, simply download the most recent <code>annotation-tools.zip</code> file from this website and replace the existing <code>annotation-tools.zip</code>, then extract the file just as when you first installed it.  As long as you followed the above instructions, no further work needs to be done.
+</p>
+-->
+
+<h3 id="viewing-source">Building from source</h3>
+
+<p>
+The annotation file utilities are pre-compiled (a jar file is included in
+the distribution), so most users do not need to compile it themselves.
+</p>
+
+<p>
+There are two ways to obtain the source code.
+Source code is provided in the
+<a href="https://github.com/typetools/annotation-tools/releases">distribution</a>.
+Alternately, see the source code repository at
+<a href="https://github.com/typetools/annotation-tools">https://github.com/typetools/annotation-tools</a>.
+</p>
+
+<p>
+To compile, run
+<code>ant jarfile</code> from the <code>annotation-file-utilities</code>
+subdirectory.  If the <code>javac</code> and <code>java</code> programs are
+not on your execution path, see the notes near the top of
+<code>annotation-file-utilities/tests/Makefile</code>.
+</p>
+
+
+<hr />
+<h2 id="using">Using the Annotation File Utilities</h2>
+
+<p>
+To use the tools, simply run them from the command-line with the
+appropriate arguments.  The following instructions are for running the
+tools on a Linux/Unix/MacOS machine.
+The tools work identically on Windows, except
+the extension <code>.bat</code> needs to be appended to the tool name (for
+example, Windows users would execute <code>insert-annotations.bat</code>
+instead of <code>insert-annotations</code>).
+</p>
+
+<p>
+For all the tools, arguments starting with a single
+&lsquo;<code>@</code>&rsquo; are recognized as argument files
+(<code>argfiles</code>), the contents of which get expanded into the
+command line.  (Initial <code>@@</code> represents a literal
+<code>@</code> in the argument text.)  For additional details of argfile
+processing, refer to Oracle's
+<a href="https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html"><code>javac</code></a>
+documentation.
+</p>
+
+<h3 id="insert-annotations"> Insert-annotations </h3>
+
+<p>
+To insert annotations specified by an annotation file into a class file, use the
+insert-annotations tool.  Running:
+</p>
+
+<pre><code>insert-annotations mypackage.MyClass indexFile.jaif</code></pre>
+
+<p>
+will read in all the annotations from the annotation file
+<code>indexFile.jaif</code> and insert those annotations pertaining to
+<code>mypackage.myClass</code> into the class file for
+<code>mypackage.MyClass</code>, outputting the final class file to
+<code>mypackage.MyClass.class</code> in the present working directory.
+Note that the class file for <code>mypackage.MyClass</code> must be located
+on your classpath.
+</p>
+
+<p>
+Multiple pairs of class and index files (in that order) can be specified
+on a single command line; if the program exits normally, the results
+are the same as if the program were run once for each pair of arguments
+in sequence.
+Run:
+</p>
+
+<pre><code>insert-annotations --help</code></pre>
+
+<p>
+for full usage information.
+</p>
+
+
+<h3 id="extract-annotations"> Extract-annotations </h3>
+
+<p>
+To extract annotations from a class file and write them to an annotation file,
+use the extract-annotations tool.  Running:
+</p>
+
+<pre><code>extract-annotations mypackage.MyClass</code></pre>
+
+<p>
+will locate the class file for <code>mypackage.MyClass</code>, read all
+annotations from it, and write the results in annotation file format to
+<code>mypackage.MyClass.jaif</code> in the present working directory.  Note
+that <code>mypackage.MyClass</code> must be located on your classpath.
+</p>
+
+<p>
+Multiple classes can be specified on a single command line; if the
+program exits normally, the results are the same as if the program were
+run once for each class in sequence.
+Run:
+</p>
+
+<pre><code>extract-annotations --help</code></pre>
+
+<p>
+for full usage information.
+</p>
+
+
+<h3 id="insert-annotations-to-source">Insert-annotations-to-source </h3>
+
+<p>
+To insert annotations specified by an annotation file into a Java source file,
+use the insert-annotations-to-source tool.  Running:
+</p>
+
+<pre><code>insert-annotations-to-source index1.jaif index2.jaif mypackage/MyClass.java yourpackage/YourClass.java</code></pre>
+
+<p>
+will read all the annotations from <code>index1.jaif</code> and
+<code>index2.jaif</code>, insert them (when applicable) into their
+appropriate locations in <code>mypackage/MyClass.java</code> and
+<code>yourpackage/YourClass.java</code>, and write the results to
+<code>annotated/mypackage/MyClass.java</code> and
+<code>annotated/mypackage/MyClass.java</code>, respectively.
+</p>
+
+<p>
+Index and source files can be specified in any order, mixing the two
+file types freely; if the source files have no overlapping definitions
+and the program exits normally, the results are the same as if the
+program were run once for each source file, with <em>all</em> JAIFs
+given for each run.
+Run:
+</p>
+
+<pre><code>insert-annotations-to-source --help</code></pre>
+
+<p>
+for full usage information.
+</p>
+
+<p>
+If you wish to insert annotations into method bodies, you must have the
+associated class <code>mypackage.MyClass.class</code> on your classpath.
+You can insert annotations on class/field/method declarations and
+signatures without the class on your classpath.
+</p>
+
+<p>
+In general (but see below for exceptions), your source code needs to
+contain the locations that your
+<code>.jaif</code> file references.  In particular, if the
+<code>.jaif</code> file contains annotations for a type parameter, but the
+source code uses a raw type, then you will get an error such as
+</p>
+<pre>
+Found class Edge, but unable to insert @checkers.nullness.quals.Nullable:
+  @checkers.nullness.quals.Nullable (nl=true) @ [GenericArrayLocationCriterion at ( [TYPE_ARGUMENT(0)] ), ...
+</pre>
+<p>
+In this case, you should add type arguments, such as changing
+</p>
+<pre>  public void pushNonezeroRing(Stack stack, Hashtable seen) {</pre>
+<p>to</p>
+<pre>  public void pushNonezeroRing(Stack&lt;Edge&gt; stack, Hashtable&lt;Edge, ?&gt; seen) {</pre>
+<p>
+In a few cases, insert-annotations-to-source will generate "default"
+code to provide a location for an annotation.  These include
+</p>
+<ul>
+<li>method and constructor <a href="http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.1-220">receivers;</a></li>
+<li>type parameter bounds (<code>extends Object</code>);</li>
+<li>type casts for expressions</li>
+<li>nullary constructor definitions; and</li>
+<li>explicit constructor invocations for literal arrays (e.g., <code>{"a", "b"}</code> becomes <code>new String[] {"a", "b"}</code>).</li>
+</ul>
+
+<p>
+The command-line options are as follows:
+</p>
+
+<!-- start options doc (DO NOT EDIT BY HAND) -->
+<ul>
+  <li id="optiongroup:General-options">General options
+    <ul>
+      <li id="option:outdir"><b>-d</b> <b>--outdir=</b><i>directory</i>. Directory in which output files are written. [default annotated/]</li>
+      <li id="option:in-place"><b>-i</b> <b>--in-place=</b><i>boolean</i>. If true, overwrite original source files (making a backup first).
+ Furthermore, if the backup files already exist, they are used instead
+ of the .java files.  This behavior permits a user to tweak the .jaif
+ file and re-run the annotator.
+ <p>
+
+ Note that if the user runs the annotator with --in-place, makes edits,
+ and then re-runs the annotator with this --in-place option, those
+ edits are lost.  Similarly, if the user runs the annotator twice in a
+ row with --in-place, only the last set of annotations will appear in
+ the codebase at the end.
+ <p>
+
+ To preserve changes when using the --in-place option, first remove the
+ backup files.  Or, use the <code>-d .</code> option, which makes (and
+ reads) no backup, instead of --in-place. [default false]</li>
+      <li id="option:abbreviate"><b>-a</b> <b>--abbreviate=</b><i>boolean</i>. Abbreviate annotation names [default true]</li>
+      <li id="option:comments"><b>-c</b> <b>--comments=</b><i>boolean</i>. Insert annotations in comments [default false]</li>
+      <li id="option:omit-annotation"><b>-o</b> <b>--omit-annotation=</b><i>string</i>. Omit given annotation</li>
+      <li id="option:nowarn"><b>--nowarn=</b><i>boolean</i>. Suppress warnings about disallowed insertions [default false]</li>
+      <li id="option:convert-jaifs"><b>--convert-jaifs=</b><i>boolean</i>. Convert JAIFs to new format [default false]</li>
+      <li id="option:help"><b>-h</b> <b>--help=</b><i>boolean</i>. Print usage information and exit [default false]</li>
+    </ul>
+  </li>
+  <li id="optiongroup:Debugging-options">Debugging options
+    <ul>
+      <li id="option:verbose"><b>-v</b> <b>--verbose=</b><i>boolean</i>. Verbose (print progress information) [default false]</li>
+      <li id="option:debug"><b>--debug=</b><i>boolean</i>. Debug (print debug information) [default false]</li>
+      <li id="option:print-error-stack"><b>--print-error-stack=</b><i>boolean</i>. Print error stack [default false]</li>
+    </ul>
+  </li>
+</ul>
+<!-- end options doc -->
+
+
+<hr />
+<h2 id="implementation">Design and Implementation Details</h2>
+
+<p>This section describes some high level-design and implementation
+details of the Annotation File Utilities, including the different
+components of the Annotation File Utilities and how they fit together.
+It is intended for someone who is beginning work on the Annotation File
+Utilities or is curious about how the Annotation File Utilities work.</p>
+
+<p>The Annotation File Utilities is composed of three sub-projects:
+<code>asmx</code>, <code>scene-lib</code>, and
+<code>annotation-file-utilities</code>.  The <code>asmx</code>
+sub-project provides methods for reading and writing class files.  The
+<code>scene-lib</code> sub-project represents a <code>.jaif</code> file
+and inserts and extracts annotations to/from bytecode.  The
+<code>annotation-file-utilities</code> sub-project inserts annotations
+into source code.</p>
+
+<h3 id="asmx" class="subsection">Asmx</h3>
+
+<p>The <code>asmx</code> sub-project provides methods for reading and
+writing class files.  <code>asmx</code> is based on an old version
+(2.2.2) of the <a href="http://asm.ow2.org/">ASM Framework</a>. It has been modified to
+allow it to read and write <a href="https://checkerframework.org/jsr308/specification/java-annotation-design.html">JSR 308</a>
+annotations in bytecode.  However, it is far behind the current ASM
+version (5.0), which provides support for type annotations. We should
+discard the custom <code>asmx</code> in the Annotation File Utilities
+and adapt the Annotation File Utilities to use the official, supported
+version of ASM.</p>
+
+<h3 id="scene-lib" class="subsection">Scene-lib</h3>
+
+<p><code>scene-lib</code> is an interface to a <code>.jaif</code> file.
+It reads in and writes out <code>.jaif</code> files and provides an
+internal representation of a <code>.jaif</code> file to access and
+manipulate.</p>
+
+<p>Internally, a <code>.jaif</code> file is represented by the
+<code>scene-lib/src/annotations/el/AScene.java</code> class. The
+<code>AScene</code> class (or &ldquo;annotated scene&rdquo;) roughly
+parallels the root of an abstract syntax tree. An <code>AScene</code>
+has a number of classes (<code>AClass</code>) as children. Each class
+has a number of methods (<code>AMethod</code>), fields
+(<code>AElement</code>), etc. as children. All of these classes are
+related in the type hierarchy shown below.</p>
+
+<div>
+<img src="figures/scene-lib-type-hierarchy.svg" alt="scene-lib type hierarchy">
+</div>
+
+<p>Each class in the type hierarchy has one or more fields to hold
+annotations for the different components of the class. For example, the
+<code>AMethod</code> class has the following fields: bounds, return
+type, receiver parameters, and throws clause. Each of these fields
+holds the annotations stored on that part of the method. For details on
+the remainder of the classes in the type hierarchy, and their
+respective fields, see the documentation for each file in
+<code>scene-lib/src/annotations/el/</code>.</p>
+
+<p>An <code>AScene</code> instance can be created in two ways. An empty
+<code>AScene</code> can be created by calling the <code>AScene</code>
+constructor, or an <code>AScene</code> can be created by parsing an
+existing <code>.jaif</code> file. Once an <code>AScene</code> is
+created, annotations can be added to it by adding them to the correct
+fields of the children. An <code>AScene</code> can also be output to
+create a new <code>.jaif</code> file.</p>
+
+<h4 id="bytecode-insertion" class="subsubsection">Bytecode Insertion</h4>
+
+<p>Annotations can be inserted into bytecode by executing the
+<code>annotation-file-utilities/scripts/insert-annotations</code>
+script. This script takes one or more &langle;class name,
+<code>.jaif</code> file&rangle; pairs as arguments. The annotations
+specified in the <code>.jaif</code> file are inserted into the
+classfile directly before the <code>.jaif</code> file in the argument
+list.</p><p>First, each <code>.jaif</code> file is parsed into an
+<code>AScene</code> (as described in
+<a href="#scene-lib">Scene-lib</a>). Then,
+<code>asmx/src/org/objectweb/asm/ClassReader.java</code> parses the
+classfile.  As it is parsing the classfile, it passes the parsed
+bytecode off to the
+<code>scene-lib/src/annotations/io/classfile/ClassAnnotationSceneWriter.java</code>
+class. This class has a reference to the <code>AScene</code> parsed
+from the <code>.jaif</code> file. As this class receives the parsed
+bytecode it inserts the relevant annotations from the
+<code>AScene</code> in the bytecode and then writes the bytecode back
+out.</p>
+
+<h4 id="bytecode-extraction" class="subsubsection">Bytecode Extraction</h4>
+
+<p>Annotations can be extracted from bytecode by executing the
+<code>annotation-file-utilities/scripts/extract-annotations</code>
+script. This script takes one or more class names as arguments and
+outputs the annotations found in those classes to <code>.jaif</code>
+files.</p><p>First, an empty <code>AScene</code> is constructed to store
+the annotations.
+<code>asmx/src/org/objectweb/asm/ClassReader.java</code> parses the
+classfile and passes the parsed bytecode off to the
+<code>scene-lib/src/annotations/io/classfile/ClassAnnotationSceneReader.java</code>
+class. This class filters out the annotations in the bytecode and adds
+them to the correct part of the <code>AScene</code>. After this, the
+<code>AScene</code> is output to a <code>.jaif</code> file.</p>
+
+<h3 id="source-insertion" class="subsection">Annotation-file-utilities</h3>
+
+<p>The <code>annotation-file-utilities</code> sub-project inserts annotations into source
+code. It can be run by executing the
+<code>annotation-file-utilities/scripts/insert-annotations-to-source</code>
+script.  The script takes one or more <code>.jaif</code> files, followed by one or more <code>.java</code> source files as arguments. The annotations in the <code>.jaif</code> files are inserted into the <code>.java</code> source files.</p><p>First, an instance of
+<code>annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java</code>
+is created. Its <code>parse</code> method parses the <code>.jaif</code>
+file into an <code>AScene</code> (as described in
+<a href="#scene-lib">Scene-lib</a>).  The <code>parse</code> method
+calls the <code>parseScene</code> method, which traverses through the
+<code>AScene</code> and creates an
+<code>annotation-file-utilities/src/annotator/specification/CriterionList.java.</code>
+A <code>CriterionList</code> identifies a unique AST node that is the
+location of an insertion. It contains objects that implement the
+<code>annotation-file-utilities/src/annotator/find/Criterion.java</code>
+interface.
+Each <code>Criterion</code> has an <code>isSatisifiedBy</code> method —
+a predicate that takes an AST node and returns <code>true</code> if the
+AST node satisfies the <code>Criterion</code> and <code>false</code>
+otherwise. To determine if a given node matches a
+<code>CriterionList</code>, the node is passed to all of the
+<code>Criterion</code>s in the <code>CriteriaList</code>. If every
+<code>Criterion</code> returns <code>true</code> then it is match. If
+one or more <code>Criterion</code>s return <code>false</code> then it is
+not a match.  The various <code>Criterion</code> classes are in the
+<code>annotation-file-utilities/src/annotator/find/</code> directory.
+For example, take the following source code:</p>
+
+<pre class="verbatim">package afu.example;
+
+public class Test {
+    public void m(boolean b, int i) {
+      // ...
+    }
+}
+</pre>
+
+<p>The <code>CriterionList</code> to specify the location of the
+<code>i</code> parameter contains the following
+<code>Criterion</code>s:</p>
+
+<ul class="itemize"><li class="li-itemize">
+<code>InPackageCriterion("afu.example")</code>
+</li><li class="li-itemize"><code>InClassCriterion("Test")</code>
+</li><li class="li-itemize"><code>InMethodCriterion("m(ZI)V")</code>
+</li><li class="li-itemize"><code>ParamCriterion(1)</code>
+</li></ul>
+
+<p>After this <code>CriterionList</code> is built up an
+<code>annotation-file-utilities/src/annotator/find/Insertion.java</code>
+is created.  An <code>Insertion</code> stores an
+<code>annotation-file-utilities/src/annotator/find/Criteria.java</code>
+(which is created from a <code>CriterionList</code>) and the text to be
+inserted. All of these <code>Insertion</code>s are then added to a
+list. The Java compiler then is called to parse the Java source into an
+abstract syntax tree. This is followed by a call to the
+<code>getPositions</code> method of
+<code>annotation-file-utilities/src/annotator/find/TreeFinder.java</code>,
+which scans through each node of the abstract syntax trees. For each node,
+it runs through the <code>Criteria</code> for each un-matched
+<code>Insertion</code>. If at least one of the <code>Criteria</code>
+does not match, then this is not the correct place for the
+<code>Insertion</code> and the <code>Insertion</code> will be checked
+at the remaining nodes of the tree. If all of the <code>Criteria</code>
+match, then this node is the correct place for the
+<code>Insertion</code>. It is removed from the list of un-matched
+<code>Insertion</code>s and the position where to insert the
+<code>Insertion</code> is determined. This position is the integer
+index in the file where the <code>Insertion</code> should be inserted.
+After the positions are found for all of the <code>Insertion</code>s,
+the <code>Insertion</code> text is inserted into the file. This happens
+backwards, with <code>Insertion</code>s at the end of the file (i.e.
+with higher positions) being inserted first. If <code>Insertion</code>s
+were instead inserted from the beginning of the file then a single
+<code>Insertion</code> would invalidate all of the positions for the
+following <code>Insertion</code>s.</p>
+
+<p>If there are remaining <code>Insertion</code>s that were not matched
+to a node in the abstract syntax tree then an error message is
+displayed.</p>
+
+
+<hr />
+<h2 id="feedback"> Feedback and bug reports </h2>
+
+<p>
+To submit a bug report or request a new feature, use the
+<a href="https://github.com/typetools/annotation-tools/issues">issue
+  tracker</a>.  When reporting a bug, please include exact instructions in
+how to reproduce it, and please also attach relevant input files.  This
+will let us resolve the issue quickly.
+</p>
+
+<p>
+You can also reach the developers at
+<a href="mailto:annotation-tools-dev@googlegroups.com">annotation-tools-dev@googlegroups.com</a>.
+But please use the
+  <a href="https://github.com/typetools/annotation-tools/issues">issue
+  tracker</a> for bug reports and feature requests.
+</p>
+
+
+<h3 id="changelog">Changelog</h3>
+
+<p>
+The <a href="changelog.html">changelog</a> describes what is new in each release.
+</p>
+
+
+<hr/>
+
+</body>
+</html>
+
+<!--  LocalWords:  utils bashrc tcsh tcshrc cshrc classpath
+ -->
diff --git a/annotation-file-utilities/annotation-file-utilities.jar b/annotation-file-utilities/annotation-file-utilities.jar
new file mode 100644
index 0000000..8d1e8f0
--- /dev/null
+++ b/annotation-file-utilities/annotation-file-utilities.jar
Binary files differ
diff --git a/annotation-file-utilities/build.properties b/annotation-file-utilities/build.properties
new file mode 100644
index 0000000..bf00a31
--- /dev/null
+++ b/annotation-file-utilities/build.properties
@@ -0,0 +1,10 @@
+workspace : ${basedir}/../..
+global.build.properties : ${basedir}/../global.build.properties
+user.build.properties : ${basedir}/../user.build.properties
+afu.ver.0=<!-- afu-version -->
+afu.ver.1=<!-- /afu-version -->
+afu.date.0=<!-- afu-date -->
+afu.date.1=<!-- /afu-date -->
+afu.zip.ver.0=<!-- annotation-tools-zip-version -->
+afu.zip.ver.1=<!-- /annotation-tools-zip-version -->
+afu.cfr.pattern=Annotation File Utilities v([^"]+)
\ No newline at end of file
diff --git a/annotation-file-utilities/build.xml b/annotation-file-utilities/build.xml
new file mode 100644
index 0000000..58b2ad4
--- /dev/null
+++ b/annotation-file-utilities/build.xml
@@ -0,0 +1,575 @@
+<?xml version="1.0"?>
+
+<project name="annotation-file-utilities" default="zipfile">
+  <description>
+     Ant build file for the annotation file utilities.
+     Run "ant -projecthelp" for a full list of options.
+  </description>
+
+  <property environment="env"/>
+
+  <property name="java-version" value="8"/>
+
+  <tstamp>
+      <format property="TIME" pattern="yy-MM-dd-HH-mm-ss-SS" />
+  </tstamp>
+  <!-- Avoid conflicts between multiple users on the same computer. -->
+  <property name="tmpdir" location="${java.io.tmpdir}/${user.name}/${TIME}" />
+  <!-- The distribution is the result of zipping this directory. -->
+  <property name="temp-annotation-tools" location="${tmpdir}/annotation-tools" />
+
+  <!-- Can't I just reuse temp-annotation-file-utilities, rather than
+       having this separate directory? -->
+  <property name="temp-jarfile" location="${tmpdir}/annotation-file-utilities-jarfile" />
+
+  <target name="init-properties">
+    <condition property="exists.build.properties">
+      <available file="build.properties"/>
+    </condition>
+    <fail
+      unless="exists.build.properties"
+      message="Local build.properites file is missing."/>
+
+    <property file="build.properties"/>
+
+    <fail
+      unless="global.build.properties"
+      message="Local build.properties file did not define global buildfile in property global.build.properties"/>
+    <condition property="exists.global.build.properties">
+      <available file="${global.build.properties}"/>
+    </condition>
+    <fail
+      unless="exists.global.build.properties"
+      message="File ${global.build.properties} file not found."/>
+    <property file="${global.build.properties}"/>
+
+    <fail
+      unless="user.build.properties"
+      message="Local build.properties file did not define global buildfile in property user.build.properties"/>
+    <condition property="exists.user.build.properties">
+      <available file="${user.build.properties}"/>
+    </condition>
+    <fail
+      unless="exists.user.build.properties"
+      message="File ${user.build.properties} file not found."/>
+    <property file="${user.build.properties}"/>
+
+    <echo message="annotations-compiler: ${annotations-compiler}"/>
+
+    <!-- convert relative to absolute pathname -->
+    <property name="annotations-compiler-absolute" location="${annotations-compiler}" />
+
+  </target>
+
+  <!-- Compiles all the subparts of the Annotation File Utilities. -->
+  <target name="init-dependencies"
+          depends="init-properties">
+      <ant dir="${asmx}" target="bin">
+        <property name="product.noshrink" value="true"/>
+      </ant>
+      <ant dir="${scene-lib}" target="bin"/>
+      <!-- repository version only: -->
+      <!-- I don't see the need for this. -->
+      <!-- <ant dir="${annotations-compiler}" antfile="make/build.xml" target="build"/> -->
+  </target>
+
+  <target name="init"
+          depends="init-properties, init-dependencies">
+    <fileset dir="src" id="src-files">
+      <include name="**/*.java"/>
+    </fileset>
+
+    <path id="libpath">
+      <!-- alternate for distribution:
+      <pathelement location="${annotations-compiler}/bin"/>
+      -->
+      <pathelement location="${annotations-compiler}/dist/lib/javac.jar"/>
+      <pathelement location="${annotation-tools}/annotation-file-utilities/lib/plume-core.jar"/>
+      <pathelement location="${annotation-tools}/annotation-file-utilities/lib/guava-20.0.jar"/>
+      <pathelement location="${scene-lib}/bin"/>
+      <pathelement location="${asmx}/bin"/>
+      <!-- needed for optionsdoc target -->
+      <pathelement location="bin"/>
+      <!-- additional for distribution:
+      <pathelement location="${jre1.6.0}"/>
+      -->
+    </path>
+  </target>
+
+  <path id="javadoc-sourcepath">
+    <pathelement location="src"/>
+  </path>
+
+  <target name="javadoc-clean">
+      <delete dir="javadoc"/>
+  </target>
+
+  <target name="javadoc" depends="javadoc-clean, init">
+      <javadoc sourcepathref="javadoc-sourcepath"
+          classpathref="libpath"
+          failonerror="true"
+          packagenames="*"
+          destdir="javadoc"
+          access="public"
+          />
+<!--
+      noqualifier="annotations:annotations.el:annotations.field:annotations.io:annotations.io.classfile:annotations.util:annotations.util.coll:java.lang"
+-->
+  </target>
+
+  <target name="optionsdoc" depends="javadoc, init">
+    <javadoc sourcepathref="javadoc-sourcepath"
+        sourcefiles="src/annotator/Main.java"
+        classpathref="libpath"
+        docletpathref="libpath"
+        failonerror="true"
+        >
+      <doclet name="plume.OptionsDoclet">
+        <param name="-format" value="javadoc"/>
+        <param name="-i"/>
+        <param name="-docfile" value="src/annotator/Main.java"/>
+      </doclet>
+    </javadoc>
+
+    <javadoc sourcepathref="javadoc-sourcepath"
+        sourcefiles="src/annotator/Main.java"
+        classpathref="libpath"
+        docletpathref="libpath"
+        failonerror="true"
+        >
+      <doclet name="plume.OptionsDoclet">
+        <param name="-format" value="html"/>
+        <param name="-i"/>
+        <param name="-docfile" value="annotation-file-utilities.html"/>
+      </doclet>
+    </javadoc>
+
+  </target>
+
+  <target name="jarfile.check.uptodate">
+      <uptodate property="bin.uptodate" targetfile="annotation-file-utilities.jar">
+        <srcfiles dir="bin" />
+      </uptodate>
+      <uptodate property="asmx.bin.uptodate" targetfile="annotation-file-utilities.jar">
+        <srcfiles dir="${asmx}/bin" excludes="tmp/**"/>
+      </uptodate>
+      <uptodate property="scene-lib.bin.uptodate" targetfile="annotation-file-utilities.jar">
+        <srcfiles dir="${scene-lib}/bin" excludes="annotations-expected/**,annotations/tests/**"/>
+      </uptodate>
+      <uptodate property="annotations-compiler.uptodate" targetfile="annotation-file-utilities.jar" srcfile="${annotations-compiler}/dist/lib/javac.jar"/>
+      <uptodate property="plume.uptodate" targetfile="annotation-file-utilities.jar" srcfile="lib/plume-core.jar"/>
+      <uptodate property="google-collect.uptodate" targetfile="annotation-file-utilities.jar" srcfile="lib/guava-20.0.jar"/>
+
+      <condition property="jarfile.uptodate">
+        <and>
+          <isset property="bin.uptodate"/>
+          <isset property="asmx.bin.uptodate"/>
+          <isset property="scene-lib.bin.uptodate"/>
+          <isset property="annotations-compiler.uptodate"/>
+          <isset property="plume.uptodate"/>
+          <isset property="google-collect.uptodate"/>
+        </and>
+      </condition>
+
+      <echo message="bin.uptodate: ${bin.uptodate}"/>
+      <echo message="asmx.bin.uptodate: ${asmx.bin.uptodate}"/>
+      <echo message="scene-lib.bin.uptodate: ${scene-lib.bin.uptodate}"/>
+      <echo message="annotations-compiler.uptodate: ${annotations-compiler.uptodate}"/>
+      <echo message="plume.uptodate: ${plume.uptodate}"/>
+      <echo message="google-collect.uptodate: ${google-collect.uptodate}"/>
+      <echo message="jarfile.uptodate: ${jarfile.uptodate}"/>
+
+  </target>
+
+  <target name="jarfile"
+          depends="init,build,jarfile.check.uptodate"
+          unless="jarfile.uptodate"
+          description="create the class library annotation-file-utilities.jar">
+
+    <echo message="Using temporary directory: ${temp-jarfile}"/>
+    <delete dir="${temp-jarfile}"/>
+    <mkdir dir="${temp-jarfile}"/>
+
+    <echo message="Copying .class files to ${temp-jarfile}"/>
+    <copy todir="${temp-jarfile}">
+      <fileset dir="bin" />
+      <fileset dir="${asmx}/bin" excludes="tmp/**"/>
+      <fileset dir="${scene-lib}/bin" excludes="annotations-expected/**,annotations/tests/**"/>
+    </copy>
+    <!-- Also need to get class files in libraries -->
+    <unjar src="lib/plume-core.jar" dest="${temp-jarfile}">
+      <patternset>
+        <include name="**/*.class"/>
+        <exclude name="META-INF/" />
+      </patternset>
+    </unjar>
+    <unjar src="lib/guava-20.0.jar" dest="${temp-jarfile}">
+      <patternset>
+        <include name="com/google/common/base/**/*.class"/>
+        <include name="com/google/common/collect/**/*.class"/>
+        <include name="com/google/common/escape/**/*.class"/>
+        <exclude name="META-INF/" />
+      </patternset>
+    </unjar>
+    <unjar src="${annotations-compiler}/dist/lib/javac.jar" dest="${temp-jarfile}">
+      <patternset>
+        <include name="**/*.class"/>
+        <exclude name="META-INF/" />
+      </patternset>
+    </unjar>
+
+    <!-- Actually create a single .jar file of all the class files,
+         scripts and documentation -->
+    <echo message="Creating jarfile annotation-file-utilities.jar"/>
+    <jar destfile="annotation-file-utilities.jar">
+      <fileset dir="${temp-jarfile}"/>
+    </jar>
+
+    <!-- Delete class files copied over -->
+    <echo message="Deleting temporary directory: ${temp-jarfile}"/>
+    <delete dir="${temp-jarfile}"/>
+  </target>
+
+  <target name="check-git-status" depends="init-properties">
+    <exec executable="git" failonerror="true"
+          outputproperty="status.output">
+      <arg value="status" />
+    </exec>
+
+    <condition property="status.output.empty">
+      <equals
+        arg1="${status.output}"
+        arg2=""/>
+    </condition>
+
+    <fail unless="status.output.empty"
+          message="`git status' did not return empty output.
+  Commit/add/remove files as appropriate, then re-try."/>
+  </target>
+
+  <!-- Copy files from repository to temporary directory from which they
+  will be packaged up. -->
+  <target name="update-workspace" depends="init-properties">
+    <delete dir="${temp-annotation-tools}" />
+
+    <exec executable="git" failonerror="true">
+      <arg value="clone" />
+      <arg value="${annotation-tools}" />
+      <arg value="${temp-annotation-tools}" />
+    </exec>
+    <delete dir="${temp-annotation-tools}/scene-lib.orig-hand-annos" />
+    <delete dir="${temp-annotation-tools}/.git" />
+    <delete file="${temp-annotation-tools}/.gitignore" />
+    <delete file="${temp-annotation-tools}/.hg_archival.txt" />
+    <delete file="${temp-annotation-tools}/.hgignore" />
+
+    <copy todir="${temp-annotation-tools}/annotation-file-utilities">
+      <fileset dir="${afu}">
+        <include name="annotation-file-format.dvi"/>
+        <include name="annotation-file-format.html"/>
+        <include name="annotation-file-format.pdf"/>
+        <include name="annotation-file-utilities.jar"/>
+        <include name="bin/**"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${temp-annotation-tools}/asmx">
+      <fileset dir="${asmx}">
+        <include name="bin/**"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${temp-annotation-tools}/scene-lib">
+      <fileset dir="${scene-lib}">
+        <include name="bin/**"/>
+      </fileset>
+    </copy>
+
+  </target>
+
+  <target name="zipfile"
+          depends="jarfile,annotation-file-format,run-tests,update-workspace"
+          description="create the distribution: annotation-tools.zip">
+    <!-- Create a new directory containing all the files and then zip that
+         directory, so that when the user unzips they extract exactly one
+         directory. -->
+
+    <!-- In order for the shell scripts to have the proper execution bit set,
+         include them specifically with the right permissions.  Ant presently
+         does not use the file's permissions themselves to do this. -->
+    <zip destfile="annotation-tools.zip" compress="true">
+      <fileset dir="${tmpdir}">
+        <include name="annotation-tools/"/>
+        <exclude name="annotation-tools/annotation-file-utilities/scripts/extract-annotations"/>
+        <exclude name="annotation-tools/annotation-file-utilities/scripts/insert-annotations"/>
+        <exclude name="annotation-tools/annotation-file-utilities/scripts/insert-annotations-to-source"/>
+      </fileset>
+      <zipfileset dir="${tmpdir}" filemode="755">
+        <include name="annotation-tools/annotation-file-utilities/scripts/extract-annotations"/>
+        <include name="annotation-tools/annotation-file-utilities/scripts/insert-annotations"/>
+        <include name="annotation-tools/annotation-file-utilities/scripts/insert-annotations-to-source"/>
+      </zipfileset>
+    </zip>
+
+    <!-- Delete temporary files once they have been zipped. -->
+<!--
+    <delete dir="${temp-annotation-file-utilities}"/>
+-->
+  </target>
+
+  <macrodef name="update">
+    <attribute name="file"/>
+    <attribute name="start"/>
+    <attribute name="end" default=""/>
+    <attribute name="with"/>
+    <sequential>
+        <echo level="info" message="updating @{file}"/>
+        <replaceregexp file="@{file}" byline="true"
+                       match="@{start}.*@{end}" replace="@{start}@{with}@{end}"/>
+    </sequential>
+  </macrodef>
+
+  <target name="update-versions" depends="init-properties">
+    <fail unless="release.ver"  message="You must specify a release version to update to"/>
+    <fail unless="release.date" message="You must specify a release date to update to"/>
+
+    <property name="release.version.regexp" value="\d\.\d\.\d+(?:\.\d)"/>
+    <property name="afuWebPage"   value="${annotation-tools}/annotation-file-utilities/annotation-file-utilities.html"/>
+
+    <replaceregexp file="${afuWebPage}" byline="true"
+                   match="annotation-tools-${release.version.regexp}{0,1}.zip" replace="annotation-tools-${release.ver}.zip"/>
+
+    <update file="${afuWebPage}"
+            start="${afu.zip.ver.0}" end="${afu.zip.ver.1}"
+            with="annotation-tools-${release.ver}.zip"/>
+
+    <update file="${afuWebPage}"
+            start="${afu.ver.0}" end="${afu.ver.1}"
+            with="${release.ver}, ${release.date}"/>
+
+    <update file="${afuWebPage}"
+            start="${afu.date.0}" end="${afu.date.1}"
+            with="${release.date}"/>
+
+    <property name="newCfrValue" value="Annotation File Utilities v${release.ver}"/>
+
+    <property name="ClassFileReaderPath" value="${annotation-tools}/scene-lib/src/annotations/io/classfile/ClassFileReader.java"/>
+    <echo level="info" message="updating ${ClassFileReaderPath}"/>
+    <replaceregexp file="${ClassFileReaderPath}" byline="true"
+                   match="${afu.cfr.pattern}" replace="${newCfrValue}"/>
+  </target>
+
+  <!-- TODO: I am not sure this target works as the original author intended
+       TODO: (i.e. I don't think check-git-status gets executed twice)
+       TODO: but I am maintaining the previous behavior.
+       -->
+  <target name="web" depends="check-git-status,web-no-checks,check-git-status"/>
+
+  <!-- New release process runs checks prior to running the web-no-checks target-->
+  <target name="web-no-checks" depends="clean,zipfile"
+          description="export the zipfile, etc. to its website">
+
+    <fail unless="deploy-dir" message="You must specify a deploy-dir, the live site deploy-dir=/cse/www2/types/annotation-file-utilities/releases/_version num_"/>
+
+    <fail unless="afu.version" message="You must specify an afu.version, such as 3.6.22"/>
+
+    <echo message="Export location: ${deploy-dir}"/>
+    <echo message="Copying annotation-tools.zip"/>
+    <copy file="annotation-tools.zip"
+      tofile="${deploy-dir}/annotation-tools-${afu.version}.zip"/>
+
+    <echo message="Copying annotation-file-utilities.html"/>
+    <copy file="annotation-file-utilities.html"
+      todir="${deploy-dir}"/>
+    <copy file="changelog.html"
+      todir="${deploy-dir}"/>
+
+    <echo message="Copying annotation-file-format.{html,pdf}"/>
+    <copy file="annotation-file-format.html"
+      todir="${deploy-dir}"/>
+    <copy file="annotation-file-format.pdf"
+      todir="${deploy-dir}"/>
+
+    <copy todir="${deploy-dir}/figures" flatten="true">
+      <fileset dir="figures">
+          <include name="*.svg"/>
+          <include name="*.png"/>
+          <include name="*.gif"/>
+      </fileset>
+    </copy>
+
+    <symlink overwrite="true"
+             link="${deploy-dir}/index.html"
+             resource="annotation-file-utilities.html"/>
+  </target>
+
+  <target name="annotation-file-format-clean"
+          description="removes generated documentation files">
+    <delete file="annotation-file-format.aux"/>
+    <delete file="annotation-file-format.dvi"/>
+    <delete file="annotation-file-format.haux"/>
+    <delete file="annotation-file-format.html"/>
+    <delete file="annotation-file-format.htoc"/>
+    <delete file="annotation-file-format.log"/>
+    <delete file="annotation-file-format.pdf"/>
+    <delete file="annotation-file-format.toc"/>
+    <exec executable="make" failonerror="true">
+      <arg value="-C"/>
+      <arg value="figures"/>
+      <arg value="clean"/>
+    </exec>
+    <delete file="scene-lib-type-hierarchy.png"/>
+  </target>
+
+  <!--
+    A problem is that the document date is the date that LaTeX was run
+    rather than the date of last modification; that should be fixed in
+    the document, perhaps.  -->
+  <target name="annotation-file-format"
+          description="Make documentation: annotation-file-format.{html,pdf}">
+    <exec executable="make" failonerror="true">
+      <arg value="-C"/>
+      <arg value="figures"/>
+    </exec>
+    <exec executable="latex" failonerror="true">
+      <arg value="annotation-file-format.tex"/>
+    </exec>
+    <exec executable="bibtex" failonerror="true">
+      <arg value="annotation-file-format"/>
+    </exec>
+    <exec executable="latex" failonerror="true">
+      <arg value="annotation-file-format.tex"/>
+    </exec>
+    <exec executable="latex" failonerror="true">
+      <arg value="annotation-file-format.tex"/>
+    </exec>
+    <exec executable="pdflatex" failonerror="true">
+      <arg value="annotation-file-format.tex"/>
+    </exec>
+    <exec executable="hevea" failonerror="true">
+      <arg value="-fix"/>
+      <arg value="-exec"/>
+      <arg value="xxdate.exe"/>
+      <arg value="urlhref.hva"/>
+      <arg value="annotation-file-format.tex"/>
+    </exec>
+  </target>
+
+  <target name="clean" depends="annotation-file-format-clean,clean-tests"
+          description="removes generated files (e.g., .jar, .zip)">
+    <delete dir="bin"/>
+
+    <!-- <echo message="Deleting temporary directory: ${temp-jarfile}"/> -->
+    <delete dir="${temp-jarfile}"/>
+    <delete dir="${temp-annotation-file-utilities}"/>
+
+    <!-- <echo message="Deleting previous distribution:  annotation-file-utilities.{jar,zip}"/> -->
+    <delete file="annotation-file-utilities.jar"/>
+    <delete file="annotation-tools.zip"/>
+  </target>
+
+  <target name="run-tests" depends="init-properties"
+          description="run tests for the annotator">
+    <exec dir="tests/" executable="make" failonerror="true">
+        <env key="XJAVAC" value="${annotations-compiler-absolute}/dist/bin/javac -g ${xjavac.args}"/>
+        <env key="JAVAC_JAR" value="${annotations-compiler-absolute}/dist/lib/javac.jar"/>
+    </exec>
+  </target>
+
+  <target name="clean-tests" description="removes generated test files">
+    <exec dir="tests/" executable="make" failonerror="true">
+      <arg value="clean"/>
+    </exec>
+    <exec dir="tests/source-extension/" executable="make" failonerror="true">
+      <arg value="clean"/>
+    </exec>
+    <exec dir="tests/system-test/" executable="make" failonerror="true">
+      <arg value="clean"/>
+    </exec>
+  </target>
+
+  <target name="bin"
+          depends="build"/>
+
+  <target name="build"
+          depends="init"
+          description="compile all source files">
+
+    <mkdir dir="bin"/>
+
+    <fileset dir="src" id="javacSrc">
+        <include name="**/*.java" />
+    </fileset>
+    <pathconvert property="cmdTxts" refid="javacSrc" pathsep=" " />
+
+      <java fork="true"
+            failonerror="true"
+            classpathref="libpath"
+            classname="com.sun.tools.javac.Main">
+          <arg value="-Xlint:-options"/>
+          <arg value="-Werror"/>
+          <arg value="-g"/>
+          <arg line="-sourcepath src"/>
+          <arg line="-d bin"/>
+          <arg line="${cmdTxts}"/>
+          <arg line="-version"/>
+          <!-- To prevent a cyclic dependency with the Checker
+               Framework, ignore type annotations in comments here.
+               Separate targets exist to check the qualifiers. -->
+          <arg line="-XDTA:noannotationsincomments"/>
+          <!-- Make sure we only have Java 7 source code and generate Java 7 bytecode. -->
+          <arg value="-source"/>
+          <arg value="7"/>
+          <arg value="-target"/>
+          <arg value="7"/>
+      </java>
+
+  </target>
+
+  <!-- Boilerplate to set jsr308javac property. Is there a better way? -->
+  <property environment="env"/>
+  <condition property="isUnix">
+    <os family="unix" />
+  </condition>
+  <condition property="isWindows">
+    <os family="windows" />
+  </condition>
+  <target name="init-jsr308javac-unix" if="isUnix">
+    <property name="jsr308javac" value="${env.CHECKERFRAMEWORK}/checker/bin/javac" />
+  </target>
+  <target name="init-jsr308javac-windows" if="isWindows">
+    <property name="jsr308javac" value="${env.CHECKERFRAMEWORK}/checker/bin/javac.bat" />
+  </target>
+
+  <target name="check-nullness"
+          description="Check for nullness errors."
+          depends="init,clean,init-jsr308javac-unix,init-jsr308javac-windows">
+    <mkdir dir="bin"/>
+    <javac srcdir="src" destdir="bin" debug="true" encoding="8859_1"
+           fork="yes"
+           executable="${jsr308javac}">
+      <compilerarg value="-version"/>
+      <compilerarg line="-target 5"/>
+      <compilerarg line="-processor org.checkerframework.checker.nonnull.NonNullFbcChecker"/>
+      <compilerarg value="-implicit:class"/>
+      <compilerarg line="-Awarns -Xmaxwarns 10000"/>
+      <classpath>
+        <path refid="libpath"/>
+        <pathelement location="${env.annotations}/checker/dist/checker.jar"/>
+      </classpath>
+    </javac>
+  </target>
+
+
+  <!-- This tags table includes the scene library. -->
+  <target name="etags" depends="tags">
+  </target>
+  <target name="tags"
+          description="create Emacs TAGS table, including scene-lib files">
+    <exec executable="/bin/sh" failonerror="true">
+      <arg value="-c"/>
+      <arg value="etags `find src -name '*.java' | sort-directory-order` `find ../scene-lib -name '*.java' | sort-directory-order`"/>
+    </exec>
+  </target>
+
+</project>
diff --git a/annotation-file-utilities/changelog.html b/annotation-file-utilities/changelog.html
new file mode 100644
index 0000000..081f896
--- /dev/null
+++ b/annotation-file-utilities/changelog.html
@@ -0,0 +1,852 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Annotation File Utilities Changelog</title>
+</head>
+<body>
+<h1>Annotation File Utilities Changelog</h1>
+
+<p>
+This is the changelog for the
+<a href="./">Annotation File Utilities</a>.
+</p>
+
+<ul>
+  <li>
+    Version 3.6.45 (released September 5, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.44 (released August 3, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.43 (released July 3, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.42 (released June 1, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.41 (released May 1, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.40 (released April 3, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.39 (released March 1, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.38 (released Jan 20, 2017):
+    <ul>
+        <li> The Annotation File Utilities webpage has moved to
+	<a href="https://checkerframework.org/annotation-file-utilities/">https://checkerframework.org/annotation-file-utilities/</a>.
+             Old URLs should redirect to the new one, but please update your links
+             and let us know if any old links are broken rather than redirecting.</li>
+    </ul>
+  </li>
+    <li>
+    Version 3.6.37 (released Jan 3, 2017):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+    <li>
+    Version 3.6.36 (released Dec 1, 2016):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+    <li>
+    Version 3.6.35 (released Nov 2, 2016):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+    <li>
+    Version 3.6.34 (released Oct 3, 2016):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+    <li>
+    Version 3.6.33 (released Sep 16, 2016):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+    <li>
+    Version 3.6.32 (released Sep 1, 2016):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.31 (released August 1, 2016):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.30 (released July 1, 2016):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.29 (released Jun 1, 2016):
+    <ul>
+      <li>
+        extract-annotations no longer puts package annotations on non-existent class,
+        fixing Issue <a href="https://github.com/typetools/annotation-tools/issues/117">#117</a>.
+      </li>
+      <li>
+        Closes issue <a href="https://github.com/typetools/annotation-tools/issues/120">#120</a>.
+      </li>
+    </ul>
+    Version 3.6.28 (released May 1, 2016):
+    <ul>
+      <li>
+	<code>AScene</code> now has a copy constructor and implements
+	<code>Cloneable</code>.
+      </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.27 (released Apr 1, 2016):
+    <ul>
+      <li>
+	The annotation file format permits use of a class name, in addition to
+	<code>&lt;init&gt;</code>, to specify a constructor.
+      </li>
+      <li>
+	Closes issues
+	<a href="https://github.com/typetools/annotation-tools/issues/70">#70</a>
+	and
+	<a href="https://github.com/typetools/annotation-tools/issues/88">#88</a>.
+      </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.26 (released Mar 1, 2016):
+    <ul>
+        <li>Avoids reinserting an annotation that already exists in comments, fixing Issue
+          <a href="https://github.com/typetools/annotation-tools/issues/114">#114</a>.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.25 (released Feb 1, 2016):
+    <ul>
+        <li>Uses system javac to compile the executable program, thus enabling Java 7 execution.</li>
+        <li>Clears set of required imports after processing each source file, fixing Issue
+          <a href="https://github.com/typetools/annotation-tools/issues/111">#111</a>.</li>
+        <li>Escapes String fields of annotations, fixing Issue
+          <a href="https://github.com/typetools/annotation-tools/issues/112">#112</a>.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.24 (released Jan 4, 2016):
+    <ul>
+        <li>Fixes Issue
+          <a href="https://github.com/typetools/annotation-tools/issues/108">#108</a>
+          by adding a <code>LICENSE.txt</code> file.</li>
+        <li>Fixes offset calculation for wide JVM instructions.</li>
+        <li>Makes minor improvements to build process.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.23 (released Dec 1, 2015):
+    <ul>
+        <li>Fixes Issue
+        <a href="https://github.com/typetools/annotation-tools/issues/106">#106</a>.</li>
+        <li>Makes minor improvements to documentation and build process.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.22 (released Nov 9, 2015):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.21 (released Oct 24, 2015):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.20 (released Oct 8, 2015):
+    <ul>
+        <li>Minor bug fixes and improvements.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.19 (released Sep 1, 2015):
+    <ul>
+        <li>Avoids importing the same annotation name from multiple packages (Issue
+        <a href="https://github.com/typetools/annotation-tools/issues/101">#101</a>).</li>
+        <li>Fixes some bugs (including Issues
+        <a href="https://github.com/typetools/annotation-tools/issues/100">#100</a> and
+        <a href="https://github.com/typetools/annotation-tools/issues/102">#102</a>)
+        pertaining to <code>extends</code> clause generation.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.18 (released Aug 4, 2015):
+    <ul>
+        <li>
+	  Moved the Annotation Tools version control repository from Google Code
+	  to GitHub, and from the Mercurial version control system to Git.
+          If you have cloned the version control repository, then discard your
+          old clone and create a new one using this command:
+	  <pre>  git clone https://github.com/typetools/annotation-tools.git</pre>
+        </li>
+        <li>Fixes some errors involving default class and method bound insertion.</li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.17 (released July 1, 2015):
+    <ul>
+        <li>
+          Minor bug fixes and improvements.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.16 (released June 1, 2015):
+    <ul>
+        <li>
+          Fixes multiple crash-causing bugs in <code>insert-annotations-to-source</code>.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.15 (released May 1, 2015):
+    <ul>
+        <li>
+          Allows annotations on constituents of an intersection type cast without the use of the AST path format.
+        </li>
+        <li>
+          Fixes assorted bugs in <code>insert-annotations</code>.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.14 (released April 17, 2015):
+    <ul>
+        <li>
+          Fixes numerous minor bugs in <code>insert-annotations</code> and <code>insert-annotations-to-source</code>.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.13 (released March 2, 2015):
+    <ul>
+        <li>
+          Extends support for lambda expressions, member references, and type arguments of static method calls and member references to <code>extract-annotations</code> and <code>insert-annotations</code>.
+        </li>
+        <li>
+          Slightly changes JAIF format for the new features.  (See file format documentation for details.)
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.12 (released January 30, 2015):
+    <ul>
+        <li>
+          Adds support for lambda expressions, member references, and type arguments of static method calls and member references to <code>insert-annotations-to-source</code>.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.11 (released December 19, 2014):
+    <ul>
+        <li>
+          Fixes more insertion logic bugs, including errors in handling generic arrays.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.10 (released November 26, 2014):
+    <ul>
+        <li>
+          Fixes many bugs, mostly in AST path-based insertion logic.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.9 (released October 30, 2014):
+    <ul>
+        <li>
+          Minor bug fixes and improvements.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.8 (released September 24, 2014):
+    <ul>
+        <li>
+          Fixes numerous bugs in AST path-based insertion logic.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.7 (released August 1, 2014):
+    <ul>
+        <li>
+          Improves Java 9 support.
+        </li>
+        <li>
+          Fills in some previously missing cases for AST path-based insertion specifications.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.6 (released July 1, 2014):
+    <ul>
+        <li>
+          Fixes bug affecting inner array types of new[] expressions in the AST path-based insertion logic.
+        </li>
+        <li>
+          Fixes JAIF generation bugs involving insert-annotation and insert typecast within methods and fields.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.5 (released June 2, 2014):
+    <ul>
+        <li>
+          Generates extends bounds when necessary for annotation insertion.
+        </li>
+        <li>
+          Fixes assorted bugs in the AST path-based insertion logic.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.4 (released May 1, 2014):
+    <ul>
+        <li>
+          Expands AST path support to cover every AST node within a class declaration.
+        </li>
+        <li>
+          Abbreviates enum tags for annotations if <code>--abbreviate</code> is selected.
+        </li>
+        <li>
+          Generates explicit default constructors when necessary for annotation insertion.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.3 (released Apr 2, 2014):
+    <ul>
+        <li>
+          Eliminates insert-annotation problems with handling Java 8 class files.
+        </li>
+        <li>
+          Corrects annotation placement within a qualified type name.
+        </li>
+        <li>
+          Fills in gaps in AST Path API.  All (Java 7) non-declaration nodes can now be identified by a path from an enclosing declaration.
+        </li>
+        <li>
+          Expands scene-lib API with visitor framework and new class <code>APIIndex</code>, which provides a cache that maps Java abstract syntax tree nodes to AST paths as defined in the annotation file format specification.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.2 (released Mar 5, 2014):
+    <ul>
+        <li>
+          Expands scene-lib API with visitor framework and new class <code>APIIndex</code>, which provides a cache that maps Java abstract syntax tree nodes to AST paths as defined in the annotation file format specification.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.6.1 (released Feb 19, 2014):
+    <ul>
+        <li>
+          Adds missing cases for AST path-based annotation/cast insertion.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.9 (released Jan 2, 2014):
+    <ul>
+        <li>
+          Fixes assorted minor bugs.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.8 (released Dec 9, 2013):
+    <ul>
+        <li>
+          Creates output directory in current working directory (by default), rather than relative to input pathname.
+        </li>
+        <li>
+          Recognizes "instanceinit" keyword in JAIF for adding annotations inside instance initializers.
+        </li>
+        <li>
+          Corrects syntax of results of inserting annotations on bare array literals.
+        </li>
+        <li>
+          More fully treats enum, interface, and annotation declarations in accordance with the rules for class declarations.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.7 (released Nov 15, 2013):
+    <ul>
+        <li>
+          Handles type paths with INNER_TYPE.
+        </li>
+        <li>
+          Disallows annotations on generic array type bounds and on wildcard bounds in "instanceof" expressions, since Java 8 prohibits type bounds in those locations.
+        </li>
+        <li>
+          Vastly improves performance when index file covers numerous source files, by filtering out irrelevant insertions at an earlier stage.
+        </li>
+        <li>
+          Avoids stack overflow during regex matching.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.6 (released Oct 23, 2013):
+    <ul>
+        <li>
+          Adds "insert-annotation" to JAIF format, allowing the use of AST paths to specify source locations for annotation insertion (which previously was possible only for typecast insertions).
+        </li>
+        <li>
+          For consistency with <code>extract-annotations</code>, <code>insert-annotations-to-source</code> now matches generic method types in the source with JAIF specifications for their least upper bounds.
+        </li>
+        <li>
+          Allows annotations on inner type declarations of parameterized types.
+        </li>
+        <li>
+          Avoids spurious code location matches on anonymous inner subclasses of an outer class.
+        </li>
+        <li>
+          Works around Java regex bug that on some input causes an infinite loop in the Matcher.
+        </li>
+        <li>
+          Fixes many other bugs, most involving receiver insertions.
+        </li>
+        <li>
+          No longer aborts on (non-fatal) warnings found during the <code>javac</code> type-checking phase of <code>insert-annotations-to-source</code>.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.5 (released Aug 28, 2013):
+    <ul>
+        <li>
+          Allows annotation insertion at constructor "return type" as per the specification.
+        </li>
+        <li>
+          Fixes assorted bugs dealing with type qualification, inner classes, and receiver insertion.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.4 (released Aug 1, 2013):
+    <ul>
+        <li>
+          Allow the use of command-line argument files.  Argument file rules are identical to those of javac.
+        </li>
+        <li>
+          The Annotation File Utilities now appropriately handles vararg parameters.
+        </li>
+        <li>
+          Minor documentation improvements.
+        </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.3 (released May 1, 2013):
+    <ul>
+      <li>
+        Don't depend on bootstrap javac files.
+      </li>
+      <li>
+        Other minor improvements.
+      </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.2 (released April 4, 2013):
+    <ul>
+      <li>
+        Allow annotations on compound types of a cast insertion.
+      </li>
+      <li>
+        Added <code>--print_error_stack</code> command-line option to
+        <code>insert-annotations-to-source</code> tool to print a stack trace on
+        an error.
+      </li>
+      <li>
+        Various bug fixes.
+      </li>
+      <li>
+        Improvements to the Annotation File Format manual.
+        <ul>
+          <li>
+            Clarified "Grammar" section by reordering and adding more details.
+          </li>
+          <li>
+            Added "Design and Implementation Details" section.
+          </li>
+        </ul>
+      </li>
+    </ul>
+  </li>
+  <li>
+    Version 3.5.1 (released March 1, 2013):
+    <ul>
+      <li>
+        Small improvements to error messages when parsing JAIF files and for
+        illegal source code insertions.
+      </li>
+      <li>
+        Installation instruction and manual improvements.
+      </li>
+      <li>
+        Better handling of source code insertions in anonymous and local
+        classes.
+        <ul>
+          <li>
+            Allow source code insertions in a local class (a class defined
+            within a method).
+          </li>
+          <li>
+            Bug fixes for anonymous and local class indexes.
+          </li>
+          <li>
+            Don't traverse into nested classes when calculating a source code
+            index.
+          </li>
+        </ul>
+      </li>
+      <li>
+        Improved support for source code method declaration receiver parameter
+        insertions.
+        <ul>
+          <li>
+            Use the full type name (Outer.Inner1.Inner2) for receiver insertions
+            in inner classes.
+          </li>
+          <li>
+            Allow annotation insertions on compound types of method declaration
+            receiver parameters.
+          </li>
+        </ul>
+      </li>
+      <li>
+        Insert source code annotations in the semantically correct place for
+        qualified types.
+      </li>
+      <li>
+        For bytecode annotation extraction and insertion read and put "local"
+        type annotations in the Code attribute, not the Method attribute.
+      </li>
+    </ul>
+  </li>
+
+  <li>
+    Version 3.5 (released February 1, 2013):
+    <ul>
+      <li>
+        Updated to JSR308 bytecode representation and receiver parameter syntax.
+      </li>
+      <li>
+        Support source code insertion of annotated casts.
+      </li>
+      <li>
+        Support adding annotations to bytecode for type casts with intersection
+        types.
+      </li>
+    </ul>
+  </li>
+
+  <li>
+    Version 3.4 (released September 11, 2012):
+    <ul>
+      <li>
+	Adapted to underlying changes in JSR 308.
+      </li>
+      <li>
+	Support .class literals as annotation attributes.
+      </li>
+    </ul>
+  </li>
+
+  <li>
+    Version 3.3 (released September 20, 2011):
+    <ul>
+      <li>
+	Improved support for annotations on array creation expressions.
+      </li>
+      <li>
+	Small tweaks to the code and documentation.
+      </li>
+    </ul>
+  </li>
+
+  <li>
+  Version 3.2 (released June 18, 2011):
+    <ul>
+      <li>
+	Support source code indexes, in contrast to the bytecode
+	centric indexes supported so far, which makes it easier for
+	source code centric tools to emit AFU files.
+	See Section "Support for source code indexes" in the
+	<a href="annotation-file-format.html">manual</a>.
+      </li>
+      <li>
+	Support annotating the bounds of class and method type
+	parameters and of wildcards, in particular also support
+	implicit bounds and make them explicit.
+	Also support adding declaration annotations on type parameters.
+      </li>
+      <li>
+	Better support for anonymous inner classes.
+      </li>
+      <li>
+	Improve handling of already existing annotations, in
+	particular for generic types.
+      </li>
+      <li>
+	Consistently do matching on methods using the erased signature.
+      </li>
+      <li>
+	Specify annotations on extends and implements clauses.
+      </li>
+      <li>
+	Handle interfaces, enums, etc. like classes.
+      </li>
+      <li>
+	Numerous small bug fixes and improvements.
+      </li>
+    </ul>
+  </li>
+
+  <li>
+  Version 3.1 (released September 18, 2010):
+    <ul>
+      <li>
+        Don't duplicate annotations that are in the source already.
+      </li>
+      <li>
+        Add <code>--omit-annotation</code> option to omit a given annotation
+        (example: <code>@ThisMutable</code>, which is the default and need not be
+        inserted in source files).
+      </li>
+      <li>
+        Bug fixes related to inner classes, array levels, multiple methods
+        with same signature but different return type (impossible in Java
+        code, but can occur in class files), parameter indices.
+      </li>
+      <li>
+        Switch from utilMDE library to
+        <a href="https://github.com/mernst/plume-lib">plume-lib</a>.
+      </li>
+    </ul>
+  </li>
+
+  <li>
+  Version 3.0 (released December 21, 2009):
+    <ul>
+      <li>
+      Handle meta-annotations.
+      Previously, the Annotation File Utilities didn't handle meta-annotations
+      (annotations that are written on an annotation type declaration); in fact,
+      AFU simply fabricated retention information.  Now, meta-annotations are
+      handled uniformly with other annotations.
+      </li>
+
+      <li>
+      Distinguish type annotations from declaration annotations.
+      Previously, AFU assumed that all annotations were type annotations.  Now,
+      this information is looked up from the meta-annotations and is
+      distinguished in the file format (see below).
+      </li>
+
+      <li>
+      Read annotations from classfiles.
+      Previously, AFU created partial information for annotations in an ad hoc
+      way, and classfile reading did not work.  (The focus was on inserting
+      annotations in class files, not reading annotations from class files.)
+      Now, classfile reading is operational, and it is often not necessary to
+      declare annotations in an annotation file.  However, if the annotation is
+      declared in the annotation file, it should be consistent with its
+      declaration in the class file.
+      </li>
+
+      <li>Changes to annotation file format.
+
+	<ul>
+	  <li>Indicating meta-annotations
+
+	    <pre>  annotation visible @Nullable:</pre>
+
+	      becomes
+
+	    <pre>  annotation @Nullable: @Retention(value=RUNTIME)</pre>
+
+	      and if it's a type annotation, it should be
+
+	    <pre>  annotation @Nullable: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})</pre>
+	  </li>
+	  <li>Locations for type and declaration annotations.
+
+	If p2.B is a declaration annotation on a method, it is written as before:
+
+	    <pre>  method foo(Ljava/util/Set;)V: @p2.B</pre>
+
+	If p2.B is a type annotation on a method return type, it is written in a
+	new location:
+
+	    <pre>  method foo(Ljava/util/Set;)V:
+    return: @p2.B</pre>
+
+	Fields and method parameters are treated similarly, but with
+	<code>type</code> marker instead of <code>return</code>.  So if p2.A is a
+	declaration annotation targeting the field, while p2.B is a type
+	annotation on the field, the field would be declared as:
+
+    <pre> field s: @p2.A
+   type: @p2.B</pre>
+
+	Furthermore, <code>inner-type:</code> is permitted only after a keyword
+	that introduces a type, such as <code>type</code>, <code>return</code>, and
+	<code>receiver</code>, to prevent confusion (though the file format
+	becomes slightly more verbose).
+
+	  </li>
+	  <li>Permit qualified names in several places that only identifiers were
+	permitted before.  This enables use of names that contain periods, such
+	as inner classes, and also fully-qualified names.
+
+	  </li>
+	  <li>Permit two shorthands in annotation uses, as in Java.  If there is a
+	single field named "value", then its name may be omitted: @A(1) instead
+	of @A(value=1).  If an array has only one element, the braces may be
+	omitted:  @B(value=1) instead of @B(value={1}).  Both
+	shorthands may be combined:  @B(1) instead of @B(value={1}).
+	  </li>
+        </ul>
+      </li>
+    </ul>
+  </li>
+
+  <li>
+  Version 2.3.3 (released August 18, 2009):
+    <ul>
+      <li>Fix a few bugs.</li>
+    </ul>
+  </li>
+
+  <li>
+  Version 2.3.2 (released August 14, 2009):
+    <ul>
+      <li>Relax file format:  receiver may precede parameters (in spec &amp;
+          implementation), parameters may omit "#" (in implementation only)</li>
+    </ul>
+  </li>
+
+  <li>
+  Version 2.3.1 (released August 12, 2009):
+    <ul>
+      <li>Fix many bugs.</li>
+    </ul>
+  </li>
+
+  <li>
+  Version 2.3 (released July 29, 2009):
+    <ul>
+      <li>Support <code>-i --in-place</code> argument.</li>
+      <li>Fix incorrect array brackets in tests.</li>
+      <li>Fix inserting annotations on receivers.</li>
+      <li>Improve documentation.</li>
+      <li>Improve error handling.</li>
+    </ul>
+  </li>
+
+  <li>
+  Version 2.2 (released December 7, 2008):
+    <ul>
+      <li>Permit Main to process multiple files at a time.</li>
+      <li>Don't re-insert annotations that are already present.</li>
+      <li>Add a space before annotation if there isn't one there.</li>
+      <li>Fix problems with array syntax; now adheres to the current JSR
+          308 syntax.</li>
+      <li>Improve error handling.</li>
+      <li>Code reorganization:  remove subdirectories and classes that
+          added complexity without commensurate functionality.</li>
+    </ul>
+  </li>
+
+  <li>
+  Version 2.1.2 (released April 28, 2008):
+  Minor updates to the &ldquo;Annotation File Format Specification&rdquo; (<a
+    href="https://checkerframework.org/annotation-file-utilities/annotation-file-format.pdf">PDF</a>, <a
+    href="https://checkerframework.org/annotation-file-utilities/annotation-file-format.html">HTML</a>).
+  </li>
+
+  <li>
+  Version 2.1.1 (released May 31, 2007):
+  Added support for two new annotation target_types; consult the
+  classfile specification (<a
+  href="https://checkerframework.org/jsr308/specification/java-annotation-design.pdf">PDF</a>,
+  <a href="https://checkerframework.org/jsr308/specification/java-annotation-design.html">HTML</a>).
+  </li>
+
+  <li>
+  Version 2.1 (released May 4, 2007):
+  Modified overall setup of distribution to be easier to understand.
+  </li>
+
+  <li>
+  Version 2.0 (released May 1, 2007):
+  Initial public release.
+  </li>
+
+  <li>
+  Version 1.0 (released March 19, 2007):
+  Preliminary release.
+  The <code>insert-to-annotations-source</code> tool only
+  operates on class, field and method signatures.
+  </li>
+
+</ul>
+
+<p>
+The original developer (through version 2.1.2) was Jaime Quinonez.
+</p>
+
+
+</body>
+</html>
+
+
+<!--  LocalWords:  utils bashrc tcsh tcshrc cshrc classpath AFU Ljava
+ -->
diff --git a/annotation-file-utilities/design.tex b/annotation-file-utilities/design.tex
new file mode 100644
index 0000000..d1298da
--- /dev/null
+++ b/annotation-file-utilities/design.tex
@@ -0,0 +1,464 @@
+\documentclass{article}
+\title{AFU v4 Design}
+\author{Dan Brown}
+
+\usepackage{graphics}
+\usepackage{verbatim}
+\newenvironment{code}{\footnotesize\verbatim}{\endverbatim\normalsize}
+\newenvironment{jcode}{\footnotesize\verbatim}{\endverbatim\normalsize}
+
+\begin{document}
+\maketitle
+
+\section{Design Considerations}
+
+The Annotation File Utilities (AFU) include programs for extracting
+annotations from Java class files and inserting annotations into Java
+source or class files, in accordance with the Java 8 language and virtual
+machine specification.  This
+document uses AFU in the singular, in reference to the source
+insertion program except where otherwise noted.
+
+The AFU suffers from multiple design flaws that impede performance and
+maintainability, including:
+
+\begin{description}
+\item[Unstructured text modification.]  The AFU inserts
+annotations into source text instead of modifying syntax trees.
+The use of source positions rather than tree paths for indicating
+insertion placement has led to code that is fragile and hence difficult
+to maintain.
+\item[Non-uniform logic.]  There are two distinct ways to
+identify where to insert annotations into the source, each having its
+own distinct data structures and logic.  Consequently, it is possible to
+have inconsistent results for different representations of the same
+location.
+\item[Inefficient traversal.]  The search strategy is
+generate-and-test: it chooses an AST node in a preorder traversal and
+checks every insertion specification relevant to the class (or package)
+to determine whether it applies to the current node.  Furthermore, since
+the implementations of some tests contain recursive calls going up the
+path to the root, these tests can be repeated many times over for some
+nodes.
+\end{description}
+
+The proposed design is aimed at mitigating these problems.  It makes
+the traversal a single pass over the annotations, with limited
+re-visitation of AST nodes.  It reduces the chance for inconsistency
+between formats by finding the AST node that corresponds to a code
+location given in the scene before determining the insertion position.
+It modifies an AST data structure rather than the source itself,
+preserving formatting and commentary by incorporating non-leaf text
+(i.e.\ whitespace and comments) into the representation of the AST.
+
+\section{Current AFU}
+
+The current AFU source code (3.6.16 as of 11 June 2015) is organized
+into four packages (not counting the \texttt{asmx} and
+\texttt{scene\_lib} sub-projects):
+\begin{itemize}
+\item  \texttt{annotator}, containing the main class and a class
+representing source text;
+\item  \texttt{annotator.find},\ containing the classes that implement
+the core insertion logic;
+\item  \texttt{annotator.scanner},\ containing support classes for
+\texttt{annotator.find};\ and
+\item  \texttt{annotator.specification},\ containing classes that
+manage intermediate representations of the insertions specified in a
+Java Annotation Index File (JAIF).
+\end{itemize}
+Should the proposed redesign be implemented, it is anticipated that the
+first will be simplified somewhat and modified to reflect changes in the
+other packages, the second will be radically simplified, the third will
+remain the same, and the fourth will be eliminated.
+
+The details of \texttt{annotator.scanner}\ are not discussed here, since
+the proposed redesign will neither change them nor be influenced by
+them.  Of the classes in \texttt{annotator.specification},\ only
+\texttt{IndexFileSpecification},\ which extracts \texttt{Insertion}s
+from \texttt{scene-lib}'s JAIF representation
+(\texttt{annotations.el.AScene}), is relevant to the redesign.
+
+By far the largest and most complex package is \texttt{annotator.find}.
+The insertion logic is distributed among \texttt{TreeFinder} and
+numerous implementations of the \texttt{Criterion} and
+\texttt{Insertion} interfaces.  (A \texttt{Criterion} is a reified
+condition that provides a method
+\texttt{boolean isSatisfiedBy(TreePath path)},
+and an \texttt{Insertion} includes matching criteria, output parameters,
+and methods for generating the source text to be inserted.)
+
+A simplified outline of the basic flow of the program is as follows:
+
+\begin{enumerate}
+\item  Interpret each JAIF as a ``scene'' (\texttt{AScene}), then invoke
+the \texttt{parse} method in \texttt{IndexFileSpecification} to extract
+the indicated \texttt{Insertion}s from the scene.
+\item  Parse each Java source file to obtain a (type-checked) AST.
+\item  Create an empty map from (Integer) source positions to
+insertions.
+\item  For each node in the AST in pre-order:
+\begin{itemize}
+\item  For each extracted \texttt{Insertion} that is relevant to the
+immediately enclosing class (or package for compilation unit nodes):
+\begin{itemize}
+\item  If the path from the root to the current node satisfies every
+\texttt{Criterion} for the \texttt{Insertion}:
+\begin{enumerate}
+\item  Find the appropriate source position for the annotation.
+\item  Add a mapping from the position to the \texttt{Insertion} to the
+map.
+\item  Remove the insertion from the extracted set.
+\end{enumerate}
+\end{itemize}
+\end{itemize}
+\item  For each position-to-insertion mapping in descending order by
+position:
+\begin{itemize}
+\item  Generate insertion text and insert at indicated position.
+\end{itemize}
+\item  Emit modified source code.
+\end{enumerate}
+
+\section{Proposed Revisions}
+
+\begin{figure}[ht]
+\begin{center}
+\resizebox{\columnwidth}{!}{\includegraphics{dataflow.png}}
+\end{center}
+\caption{
+Data flow between components.  Boxes represent sources and sinks;
+ellipses represent Java objects.  Bold lines mark new classes and data
+paths, and dashed lines indicate current data flows to be bypassed.
+}
+\label{fig:dataflow}
+\end{figure}
+
+Figure~\ref{fig:dataflow} gives an overview of proposed changes to the
+flow of data between classes.  First, the current re-interpretation of
+the scene as a ``specification'' and subsequent extraction of
+\texttt{Criteria} will be eliminated, as the necessary information can
+be determined directly from the \texttt{Scene}.  Second, the source text
+will be incorporated into the augmented AST in a more convenient form,
+so there will no need to refer back to the source itself.
+
+The division of responsibilities among classes will remain largely the
+same, with the major differences corresponding to the changes in the
+data flow.  First, the annotation locations will be read from the
+\texttt{Scene} and mapped to the corresponding path in the (augmented)
+AST (if any).  Second, since \texttt{AAST} will incorporate the salient
+capabilities of \texttt{JCTree} and \texttt{Source}, it will cover the
+responsibilities of both classes.
+
+Many of the current supporting classes will be retained as they are.  In
+particular, the new AFU will continue to rely on javac's AST classes and
+parser, and \texttt{scene-lib} probably will need little or no
+modification.  The \texttt{annotator.find} package will be radically
+simplified with the elimination of the \texttt{Criterion} and
+(possibly) \texttt{Insertion} hierarchies, as the insertion location
+within the AST can be determined directly from the \texttt{Scene}.
+
+The redesign can be broken down into two independent parts:
+\begin{description}
+\item{Path-based positioning.} Eliminate the \texttt{Criterion}
+hierarchy; determine the insertion location and the text to be inserted
+during (a single) scene traversal; let the scene traversal rather than
+an AST traversal be the outer loop.  Advantages: since insertion is
+based on the node rather than a numeric position, there is only one
+positioning logic (even if there are still two matching objects); an
+entire pass and a large amount of intermediate data are eliminated, thus
+improving time and space efficiency.
+\item{Tree transformation.}  Eliminate the \texttt{Insertion} hierarchy;
+instead of calculating numeric positions, apply a transformation to the
+appropriate subtree.  Advantage: the logic of tree transformation is
+both closer to the specification and easier to comprehend (and maintain)
+than the logic of finding numeric positions for inserting text by
+reverse order.
+\end{description}
+
+While these have no obvious technical disadvantages with respect to the
+existing AFU, the payoff must be weighed against the costs.  The current
+codebase is mostly stable at this point, so any increase in reliability
+will be small (though for verification software, a jump from, say,
+99.99\% to 99.9999\% correct makes an enormous difference).
+Fortunately, the AFU's extensive test suite will make it clear how
+quickly a revised implementation achieves the same level of reliability.
+
+\subsection{Path-based Positioning}
+
+The current AFU creates transforms the scene into a set of
+\texttt{Insertions}, each with its own \texttt{Criteria} (a collection
+of objects of various \texttt{Criterion} subclasses).  In doing so, it
+throws away information about hierarchical relationships that could be
+used to guide the search for matching locations in the code.
+
+\begin{figure}[ht]
+\begin{center}
+\resizebox{156pt}{!}{\includegraphics{corresp.png}}
+\end{center}
+\caption{
+Correspondence between scene and AST.
+}
+\label{fig:corresp}
+\end{figure}
+
+It makes more sense to traverse an AST in parallel (conceptually, not
+necessarily by different processors) with a scene that refers to it.
+For example, if a class specification (\texttt{AClass}) in a scene
+corresponds to a class node in an AST, each time one of the method
+specifications (\texttt{AMethod}) is visited, the corresponding method
+node (see Figure~\ref{fig:corresp}) should be visited as well to see
+whether any insertions under the \texttt{AMethod} apply to it.  This
+strategy not only obviates an additional pass but avoids much useless
+test repetition, as there is no need to test \emph{every} potential
+insertion against \emph{every} AST node encountered before the correct
+insertion site.  With this change, it becomes possible to generate
+\texttt{Insertion}s on the fly---or perhaps even to go ahead and insert
+text, though managing order dependencies would take some additional
+work.
+
+The main loop of the program thus will become a pre-order traversal of
+\texttt{AScene} rather than of \texttt{JCTree}, although the scene
+traversal will explore corresponding parts of the AST.  In other words:
+the process will be guided by the annotations to be inserted rather than
+by the shape of the AST.  Hence, each specified annotation will be
+visited exactly once.  The information currently used to create
+\texttt{Criteria} for the insertions can instead be used to directly
+determine the path (if any) in the AST to which the current scene
+element corresponds.
+
+\subsection{Tree Transformation}
+
+The current AFU finds numeric source positions in trees and inserts
+annotations directly into the source code.  The position-finding logic
+is therefore applied at a time other than insertion, introducing
+insertion order dependencies and making it harder to find the source of
+a bug.
+
+A new class hierarchy, the ``Augmented AST'' (\texttt{AAST}), will
+directly manage non-leaf text--that is, inter-leaf text (whitespace and
+comments) and intra-branch text (implicit or contained in branch node
+data members).  It will be an implementation of
+\texttt{com.sun.source.Tree} that provides needed bits from
+\texttt{com.sun.tools.javac.tree.JCTree} and defines a parallel subclass
+hierarchy.  AAST and subclasses provide methods to retrieve the
+inter-leaf text segments that precede and follow an AST node and
+printing methods that preserve the formatting of the source code.
+
+Non-leaf text will be stored in three types of locations in an AAST:
+\begin{enumerate}
+\item  Each leaf node will store any non-code text that immediately
+follows.
+\item  For branch nodes, for every implicit text segment or data member
+that is represented in the source code but not in a descendant node, the
+node will store any whitespace and comments that follow, unless the
+content is at the end of the node's text.
+\item  The computation unit node will store any text that precedes the
+beginning of the Java code.
+\end{enumerate}
+In this way, all text segment references occur exactly once in the AST,
+and annotations can be inserted at the front with no need to take
+preceding text into account.
+
+An annotation insertion will consist of a transformation of a tree node.
+If no additional code is to be generated, the transformation is simple:
+\begin{itemize}
+\item  For type annotations, the parent AST node of the element to be
+annotated adds the optional annotation node if it is absent, then adds
+the annotation to the annotation node.
+\item  For declaration annotations, the annotation is added onto the
+front of a declaration's list of modifiers.
+\end{itemize}
+Cases that involve code generation are more complex.
+Figures~\ref{fig:nocast} and \ref{fig:typecast} give an example (pre-
+and post-insertion, respectively) of a transformation that requires a
+typecast to be generated and inserted.  Note that previously existing
+non-leaf text segments remain unaltered.
+
+\begin{figure}[ht]
+\begin{center}
+\resizebox{261pt}{!}{\includegraphics{nocast.png}}
+\end{center}
+\caption{
+Augmented subgraph for \texttt{int i = 0}.  Text of leaf nodes and text
+representing elements of branch nodes are connected with solid arrows,
+and inter-leaf and inter-element text segments are connected with dashed
+arrows.  The text of the subgraph can be read from the lower frontier of
+the graph.  (Underscores represent spaces here.)
+}
+\label{fig:nocast}
+\end{figure}
+
+\begin{figure}[ht]
+\begin{center}
+\resizebox{\columnwidth}{!}{\includegraphics{typecast.png}}
+\end{center}
+\caption{
+Augmented subgraph for \texttt{int i = (@A int) 0}, the result of adding
+an annotated cast \texttt{(@A int)} to the previous figure's code.  With
+respect to the graph in the previous figure, \texttt{TypeCast} has taken
+\texttt{Literal}'s place in the tree, and \texttt{Literal} has become a
+child of \texttt{TypeCast} along with the newly inserted material.
+}
+\label{fig:typecast}
+\end{figure}
+
+The other major change will be in the output generation process, which
+will depend on the \texttt{AAST} rather than directly on the source.
+The \texttt{AAST}'s pretty-printer will perform an in-order traversal
+and emit the text of each leaf interleaved with the non-leaf text.
+
+\subsection{Algorithm Summary}
+
+\begin{enumerate}
+\item  Interpret each JAIF as a ``scene'' (\texttt{AScene}).
+\item  Parse each Java source file to obtain a (type-checked) AST.
+\item  For each AST:
+\begin{enumerate}
+\item  Transform the AST into an AAST.
+\item  For each annotated element in the scene in pre-order:
+\begin{itemize}
+\item  Try to find the AAST node to which the element corresponds.
+\item  If the node exists (or can be generated):
+\begin{itemize}
+\item  Transform the subtree rooted at the node to incorporate the
+annotation.
+\end{itemize}
+\end{itemize}
+\item  Pretty-print AAST.
+\end{enumerate}
+\end{enumerate}
+
+\section{Planning}
+
+There will be some time required for integrating the two parts if they
+are implemented separately.  In particular, doing tree transformation
+separately from path-based positioning means reifying transformations
+so they can be done in a second pass.  Hence it makes more sense to
+do path-based positioning on the way to doing tree transformation.
+
+Estimated times are as follows:
+\begin{itemize}
+\item  Path-based positioning: 2.5 weeks.
+\item  Tree transformation: 3 to 3.5 weeks if done after path-based
+positioning, 4 weeks otherwise.
+\end{itemize}
+Thus, doing the full job should take about six weeks.
+
+\appendix
+
+\section{API extensions (non-exhaustive)}
+
+\begin{jcode}
+public abstract class AAST extends com.sun.source.Tree {
+  /** Interleaves non-leaf text with text serialization of AAST. */
+  public void prettyPrint(java.io.Writer w) throws java.io.IOException;
+
+  /** Insert single annotation at given path. */
+  public insertAnnotation(TreePath path, Annotation anno);
+}
+
+// main loop
+AAST insertAnnotations(annotations.el.AScene scene, AAST aast);
+\end{jcode}
+
+\newpage
+\section{Excerpts from original API}\label{sec:exc}
+
+\begin{jcode}
+public interface Tree {
+    public enum Kind {  // MUCH larger in actual interface
+        /**
+         * Used for instances of {@link BinaryTree} representing
+         * addition or string concatenation {@code +}.
+         */
+        PLUS(BinaryTree.class),
+        /**
+         * Used for instances of {@link LiteralTree} representing
+         * an integral literal expression of type {@code int}.
+         */
+        INT_LITERAL(LiteralTree.class);
+    }
+    Kind getKind();
+    /**
+     * Accept method used to implement the visitor pattern.  The
+     * visitor pattern is used to implement operations on trees.
+     *
+     * @param <R> result type of this operation.
+     * @param <D> type of additional data.
+     */
+    <R,D> R accept(TreeVisitor<R,D> visitor, D data);
+}
+public interface ExpressionTree extends Tree {}
+public interface BinaryTree extends ExpressionTree {
+    ExpressionTree getLeftOperand();
+    ExpressionTree getRightOperand();
+}
+public interface LiteralTree extends ExpressionTree {
+    Object getValue();
+}
+
+public abstract class JCTree implements Tree {
+    public int getStartPosition() {
+        return TreeInfo.getStartPos(this);
+    }
+    public static abstract class JCExpression
+            extends JCTree implements ExpressionTree {}
+    public static class JCBinary extends JCExpression implements BinaryTree {
+        private Tag opcode;
+        public JCExpression lhs;
+        public JCExpression rhs;
+        public Symbol operator;
+        protected JCBinary(Tag opcode,
+                         JCExpression lhs,
+                         JCExpression rhs,
+                         Symbol operator) {
+            this.opcode = opcode;
+            this.lhs = lhs;
+            this.rhs = rhs;
+            this.operator = operator;
+        }
+        public Kind getKind() { return TreeInfo.tagToKind(getTag()); }
+        public JCExpression getLeftOperand() { return lhs; }
+        public JCExpression getRightOperand() { return rhs; }
+        public Symbol getOperator() { return operator; }
+        @Override
+        public <R,D> R accept(TreeVisitor<R,D> v, D d) {
+            return v.visitBinary(this, d);
+        }
+    }
+    public static class JCLiteral extends JCExpression implements LiteralTree {
+        public TypeTag typetag;
+        public Object value;
+        protected JCLiteral(TypeTag typetag, Object value) {
+            this.typetag = typetag;
+            this.value = value;
+        }
+        public Kind getKind() {
+            return typetag.getKindLiteral();
+        }
+        public Object getValue() {
+            switch (typetag) {
+                case BOOLEAN:
+                    int bi = (Integer) value;
+                    return (bi != 0);
+                case CHAR:
+                    int ci = (Integer) value;
+                    char c = (char) ci;
+                    if (c != ci)
+                        throw new AssertionError("bad value for char literal");
+                    return c;
+                default:
+                    return value;
+            }
+        }
+        @Override
+        public <R,D> R accept(TreeVisitor<R,D> v, D d) {
+            return v.visitLiteral(this, d);
+        }
+    }
+}
+\end{jcode}
+
+\end{document}
+
diff --git a/annotation-file-utilities/figures/Makefile b/annotation-file-utilities/figures/Makefile
new file mode 100644
index 0000000..5a63c61
--- /dev/null
+++ b/annotation-file-utilities/figures/Makefile
@@ -0,0 +1,19 @@
+# Put user-specific changes in your own Makefile.user.
+# Make will silently continue if that file does not exist.
+-include Makefile.user
+
+
+PNGFILES = corresp.png dataflow.png nocast.png typecast.png
+
+all: ${EPSFILES} ${PDFFILES} ${PNGFILES} png-copy
+
+png-copy: ${PNGFILES}
+	cp -pf ${PNGFILES} ..
+
+%.png : %.dot
+	dot -Tpng $< > `basename $< .dot`.png
+
+clean:
+	@\rm -f *.eps
+	@\rm -f *.pdf
+	@\rm -f *.png
diff --git a/annotation-file-utilities/figures/corresp.dot b/annotation-file-utilities/figures/corresp.dot
new file mode 100644
index 0000000..ac4b50a
--- /dev/null
+++ b/annotation-file-utilities/figures/corresp.dot
@@ -0,0 +1,17 @@
+strict digraph corresp {
+graph [ratio=1,ordering=out]
+AClass
+AMethod
+ABlock
+ClassTree [shape=box]
+MethodTree [shape=box]
+BlockTree [shape=box]
+z1 [label="..."]
+z2 [label="..."]
+AClass -> AMethod -> ABlock -> z1
+ClassTree -> MethodTree -> BlockTree -> z2
+AClass -> ClassTree [style=dashed,constraint=false]
+AMethod -> MethodTree [style=dashed,constraint=false]
+ABlock -> BlockTree [style=dashed,constraint=false]
+}
+
diff --git a/annotation-file-utilities/figures/dataflow.dot b/annotation-file-utilities/figures/dataflow.dot
new file mode 100644
index 0000000..de33bb0
--- /dev/null
+++ b/annotation-file-utilities/figures/dataflow.dot
@@ -0,0 +1,28 @@
+strict digraph {
+JAIF [shape=box]
+Java [shape=box]
+"Annotated Java" [shape=box]
+"Augmented AST" [style=bold]
+Source [constraint=false]
+JAIF -> Scene
+Scene -> "Annotations\nw/ AST Paths" [style=bold]
+Java -> Source
+Source -> AST
+AST -> "Augmented AST" [style=bold]
+Source -> "Augmented AST" [style=bold]
+"Augmented AST" -> "Annotated AAST" [style=bold]
+"Augmented AST" -> "Annotations\nw/ AST Paths" [style=bold]
+"Annotation\nSpecifications" -> "Annotations\nw/ AST Paths" [style=dashed]
+"Annotation\nSpecifications" -> "Annotations w/\nCode Locations" [style=dashed]
+"Annotations\nw/ AST Paths" -> "Annotated AAST"
+"Annotated AAST" -> "Annotated Java"
+Source -> Bytecode
+Bytecode -> "Annotated AAST"
+Source -> "Annotated AAST" [style=dashed]
+Source -> "Annotated Java" [style=dashed]
+AST -> "Annotated AAST" [style=dashed]
+AST -> "Annotations\nw/ AST Paths" [style=dashed]
+"Annotations w/\nCode Locations" -> "Annotated AAST" [style=dashed]
+Scene -> "Annotation\nSpecifications" [style=dashed]
+}
+
diff --git a/annotation-file-utilities/figures/nocast.dot b/annotation-file-utilities/figures/nocast.dot
new file mode 100644
index 0000000..152508f
--- /dev/null
+++ b/annotation-file-utilities/figures/nocast.dot
@@ -0,0 +1,34 @@
+strict digraph {
+graph [ordering=out]
+
+d [shape=box,label=LocalVariableDeclaration]
+m [shape=box,label=Modifiers]
+t [shape=box,label=PrimitiveTypeTree]
+l [shape=box,label=Literal]
+e1 [shape=plaintext,label="",width=0.125]
+e2 [shape=plaintext,label="",width=0.125]
+int [shape=plaintext,width=0.125]
+i [shape=plaintext,constraint=false,width=0.125]
+b1 [shape=plaintext,label="_",width=0.125]
+b2 [shape=plaintext,label="_",constraint=false,width=0.125]
+b3 [shape=plaintext,label="_",constraint=false,width=0.125]
+q [shape=plaintext,label="=",constraint=false,width=0.125]
+z [shape=plaintext,label="0",width=0.125]
+y [shape=plaintext,label="[...]",width=0.125]
+
+subgraph {rank=same;int;b1;i;b2;q;b3;z;y}
+
+d -> m
+m -> e1
+m -> e2 [style=dashed]
+d -> t
+t -> int
+t -> b1 [style=dashed]
+d -> i
+d -> b2 [style=dashed]
+d -> q
+d -> b3 [style=dashed]
+d -> l
+l -> z
+l -> y [style=dashed]
+}
diff --git a/annotation-file-utilities/figures/scene-lib-type-hierarchy.svg b/annotation-file-utilities/figures/scene-lib-type-hierarchy.svg
new file mode 100644
index 0000000..07cccd1
--- /dev/null
+++ b/annotation-file-utilities/figures/scene-lib-type-hierarchy.svg
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
+<svg width="25cm" height="6cm" viewBox="-1 -1 901 201" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <g>
+    <rect style="fill: #ffffff" x="379" y="1" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="379" y="1" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="458.5" y="25.1333">
+      <tspan x="458.5" y="25.1333">AElement</tspan>
+    </text>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="380" y="80" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="380" y="80" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="459.5" y="104.133">
+      <tspan x="459.5" y="104.133">ADeclaration</tspan>
+    </text>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="621" y="79" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="621" y="79" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="700.5" y="103.133">
+      <tspan x="700.5" y="103.133">AExpression</tspan>
+    </text>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="740" y="159" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="740" y="159" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="819.5" y="183.133">
+      <tspan x="819.5" y="183.133">ABlock</tspan>
+    </text>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="381" y="159" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="381" y="159" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="460.5" y="183.133">
+      <tspan x="460.5" y="183.133">AMethod</tspan>
+    </text>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="140" y="81" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="140" y="81" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="219.5" y="105.133">
+      <tspan x="219.5" y="105.133">ATypeElement</tspan>
+    </text>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="1" y="159" width="179.35" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="1" y="159" width="179.35" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="90.675" y="183.133">
+      <tspan x="90.675" y="183.133">ATypeElementWithType</tspan>
+    </text>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="459.5" y1="80" x2="458.75" y2="50.7329"/>
+    <polygon style="fill: #000000" points="458.557,43.2353 463.812,53.1039 458.75,50.7329 453.815,53.3602 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="458.557,43.2353 463.812,53.1039 458.75,50.7329 453.815,53.3602 "/>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="660.75" y1="79" x2="507.73" y2="43.2169"/>
+    <polygon style="fill: #000000" points="500.427,41.5092 511.303,38.9175 507.73,43.2169 509.026,48.6548 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="500.427,41.5092 511.303,38.9175 507.73,43.2169 509.026,48.6548 "/>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="259.25" y1="81" x2="409.306" y2="43.3683"/>
+    <polygon style="fill: #000000" points="416.581,41.5439 408.098,48.8263 409.306,43.3683 405.665,39.1266 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="416.581,41.5439 408.098,48.8263 409.306,43.3683 405.665,39.1266 "/>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="779.75" y1="159" x2="709.192" y2="123.387"/>
+    <polygon style="fill: #000000" points="702.496,120.008 713.676,120.05 709.192,123.387 709.171,128.977 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="702.496,120.008 713.676,120.05 709.192,123.387 709.171,128.977 "/>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="460.5" y1="159" x2="459.75" y2="129.733"/>
+    <polygon style="fill: #000000" points="459.557,122.235 464.812,132.104 459.75,129.733 454.815,132.36 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="459.557,122.235 464.812,132.104 459.75,129.733 454.815,132.36 "/>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="135.513" y1="159" x2="210.63" y2="125.013"/>
+    <polygon style="fill: #000000" points="217.463,121.922 210.413,130.599 210.63,125.013 206.291,121.489 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="217.463,121.922 210.413,130.599 210.63,125.013 206.291,121.489 "/>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="560.45" y="159.2" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="560.45" y="159.2" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="639.95" y="183.333">
+      <tspan x="639.95" y="183.333">AField</tspan>
+    </text>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="600.2" y1="159.2" x2="508.326" y2="123.524"/>
+    <polygon style="fill: #000000" points="501.334,120.809 512.466,119.768 508.326,123.524 508.846,129.09 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="501.334,120.809 512.466,119.768 508.326,123.524 508.846,129.09 "/>
+  </g>
+  <g>
+    <rect style="fill: #ffffff" x="200.9" y="159.4" width="159" height="40"/>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x="200.9" y="159.4" width="159" height="40"/>
+    <text font-size="13.5467" style="fill: #000000;text-anchor:middle;font-family:'courier new';font-style:normal;font-weight:normal" x="280.4" y="183.533">
+      <tspan x="280.4" y="183.533">AClass</tspan>
+    </text>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="320.15" y1="159.4" x2="410.697" y2="123.581"/>
+    <polygon style="fill: #000000" points="417.671,120.823 410.211,129.15 410.697,123.581 406.533,119.852 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="417.671,120.823 410.211,129.15 410.697,123.581 406.533,119.852 "/>
+  </g>
+</svg>
diff --git a/annotation-file-utilities/figures/tool-relations.svg b/annotation-file-utilities/figures/tool-relations.svg
new file mode 100644
index 0000000..d2b0a3b
--- /dev/null
+++ b/annotation-file-utilities/figures/tool-relations.svg
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/PR-SVG-20010719/DTD/svg10.dtd">
+<svg width="8cm" height="8cm" viewBox="-1 -81 307 284" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <g>
+    <polygon style="fill: #ffffff" points="0,-80 52.2,-80 64.2,-68 64.2,2 0,2 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="0,-80 52.2,-80 64.2,-68 64.2,2 0,2 "/>
+    <polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="52.2,-80 52.2,-68 64.2,-68 "/>
+    <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="7" y="-55.1">
+      <tspan x="7" y="-55.1"></tspan>
+      <tspan x="7" y="-39.1">.java </tspan>
+      <tspan x="7" y="-23.1"></tspan>
+      <tspan x="7" y="-7.1"></tspan>
+    </text>
+  </g>
+  <g>
+    <polygon style="fill: #ffffff" points="240,-80 292.2,-80 304.2,-68 304.2,2 240,2 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="240,-80 292.2,-80 304.2,-68 304.2,2 240,2 "/>
+    <polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="292.2,-80 292.2,-68 304.2,-68 "/>
+    <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="247" y="-55.1">
+      <tspan x="247" y="-55.1"></tspan>
+      <tspan x="247" y="-39.1">.class</tspan>
+      <tspan x="247" y="-23.1"></tspan>
+      <tspan x="247" y="-7.1"></tspan>
+    </text>
+  </g>
+  <g>
+    <polygon style="fill: #ffffff" points="120,120 172.2,120 184.2,132 184.2,202 120,202 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="120,120 172.2,120 184.2,132 184.2,202 120,202 "/>
+    <polyline style="fill: none; fill-opacity:0; stroke-width: 1; stroke: #000000" points="172.2,120 172.2,132 184.2,132 "/>
+    <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="127" y="144.9">
+      <tspan x="127" y="144.9"></tspan>
+      <tspan x="127" y="160.9">.jaif </tspan>
+      <tspan x="127" y="176.9"></tspan>
+      <tspan x="127" y="192.9"></tspan>
+    </text>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="64.2" y1="-39" x2="230.264" y2="-39"/>
+    <polygon style="fill: #000000" points="237.764,-39 227.764,-34 230.264,-39 227.764,-44 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="237.764,-39 227.764,-34 230.264,-39 227.764,-44 "/>
+  </g>
+  <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="120" y1="161" x2="78.8465" y2="92.0049"/>
+  <text font-size="12.8" style="fill: #000000;text-anchor:start;font-family:monospace;font-style:normal;font-weight:normal" x="132.177" y="-44.609">
+    <tspan x="132.177" y="-44.609">javac</tspan>
+  </text>
+  <g>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2.35099e-37; stroke: #ffffff" x="0" y="60" width="138.6" height="32"/>
+    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="69.3" y="71.9">
+      <tspan x="69.3" y="71.9">insert-annotations</tspan>
+      <tspan x="69.3" y="87.9">-to-source</tspan>
+    </text>
+  </g>
+  <g>
+    <line style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" x1="61.2624" y1="60.0112" x2="36.4729" y2="10.6988"/>
+    <polygon style="fill: #000000" points="33.1043,3.99784 42.0631,10.6867 36.4729,10.6988 33.1285,15.1781 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="33.1043,3.99784 42.0631,10.6867 36.4729,10.6988 33.1285,15.1781 "/>
+  </g>
+  <g>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2.35099e-37; stroke: #ffffff" x="215.572" y="74.687" width="84.7" height="48"/>
+    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="257.922" y="94.587">
+      <tspan x="257.922" y="94.587">insert-</tspan>
+      <tspan x="257.922" y="110.587">annotations</tspan>
+    </text>
+  </g>
+  <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 184.2,161 A 133.344,133.344 0 0 0 241.696,122.113"/>
+  <g>
+    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 266.31,74.6929 A 192.351,192.351 0 0 0 272.935,11.6908"/>
+    <polygon style="fill: #000000" points="272.348,4.22225 278.427,13.6054 273.181,11.6759 268.489,14.7155 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="272.348,4.22225 278.427,13.6054 273.181,11.6759 268.489,14.7155 "/>
+  </g>
+  <g>
+    <rect style="fill: none; fill-opacity:0; stroke-width: 2.35099e-37; stroke: #ffffff" x="140" y="20" width="84.7" height="32"/>
+    <text font-size="12.8" style="fill: #000000;text-anchor:middle;font-family:monospace;font-style:normal;font-weight:normal" x="182.35" y="31.9">
+      <tspan x="182.35" y="31.9">extract-</tspan>
+      <tspan x="182.35" y="47.9">annotations</tspan>
+    </text>
+  </g>
+  <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 272.1,2 A 185.385,185.385 0 0 0 209.031,20.008"/>
+  <g>
+    <path style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" d="M 173.747,51.9912 A 242.139,242.139 0 0 0 153.845,110.428"/>
+    <polygon style="fill: #000000" points="152.457,117.793 149.116,107.123 153.653,110.389 158.988,108.718 "/>
+    <polygon style="fill: none; fill-opacity:0; stroke-width: 2; stroke: #000000" points="152.457,117.793 149.116,107.123 153.653,110.389 158.988,108.718 "/>
+  </g>
+</svg>
diff --git a/annotation-file-utilities/figures/typecast.dot b/annotation-file-utilities/figures/typecast.dot
new file mode 100644
index 0000000..ca63506
--- /dev/null
+++ b/annotation-file-utilities/figures/typecast.dot
@@ -0,0 +1,57 @@
+strict digraph {
+graph [ordering=out]
+
+TypeCast [shape=box,width=1.25]
+AnnotatedType [shape=box]
+Annotation [shape=box]
+Ident [shape=box]
+PrimitiveTypeTree [shape=box]
+e1 [shape=plaintext,label="",width=0.0]
+e2 [shape=plaintext,label="",width=0.0]
+e3 [shape=plaintext,label="",width=0.0]
+b1 [shape=plaintext,label="_",width=0.0]
+b2 [shape=plaintext,label="_",width=0.0]
+b3 [shape=plaintext,label="_",width=0.0]
+b4 [shape=plaintext,label="_",width=0.0]
+b5 [shape=plaintext,label="_",width=0.0]
+b6 [shape=plaintext,label="_",width=0.0]
+d [shape=box,label=LocalVariableDeclaration]
+m [shape=box,label=Modifiers]
+t [shape=box,label=PrimitiveTypeTree]
+l [shape=box,label=Literal]
+i [shape=plaintext,width=0.0]
+i1 [shape=plaintext,label=int,width=0.0]
+i2 [shape=plaintext,label=int,width=0.0]
+p1 [shape=plaintext,label="(",width=0.0]
+p2 [shape=plaintext,label=")",width=0.0]
+a [shape=plaintext,label="A",width=0.0]
+z [shape=plaintext,label="0",width=0.0]
+q [shape=plaintext,label="=",width=0.0]
+y [shape=plaintext,label="[...]",width=0.0]
+
+subgraph {rank=same;e1;e2;i1;b1;i;b2;q;b3;TypeCast}
+subgraph {rank=sink;e3;b4;b5;b6;i2;p1;p2;a;z;y}
+
+d -> {m, t}
+m -> e1
+m -> e2 [style=dashed]
+t -> i1
+t -> b1
+d -> i
+d -> b2 [style=dashed]
+d -> q
+d -> b3 [style=dashed]
+d -> TypeCast -> p1
+TypeCast -> e3 [style=dashed]
+TypeCast -> AnnotatedType
+AnnotatedType -> {Annotation, PrimitiveTypeTree}
+Annotation -> Ident -> a
+Ident -> b4
+PrimitiveTypeTree -> i2
+PrimitiveTypeTree -> b5 [style = dashed]
+l -> z
+l -> y [style=dashed]
+TypeCast -> p2
+TypeCast -> b6 [style=dashed]
+TypeCast -> l
+}
diff --git a/annotation-file-utilities/lib/asm-5.0.jar b/annotation-file-utilities/lib/asm-5.0.jar
new file mode 100644
index 0000000..5678cc0
--- /dev/null
+++ b/annotation-file-utilities/lib/asm-5.0.jar
Binary files differ
diff --git a/annotation-file-utilities/lib/guava-20.0.jar b/annotation-file-utilities/lib/guava-20.0.jar
new file mode 100644
index 0000000..632772f
--- /dev/null
+++ b/annotation-file-utilities/lib/guava-20.0.jar
Binary files differ
diff --git a/annotation-file-utilities/lib/plume-core.jar b/annotation-file-utilities/lib/plume-core.jar
new file mode 100644
index 0000000..5bd7bb5
--- /dev/null
+++ b/annotation-file-utilities/lib/plume-core.jar
Binary files differ
diff --git a/annotation-file-utilities/scripts/extract-annotations b/annotation-file-utilities/scripts/extract-annotations
new file mode 100755
index 0000000..c452d39
--- /dev/null
+++ b/annotation-file-utilities/scripts/extract-annotations
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Extract annotations from a class file and write them to an annotation file.
+# For usage information, run: extract-annotations --help
+# See the Annotation File Utilities documentation for more information.
+
+# If the very first argument is "--debug-script", debug this script (but
+# don't pass --debug-script to the underlying program).
+DEBUG=0
+if [ "$1" = "--debug-script" ]; then
+  DEBUG=1
+  shift 1
+fi
+
+AFU=${AFU:-$(dirname $0)/..}
+ANNOTATION_FILE_UTILS=${AFU}/bin:${AFU}/../scene-lib/bin:${AFU}/../asmx/bin:${AFU}/annotation-file-utilities.jar
+LANGTOOLS=${LANGTOOLS:-${AFU}/../../jsr308-langtools}
+JAVAC_JAR=${JAVAC_JAR:-${LANGTOOLS}/dist/lib/javac.jar}
+
+if [ "$DEBUG" = "1" ]; then
+  echo "--- start of extract-annotations debugging output"
+  echo "AFU=${AFU}"
+  echo "ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS}"
+  echo "LANGTOOLS=${LANGTOOLS}"
+  echo "JAVAC_JAR=${JAVAC_JAR}"
+  # Keep this in sync with the actual command below.
+  echo java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-core.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileReader "$@"
+  echo "--- end of extract-annotations debugging output"
+fi
+
+# Needs CLASSPATH to find user files
+java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-core.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileReader "$@"
diff --git a/annotation-file-utilities/scripts/extract-annotations.bat b/annotation-file-utilities/scripts/extract-annotations.bat
new file mode 100644
index 0000000..ab1f626
--- /dev/null
+++ b/annotation-file-utilities/scripts/extract-annotations.bat
@@ -0,0 +1,14 @@
+
+:: Extract annotations from a class file and write them to an annotation file.
+:: For usage information, run: extract-annotations.bat --help
+:: See the annotation file utilities documentation for more information.
+
+set ANNOTATION_FILE_UTILS=%~d0
+set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%%~p0
+set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%\..\annotation-file-utilities.jar
+set JAVAC_JAR=%~d0
+set JAVAC_JAR=%ANNOTATION_FILE_UTILS%%~p0
+set JAVAC_JAR=%JAVAC_JAR%..\..\..\jsr308-langtools\dist\lib\javac.jar
+
+java -ea -cp "%JAVAC_JAR%;%ANNOTATION_FILE_UTILS%;%CLASSPATH%" annotations.io.classfile.ClassFileReader %*
+
diff --git a/annotation-file-utilities/scripts/insert-annotations b/annotation-file-utilities/scripts/insert-annotations
new file mode 100755
index 0000000..d648dad
--- /dev/null
+++ b/annotation-file-utilities/scripts/insert-annotations
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+# Insert annotations (from an annotation file) into a class file.
+# For usage information, run: insert-annotations --help
+# See the Annotation File Utilities documentation for more information.
+
+# If the very first argument is "--debug-script", debug this script (but
+# don't pass --debug-script to the underlying program).
+DEBUG=0
+if [ "$1" = "--debug-script" ]; then
+  DEBUG=1
+  shift 1
+fi
+
+AFU=${AFU:-$(dirname $0)/..}
+ANNOTATION_FILE_UTILS=${AFU}/bin:${AFU}/../scene-lib/bin:${AFU}/../asmx/bin:${AFU}/annotation-file-utilities.jar
+LANGTOOLS=${LANGTOOLS:-${AFU}/../../jsr308-langtools}
+JAVAC_JAR=${JAVAC_JAR:-${LANGTOOLS}/dist/lib/javac.jar}
+
+if [ "$DEBUG" = "1" ]; then
+  echo "--- start of insert-annotations debugging output"
+  echo "AFU=${AFU}"
+  echo "ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS}"
+  echo "LANGTOOLS=${LANGTOOLS}"
+  echo "JAVAC_JAR=${JAVAC_JAR}"
+  # Keep this in sync with the actual command below.
+  echo "java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-lib.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileWriter $@"
+  echo "--- end of insert-annotations debugging output"
+fi
+
+# Needs CLASSPATH to find user files
+java -ea -cp ${JAVAC_JAR}:${AFU}/lib/plume-lib.jar:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotations.io.classfile.ClassFileWriter "$@"
diff --git a/annotation-file-utilities/scripts/insert-annotations-to-source b/annotation-file-utilities/scripts/insert-annotations-to-source
new file mode 100755
index 0000000..ac1c50a
--- /dev/null
+++ b/annotation-file-utilities/scripts/insert-annotations-to-source
@@ -0,0 +1,33 @@
+#!/bin/sh
+
+# Insert annotations (from an annotation file) into a Java source file.
+# For usage information, run: insert-annotations-to-source --help
+# See the Annotation File Utilities documentation for more information.
+
+# If the very first argument is "--debug-script", debug this script (but
+# don't pass --debug-script to the underlying program).
+DEBUG=0
+if [ "$1" = "--debug-script" ]; then
+  DEBUG=1
+  shift 1
+fi
+
+AFU=${AFU:-$(dirname $0)/..}
+AFU=`cd \`dirname $0\`/.. && pwd`
+ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS:-${AFU}/bin:${AFU}/../scene-lib/bin:${AFU}/../asmx/bin:${AFU}/annotation-file-utilities.jar}
+LANGTOOLS=${LANGTOOLS:-${AFU}/../../jsr308-langtools}
+JAVAC_JAR=${JAVAC_JAR:-${LANGTOOLS}/dist/lib/javac.jar}
+
+if [ "$DEBUG" = "1" ]; then
+  echo "--- start of insert-annotations-to-source debugging output"
+  echo "AFU=${AFU}"
+  echo "ANNOTATION_FILE_UTILS=${ANNOTATION_FILE_UTILS}"
+  echo "LANGTOOLS=${LANGTOOLS}"
+  echo "JAVAC_JAR=${JAVAC_JAR}"
+  # Keep this in sync with the actual command below.
+  echo java -ea -Xmx512m -classpath ${JAVAC_JAR}:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotator.Main "$@"
+  echo "--- end of insert-annotations-to-source debugging output"
+fi
+
+# Augment, don't replace, CLASSPATH, so as to find user files.
+java -ea -Xmx512m -classpath ${JAVAC_JAR}:${ANNOTATION_FILE_UTILS}:${CLASSPATH} annotator.Main "$@"
diff --git a/annotation-file-utilities/scripts/insert-annotations-to-source.bat b/annotation-file-utilities/scripts/insert-annotations-to-source.bat
new file mode 100644
index 0000000..26faa94
--- /dev/null
+++ b/annotation-file-utilities/scripts/insert-annotations-to-source.bat
@@ -0,0 +1,12 @@
+:: Insert annotations (from an annotation file) into a Java source file.
+:: For usage information, run: insert-annotations-to-source.bat --help
+:: See the annotation file utilities documentation for more information.
+
+set ANNOTATION_FILE_UTILS=%~d0
+set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%%~p0
+set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%\..\annotation-file-utilities.jar
+set JAVAC_JAR=%~d0
+set JAVAC_JAR=%ANNOTATION_FILE_UTILS%%~p0
+set JAVAC_JAR=%JAVAC_JAR%..\..\..\jsr308-langtools\dist\lib\javac.jar
+
+java -ea -cp "%JAVAC_JAR%;%ANNOTATION_FILE_UTILS%;%CLASSPATH%" annotator.Main %*
diff --git a/annotation-file-utilities/scripts/insert-annotations.bat b/annotation-file-utilities/scripts/insert-annotations.bat
new file mode 100644
index 0000000..1ae5184
--- /dev/null
+++ b/annotation-file-utilities/scripts/insert-annotations.bat
@@ -0,0 +1,13 @@
+
+:: Insert annotations (from an annoation file) into a class file.
+:: For usage information, run: insert-annotations.bat --help
+:: See the annotation file utilities documentation for more information.
+
+set ANNOTATION_FILE_UTILS=%~d0
+set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%%~p0
+set ANNOTATION_FILE_UTILS=%ANNOTATION_FILE_UTILS%\..\annotation-file-utilities.jar
+set JAVAC_JAR=%~d0
+set JAVAC_JAR=%ANNOTATION_FILE_UTILS%%~p0
+set JAVAC_JAR=%JAVAC_JAR%..\..\..\jsr308-langtools\dist\lib\javac.jar
+
+java -ea -cp "%JAVAC_JAR%;%ANNOTATION_FILE_UTILS%;%CLASSPATH%" annotations.io.classfile.ClassFileWriter %*
diff --git a/annotation-file-utilities/scripts/merge-annotations b/annotation-file-utilities/scripts/merge-annotations
new file mode 100755
index 0000000..f843735
--- /dev/null
+++ b/annotation-file-utilities/scripts/merge-annotations
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Ad hoc script for merging annotated JDKs.  Arguments are JDK source
+# directories, assumed to be children of $js and identified by directory
+# name only (e.g. "merge-annotations nullness").
+
+if [ $# -le 1 ]; then exit 0; fi
+
+js="$HOME/src/jdk1.8.0_45/src"
+jb="$CHECKERFRAMEWORK/checker/dist/*"
+cf="$CHECKERFRAMEWORK/checker/build"
+sl="$JSR308/annotation-tools/scene-lib/bin"
+al="$JSR308/annotation-tools/annotation-file-utilities/lib"
+jj=./jdk.jaif
+wd=`pwd`
+cp="$jb:$cf:$sl:$al/*:$wd:$CLASSPATH"
+if [ 0 -eq 1 ] ; then
+td=/tmp/merge-jaifs.*
+else
+td=/tmp/merge-jaifs.$$
+
+for aj in $* ; do
+    cd $aj/build
+    for f in `find . -name '*\.class' -print | sed 's-^\./--'` ; do
+        CLASSPATH="$wd/$aj/build:$CHECKERFRAMEWORK/checker/build:$CLASSPATH" \
+          extract-annotations "$f"
+        if [ $? -eq 0 ] ; then
+            d=`dirname "$f"`
+            mkdir -p "$td/$aj/$d"
+            mv "$d"/*.jaif "$td/$aj/$d"
+        fi
+    done
+    cd "$wd"
+done
+fi
+
+find $td -type f -print | xargs java -cp "$cp" annotations.tools.IndexFileMerger $td | sed 's/(value={\[/({/g' | sed 's/\]}/}/g' > "./$jj" &&\
+find "$js" -name "*\.java" -print | CLASSPATH="$cp" xargs insert-annotations-to-source --print-error-stack "$jj" &&\
+rm -rf $td
+
diff --git a/annotation-file-utilities/src/annotator/Main.java b/annotation-file-utilities/src/annotator/Main.java
new file mode 100644
index 0000000..ae49953
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/Main.java
@@ -0,0 +1,1012 @@
+package annotator;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import plume.FileIOException;
+import plume.Option;
+import plume.OptionGroup;
+import plume.Options;
+import plume.Pair;
+import plume.UtilMDE;
+import type.Type;
+import annotations.Annotation;
+import annotations.el.ABlock;
+import annotations.el.AClass;
+import annotations.el.ADeclaration;
+import annotations.el.AElement;
+import annotations.el.AExpression;
+import annotations.el.AField;
+import annotations.el.AMethod;
+import annotations.el.AScene;
+import annotations.el.ATypeElement;
+import annotations.el.ATypeElementWithType;
+import annotations.el.AnnotationDef;
+import annotations.el.DefException;
+import annotations.el.ElementVisitor;
+import annotations.el.LocalLocation;
+import annotations.io.ASTIndex;
+import annotations.io.ASTPath;
+import annotations.io.ASTRecord;
+import annotations.io.DebugWriter;
+import annotations.io.IndexFileParser;
+import annotations.io.IndexFileWriter;
+import annotations.util.coll.VivifyingMap;
+import annotator.find.AnnotationInsertion;
+import annotator.find.CastInsertion;
+import annotator.find.ConstructorInsertion;
+import annotator.find.Criteria;
+import annotator.find.GenericArrayLocationCriterion;
+import annotator.find.Insertion;
+import annotator.find.Insertions;
+import annotator.find.NewInsertion;
+import annotator.find.ReceiverInsertion;
+import annotator.find.TreeFinder;
+import annotator.find.TypedInsertion;
+import annotator.scanner.LocalVariableScanner;
+import annotator.specification.IndexFileSpecification;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.SetMultimap;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+import com.sun.tools.javac.main.CommandLine;
+import com.sun.tools.javac.tree.JCTree;
+
+/**
+ * This is the main class for the annotator, which inserts annotations in
+ * Java source code.  You can call it as <tt>java annotator.Main</tt> or by
+ * using the shell script <tt>insert-annotations-to-source</tt>.
+ * <p>
+ *
+ * It takes as input
+ * <ul>
+ *   <li>annotation (index) files, which indicate the annotations to insert</li>
+ *   <li>Java source files, into which the annotator inserts annotations</li>
+ * </ul>
+ * Annotations that are not for the specified Java files are ignored.
+ * <p>
+ *
+ * The <a name="command-line-options">command-line options</a> are as follows:
+ * <!-- start options doc (DO NOT EDIT BY HAND) -->
+ * <ul>
+ *   <li id="optiongroup:General-options">General options
+ *     <ul>
+ *       <li id="option:outdir"><b>-d</b> <b>--outdir=</b><i>directory</i>. Directory in which output files are written. [default annotated/]</li>
+ *       <li id="option:in-place"><b>-i</b> <b>--in-place=</b><i>boolean</i>. If true, overwrite original source files (making a backup first).
+ *  Furthermore, if the backup files already exist, they are used instead
+ *  of the .java files.  This behavior permits a user to tweak the .jaif
+ *  file and re-run the annotator.
+ *  <p>
+ *
+ *  Note that if the user runs the annotator with --in-place, makes edits,
+ *  and then re-runs the annotator with this --in-place option, those
+ *  edits are lost.  Similarly, if the user runs the annotator twice in a
+ *  row with --in-place, only the last set of annotations will appear in
+ *  the codebase at the end.
+ *  <p>
+ *
+ *  To preserve changes when using the --in-place option, first remove the
+ *  backup files.  Or, use the <tt>-d .</tt> option, which makes (and
+ *  reads) no backup, instead of --in-place. [default false]</li>
+ *       <li id="option:abbreviate"><b>-a</b> <b>--abbreviate=</b><i>boolean</i>. Abbreviate annotation names [default true]</li>
+ *       <li id="option:comments"><b>-c</b> <b>--comments=</b><i>boolean</i>. Insert annotations in comments [default false]</li>
+ *       <li id="option:omit-annotation"><b>-o</b> <b>--omit-annotation=</b><i>string</i>. Omit given annotation</li>
+ *       <li id="option:nowarn"><b>--nowarn=</b><i>boolean</i>. Suppress warnings about disallowed insertions [default false]</li>
+ *       <li id="option:convert-jaifs"><b>--convert-jaifs=</b><i>boolean</i>. Convert JAIFs to new format [default false]</li>
+ *       <li id="option:help"><b>-h</b> <b>--help=</b><i>boolean</i>. Print usage information and exit [default false]</li>
+ *     </ul>
+ *   </li>
+ *   <li id="optiongroup:Debugging-options">Debugging options
+ *     <ul>
+ *       <li id="option:verbose"><b>-v</b> <b>--verbose=</b><i>boolean</i>. Verbose (print progress information) [default false]</li>
+ *       <li id="option:debug"><b>--debug=</b><i>boolean</i>. Debug (print debug information) [default false]</li>
+ *       <li id="option:print-error-stack"><b>--print-error-stack=</b><i>boolean</i>. Print error stack [default false]</li>
+ *     </ul>
+ *   </li>
+ * </ul>
+ * <!-- end options doc -->
+ */
+public class Main {
+
+  /** Directory in which output files are written. */
+  @OptionGroup("General options")
+  @Option("-d <directory> Directory in which output files are written")
+  public static String outdir = "annotated/";
+
+  /**
+   * If true, overwrite original source files (making a backup first).
+   * Furthermore, if the backup files already exist, they are used instead
+   * of the .java files.  This behavior permits a user to tweak the .jaif
+   * file and re-run the annotator.
+   * <p>
+   *
+   * Note that if the user runs the annotator with --in-place, makes edits,
+   * and then re-runs the annotator with this --in-place option, those
+   * edits are lost.  Similarly, if the user runs the annotator twice in a
+   * row with --in-place, only the last set of annotations will appear in
+   * the codebase at the end.
+   * <p>
+   *
+   * To preserve changes when using the --in-place option, first remove the
+   * backup files.  Or, use the <tt>-d .</tt> option, which makes (and
+   * reads) no backup, instead of --in-place.
+   */
+  @Option("-i Overwrite original source files")
+  public static boolean in_place = false;
+
+  @Option("-a Abbreviate annotation names")
+  public static boolean abbreviate = true;
+
+  @Option("-c Insert annotations in comments")
+  public static boolean comments = false;
+
+  @Option("-o Omit given annotation")
+  public static String omit_annotation;
+
+  @Option("Suppress warnings about disallowed insertions")
+  public static boolean nowarn;
+
+  // Instead of doing insertions, create new JAIFs using AST paths
+  //  extracted from existing JAIFs and source files they match
+  @Option("Convert JAIFs to AST Path format")
+  public static boolean convert_jaifs = false;
+
+  @Option("-h Print usage information and exit")
+  public static boolean help = false;
+
+  // Debugging options go below here.
+
+  @OptionGroup("Debugging options")
+  @Option("-v Verbose (print progress information)")
+  public static boolean verbose;
+
+  @Option("Debug (print debug information)")
+  public static boolean debug = false;
+
+  @Option("Print error stack")
+  public static boolean print_error_stack = false;
+
+  private static ElementVisitor<Void, AElement> classFilter =
+      new ElementVisitor<Void, AElement>() {
+    <K, V extends AElement>
+    Void filter(VivifyingMap<K, V> vm0, VivifyingMap<K, V> vm1) {
+      for (Map.Entry<K, V> entry : vm0.entrySet()) {
+        entry.getValue().accept(this, vm1.vivify(entry.getKey()));
+      }
+      return null;
+    }
+
+    @Override
+    public Void visitAnnotationDef(AnnotationDef def, AElement el) {
+      // not used, since package declarations not handled here
+      return null;
+    }
+
+    @Override
+    public Void visitBlock(ABlock el0, AElement el) {
+      ABlock el1 = (ABlock) el;
+      filter(el0.locals, el1.locals);
+      return visitExpression(el0, el);
+    }
+
+    @Override
+    public Void visitClass(AClass el0, AElement el) {
+      AClass el1 = (AClass) el;
+      filter(el0.methods, el1.methods);
+      filter(el0.fields, el1.fields);
+      filter(el0.fieldInits, el1.fieldInits);
+      filter(el0.staticInits, el1.staticInits);
+      filter(el0.instanceInits, el1.instanceInits);
+      return visitDeclaration(el0, el);
+    }
+
+    @Override
+    public Void visitDeclaration(ADeclaration el0, AElement el) {
+      ADeclaration el1 = (ADeclaration) el;
+      VivifyingMap<ASTPath, ATypeElement> insertAnnotations =
+          el1.insertAnnotations;
+      VivifyingMap<ASTPath, ATypeElementWithType> insertTypecasts =
+          el1.insertTypecasts;
+      for (Map.Entry<ASTPath, ATypeElement> entry :
+          el0.insertAnnotations.entrySet()) {
+        ASTPath p = entry.getKey();
+        ATypeElement e = entry.getValue();
+        insertAnnotations.put(p, e);
+        // visitTypeElement(e, insertAnnotations.vivify(p));
+      }
+      for (Map.Entry<ASTPath, ATypeElementWithType> entry :
+          el0.insertTypecasts.entrySet()) {
+        ASTPath p = entry.getKey();
+        ATypeElementWithType e = entry.getValue();
+        type.Type type = e.getType();
+        if (type instanceof type.DeclaredType
+            && ((type.DeclaredType) type).getName().isEmpty()) {
+          insertAnnotations.put(p, e);
+          // visitTypeElement(e, insertAnnotations.vivify(p));
+        } else {
+          insertTypecasts.put(p, e);
+          // visitTypeElementWithType(e, insertTypecasts.vivify(p));
+        }
+      }
+      return null;
+    }
+
+    @Override
+    public Void visitExpression(AExpression el0, AElement el) {
+      AExpression el1 = (AExpression) el;
+      filter(el0.typecasts, el1.typecasts);
+      filter(el0.instanceofs, el1.instanceofs);
+      filter(el0.news, el1.news);
+      return null;
+    }
+
+    @Override
+    public Void visitField(AField el0, AElement el) {
+      return visitDeclaration(el0, el);
+    }
+
+    @Override
+    public Void visitMethod(AMethod el0, AElement el) {
+      AMethod el1 = (AMethod) el;
+      filter(el0.bounds, el1.bounds);
+      filter(el0.parameters, el1.parameters);
+      filter(el0.throwsException, el1.throwsException);
+      el0.returnType.accept(this, el1.returnType);
+      el0.receiver.accept(this, el1.receiver);
+      el0.body.accept(this, el1.body);
+      return visitDeclaration(el0, el);
+    }
+
+    @Override
+    public Void visitTypeElement(ATypeElement el0, AElement el) {
+      ATypeElement el1 = (ATypeElement) el;
+      filter(el0.innerTypes, el1.innerTypes);
+      return null;
+    }
+
+    @Override
+    public Void visitTypeElementWithType(ATypeElementWithType el0,
+        AElement el) {
+      ATypeElementWithType el1 = (ATypeElementWithType) el;
+      el1.setType(el0.getType());
+      return visitTypeElement(el0, el);
+    }
+
+    @Override
+    public Void visitElement(AElement el, AElement arg) {
+      return null;
+    }
+  };
+
+  private static AScene filteredScene(final AScene scene) {
+    final AScene filtered = new AScene();
+    filtered.packages.putAll(scene.packages);
+    filtered.imports.putAll(scene.imports);
+    for (Map.Entry<String, AClass> entry : scene.classes.entrySet()) {
+      String key = entry.getKey();
+      AClass clazz0 = entry.getValue();
+      AClass clazz1 = filtered.classes.vivify(key);
+      clazz0.accept(classFilter, clazz1);
+    }
+    filtered.prune();
+    return filtered;
+  }
+
+  private static ATypeElement findInnerTypeElement(Tree t,
+      ASTRecord rec, ADeclaration decl, Type type, Insertion ins) {
+    ASTPath astPath = rec.astPath;
+    GenericArrayLocationCriterion galc =
+        ins.getCriteria().getGenericArrayLocation();
+    assert astPath != null && galc != null;
+    List<TypePathEntry> tpes = galc.getLocation();
+    ASTPath.ASTEntry entry;
+    for (TypePathEntry tpe : tpes) {
+      switch (tpe.tag) {
+      case ARRAY:
+        if (!astPath.isEmpty()) {
+          entry = astPath.get(-1);
+          if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY
+              && entry.childSelectorIs(ASTPath.TYPE)) {
+            entry = new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY,
+                ASTPath.TYPE, entry.getArgument() + 1);
+            break;
+          }
+        }
+        entry = new ASTPath.ASTEntry(Tree.Kind.ARRAY_TYPE,
+            ASTPath.TYPE);
+        break;
+      case INNER_TYPE:
+        entry = new ASTPath.ASTEntry(Tree.Kind.MEMBER_SELECT,
+            ASTPath.EXPRESSION);
+        break;
+      case TYPE_ARGUMENT:
+        entry = new ASTPath.ASTEntry(Tree.Kind.PARAMETERIZED_TYPE,
+            ASTPath.TYPE_ARGUMENT, tpe.arg);
+        break;
+      case WILDCARD:
+        entry = new ASTPath.ASTEntry(Tree.Kind.UNBOUNDED_WILDCARD,
+            ASTPath.BOUND);
+        break;
+      default:
+        throw new IllegalArgumentException("unknown type tag " + tpe.tag);
+      }
+      astPath = astPath.extend(entry);
+    }
+
+    return decl.insertAnnotations.vivify(astPath);
+  }
+
+  private static void convertInsertion(String pkg,
+      JCTree.JCCompilationUnit tree, ASTRecord rec, Insertion ins,
+      AScene scene, Multimap<Insertion, Annotation> insertionSources) {
+    Collection<Annotation> annos = insertionSources.get(ins);
+    if (rec == null) {
+      if (ins.getCriteria().isOnPackage()) {
+        for (Annotation anno : annos) {
+          scene.packages.get(pkg).tlAnnotationsHere.add(anno);
+        }
+      }
+    } else if (scene != null && rec.className != null) {
+      AClass clazz = scene.classes.vivify(rec.className);
+      ADeclaration decl = null;  // insertion target
+      if (ins.getCriteria().onBoundZero()) {
+        int n = rec.astPath.size();
+        if (!rec.astPath.get(n-1).childSelectorIs(ASTPath.BOUND)) {
+          ASTPath astPath = ASTPath.empty();
+          for (int i = 0; i < n; i++) {
+            astPath = astPath.extend(rec.astPath.get(i));
+          }
+          astPath = astPath.extend(
+              new ASTPath.ASTEntry(Tree.Kind.TYPE_PARAMETER,
+                  ASTPath.BOUND, 0));
+          rec = rec.replacePath(astPath);
+        }
+      }
+      if (rec.methodName == null) {
+        decl = rec.varName == null ? clazz
+            : clazz.fields.vivify(rec.varName);
+      } else {
+        AMethod meth = clazz.methods.vivify(rec.methodName);
+        if (rec.varName == null) {
+          decl = meth;  // ?
+        } else {
+          try {
+            int i = Integer.parseInt(rec.varName);
+            decl = i < 0 ? meth.receiver
+                : meth.parameters.vivify(i);
+          } catch (NumberFormatException e) {
+            TreePath path = ASTIndex.getTreePath(tree, rec);
+            JCTree.JCVariableDecl varTree = null;
+            JCTree.JCMethodDecl methTree = null;
+            JCTree.JCClassDecl classTree = null;
+            loop:
+              while (path != null) {
+                Tree leaf = path.getLeaf();
+                switch (leaf.getKind()) {
+                case VARIABLE:
+                  varTree = (JCTree.JCVariableDecl) leaf;
+                  break;
+                case METHOD:
+                  methTree = (JCTree.JCMethodDecl) leaf;
+                  break;
+                case ANNOTATION:
+                case CLASS:
+                case ENUM:
+                case INTERFACE:
+                  break loop;
+                default:
+                  path = path.getParentPath();
+                }
+              }
+            while (path != null) {
+              Tree leaf = path.getLeaf();
+              Tree.Kind kind = leaf.getKind();
+              if (kind == Tree.Kind.METHOD) {
+                methTree = (JCTree.JCMethodDecl) leaf;
+                int i = LocalVariableScanner.indexOfVarTree(path,
+                    varTree, rec.varName);
+                int m = methTree.getStartPosition();
+                int a = varTree.getStartPosition();
+                int b = varTree.getEndPosition(tree.endPositions);
+                LocalLocation loc = new LocalLocation(i, a-m, b-a);
+                decl = meth.body.locals.vivify(loc);
+                break;
+              }
+              if (ASTPath.isClassEquiv(kind)) {
+                classTree = (JCTree.JCClassDecl) leaf;
+                // ???
+                    break;
+              }
+              path = path.getParentPath();
+            }
+          }
+        }
+      }
+      if (decl != null) {
+        AElement el;
+        if (rec.astPath.isEmpty()) {
+          el = decl;
+        } else if (ins.getKind() == Insertion.Kind.CAST) {
+          annotations.el.ATypeElementWithType elem =
+              decl.insertTypecasts.vivify(rec.astPath);
+          elem.setType(((CastInsertion) ins).getType());
+          el = elem;
+        } else {
+          el = decl.insertAnnotations.vivify(rec.astPath);
+        }
+        for (Annotation anno : annos) {
+          el.tlAnnotationsHere.add(anno);
+        }
+        if (ins instanceof TypedInsertion) {
+          TypedInsertion ti = (TypedInsertion) ins;
+          if (!rec.astPath.isEmpty()) {
+            // addInnerTypePaths(decl, rec, ti, insertionSources);
+          }
+          for (Insertion inner : ti.getInnerTypeInsertions()) {
+            Tree t = ASTIndex.getNode(tree, rec);
+            if (t != null) {
+              ATypeElement elem = findInnerTypeElement(t,
+                  rec, decl, ti.getType(), inner);
+              for (Annotation a : insertionSources.get(inner)) {
+                elem.tlAnnotationsHere.add(a);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+
+  // Implementation details:
+  //  1. The annotator partially compiles source
+  //     files using the compiler API (JSR-199), obtaining an AST.
+  //  2. The annotator reads the specification file, producing a set of
+  //     annotator.find.Insertions.  Insertions completely specify what to
+  //     write (as a String, which is ultimately translated according to the
+  //     keyword file) and how to write it (as annotator.find.Criteria).
+  //  3. It then traverses the tree, looking for nodes that satisfy the
+  //     Insertion Criteria, translating the Insertion text against the
+  //     keyword file, and inserting the annotations into the source file.
+
+  /**
+   * Runs the annotator, parsing the source and spec files and applying
+   * the annotations.
+   */
+  public static void main(String[] args) throws IOException {
+
+    if (verbose) {
+      System.out.printf("insert-annotations-to-source (%s)",
+                        annotations.io.classfile.ClassFileReader.INDEX_UTILS_VERSION);
+    }
+
+    Options options = new Options(
+        "Main [options] { jaif-file | java-file | @arg-file } ...\n"
+            + "(Contents of argfiles are expanded into the argument list.)",
+        Main.class);
+    String[] file_args;
+    try {
+      String[] cl_args = CommandLine.parse(args);
+      file_args = options.parse_or_usage(cl_args);
+    } catch (IOException ex) {
+      System.err.println(ex);
+      System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\".");
+      System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'.");
+      System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)");
+      file_args = null;  // Eclipse compiler issue workaround
+      System.exit(1);
+    }
+
+    DebugWriter dbug = new DebugWriter();
+    DebugWriter verb = new DebugWriter();
+    DebugWriter both = dbug.or(verb);
+    dbug.setEnabled(debug);
+    verb.setEnabled(verbose);
+    TreeFinder.warn.setEnabled(!nowarn);
+    TreeFinder.stak.setEnabled(print_error_stack);
+    TreeFinder.dbug.setEnabled(debug);
+    Criteria.dbug.setEnabled(debug);
+
+    if (help) {
+      options.print_usage();
+      System.exit(0);
+    }
+
+    if (in_place && outdir != "annotated/") { // interned
+      options.print_usage("The --outdir and --in-place options are mutually exclusive.");
+      System.exit(1);
+    }
+
+    if (file_args.length < 2) {
+      options.print_usage("Supplied %d arguments, at least 2 needed%n", file_args.length);
+      System.exit(1);
+    }
+
+    // The insertions specified by the annotation files.
+    Insertions insertions = new Insertions();
+    // The Java files into which to insert.
+    List<String> javafiles = new ArrayList<String>();
+
+    // Indices to maintain insertion source traces.
+    Map<String, Multimap<Insertion, Annotation>> insertionIndex =
+        new HashMap<String, Multimap<Insertion, Annotation>>();
+    Map<Insertion, String> insertionOrigins = new HashMap<Insertion, String>();
+    Map<String, AScene> scenes = new HashMap<String, AScene>();
+
+    IndexFileParser.setAbbreviate(abbreviate);
+    for (String arg : file_args) {
+      if (arg.endsWith(".java")) {
+        javafiles.add(arg);
+      } else if (arg.endsWith(".jaif") ||
+                 arg.endsWith(".jann")) {
+        IndexFileSpecification spec = new IndexFileSpecification(arg);
+        try {
+          List<Insertion> parsedSpec = spec.parse();
+          AScene scene = spec.getScene();
+          Collections.sort(parsedSpec, new Comparator<Insertion>() {
+            @Override
+            public int compare(Insertion i1, Insertion i2) {
+              ASTPath p1 = i1.getCriteria().getASTPath();
+              ASTPath p2 = i2.getCriteria().getASTPath();
+              return p1 == null
+                  ? p2 == null ? 0 : -1
+                  : p2 == null ? 1 : p1.compareTo(p2);
+            }
+          });
+          if (convert_jaifs) {
+            scenes.put(arg, filteredScene(scene));
+            for (Insertion ins : parsedSpec) {
+              insertionOrigins.put(ins, arg);
+            }
+            if (!insertionIndex.containsKey(arg)) {
+              insertionIndex.put(arg,
+                  LinkedHashMultimap.<Insertion, Annotation>create());
+            }
+            insertionIndex.get(arg).putAll(spec.insertionSources());
+          }
+          both.debug("Read %d annotations from %s%n", parsedSpec.size(), arg);
+          if (omit_annotation != null) {
+            List<Insertion> filtered =
+                new ArrayList<Insertion>(parsedSpec.size());
+            for (Insertion insertion : parsedSpec) {
+              // TODO: this won't omit annotations if the insertion is more than
+              // just the annotation (such as if the insertion is a cast
+              // insertion or a 'this' parameter in a method declaration).
+              if (! omit_annotation.equals(insertion.getText())) {
+                filtered.add(insertion);
+              }
+            }
+            parsedSpec = filtered;
+            both.debug("After filtering: %d annotations from %s%n",
+                parsedSpec.size(), arg);
+          }
+          insertions.addAll(parsedSpec);
+        } catch (RuntimeException e) {
+          if (e.getCause() != null
+              && e.getCause() instanceof FileNotFoundException) {
+            System.err.println("File not found: " + arg);
+            System.exit(1);
+          } else {
+            throw e;
+          }
+        } catch (FileIOException e) {
+          // Add 1 to the line number since line numbers in text editors are usually one-based.
+          System.err.println("Error while parsing annotation file " + arg + " at line "
+              + (e.lineNumber + 1) + ":");
+          if (e.getMessage() != null) {
+            System.err.println('\t' + e.getMessage());
+          }
+          if (e.getCause() != null && e.getCause().getMessage() != null) {
+            System.err.println('\t' + e.getCause().getMessage());
+          }
+          if (print_error_stack) {
+            e.printStackTrace();
+          }
+          System.exit(1);
+        }
+      } else {
+        throw new Error("Unrecognized file extension: " + arg);
+      }
+    }
+
+    if (dbug.isEnabled()) {
+      dbug.debug("%d insertions, %d .java files%n",
+          insertions.size(), javafiles.size());
+      dbug.debug("Insertions:%n");
+      for (Insertion insertion : insertions) {
+        dbug.debug("  %s%n", insertion);
+      }
+    }
+
+    for (String javafilename : javafiles) {
+      verb.debug("Processing %s%n", javafilename);
+
+      File javafile = new File(javafilename);
+      File unannotated = new File(javafilename + ".unannotated");
+      if (in_place) {
+        // It doesn't make sense to check timestamps;
+        // if the .java.unannotated file exists, then just use it.
+        // A user can rename that file back to just .java to cause the
+        // .java file to be read.
+        if (unannotated.exists()) {
+          verb.debug("Renaming %s to %s%n", unannotated, javafile);
+          boolean success = unannotated.renameTo(javafile);
+          if (! success) {
+            throw new Error(String.format("Failed renaming %s to %s",
+                                          unannotated, javafile));
+          }
+        }
+      }
+
+      String fileSep = System.getProperty("file.separator");
+      String fileLineSep = System.getProperty("line.separator");
+      Source src;
+      // Get the source file, and use it to obtain parse trees.
+      try {
+        // fileLineSep is set here so that exceptions can be caught
+        fileLineSep = UtilMDE.inferLineSeparator(javafilename);
+        src = new Source(javafilename);
+        verb.debug("Parsed %s%n", javafilename);
+      } catch (Source.CompilerException e) {
+        e.printStackTrace();
+        return;
+      } catch (IOException e) {
+        e.printStackTrace();
+        return;
+      }
+
+      // Imports required to resolve annotations (when abbreviate==true).
+      LinkedHashSet<String> imports = new LinkedHashSet<String>();
+      int num_insertions = 0;
+      String pkg = "";
+
+      for (CompilationUnitTree cut : src.parse()) {
+        JCTree.JCCompilationUnit tree = (JCTree.JCCompilationUnit) cut;
+        ExpressionTree pkgExp = cut.getPackageName();
+        pkg = pkgExp == null ? "" : pkgExp.toString();
+
+        // Create a finder, and use it to get positions.
+        TreeFinder finder = new TreeFinder(tree);
+        SetMultimap<Pair<Integer, ASTPath>, Insertion> positions =
+            finder.getPositions(tree, insertions);
+
+        if (convert_jaifs) {
+          // program used only for JAIF conversion; execute following
+          // block and then skip remainder of loop
+          Multimap<ASTRecord, Insertion> astInsertions =
+              finder.getPaths();
+          for (Map.Entry<ASTRecord, Collection<Insertion>> entry :
+              astInsertions.asMap().entrySet()) {
+            ASTRecord rec = entry.getKey();
+            for (Insertion ins : entry.getValue()) {
+              if (ins.getCriteria().getASTPath() != null) { continue; }
+              String arg = insertionOrigins.get(ins);
+              AScene scene = scenes.get(arg);
+              Multimap<Insertion, Annotation> insertionSources =
+                  insertionIndex.get(arg);
+              // String text =
+              //  ins.getText(comments, abbreviate, false, 0, '\0');
+
+              // TODO: adjust for missing end of path (?)
+
+              if (insertionSources.containsKey(ins)) {
+                convertInsertion(pkg, tree, rec, ins, scene, insertionSources);
+              }
+            }
+          }
+          continue;
+        }
+
+        // Apply the positions to the source file.
+        if (both.isEnabled()) {
+          System.err.printf(
+              "getPositions returned %d positions in tree for %s%n",
+              positions.size(), javafilename);
+        }
+
+        Set<Pair<Integer, ASTPath>> positionKeysUnsorted =
+            positions.keySet();
+        Set<Pair<Integer, ASTPath>> positionKeysSorted =
+          new TreeSet<Pair<Integer, ASTPath>>(
+              new Comparator<Pair<Integer, ASTPath>>() {
+                @Override
+                public int compare(Pair<Integer, ASTPath> p1,
+                    Pair<Integer, ASTPath> p2) {
+                  int c = Integer.compare(p2.a, p1.a);
+                  if (c == 0) {
+                    c = p2.b == null ? p1.b == null ? 0 : -1
+                        : p1.b == null ? 1 : p2.b.compareTo(p1.b);
+                  }
+                  return c;
+                }
+              });
+        positionKeysSorted.addAll(positionKeysUnsorted);
+        for (Pair<Integer, ASTPath> pair : positionKeysSorted) {
+          boolean receiverInserted = false;
+          boolean newInserted = false;
+          boolean constructorInserted = false;
+          Set<String> seen = new TreeSet<String>();
+          List<Insertion> toInsertList = new ArrayList<Insertion>(positions.get(pair));
+          Collections.reverse(toInsertList);
+          dbug.debug("insertion pos: %d%n", pair.a);
+          assert pair.a >= 0
+            : "pos is negative: " + pair.a + " " + toInsertList.get(0) + " " + javafilename;
+          for (Insertion iToInsert : toInsertList) {
+            // Possibly add whitespace after the insertion
+            String trailingWhitespace = "";
+            boolean gotSeparateLine = false;
+            int pos = pair.a;  // reset each iteration in case of dyn adjustment
+            if (iToInsert.getSeparateLine()) {
+              // System.out.printf("getSeparateLine=true for insertion at pos %d: %s%n", pos, iToInsert);
+              int indentation = 0;
+              while ((pos - indentation != 0)
+                     // horizontal whitespace
+                     && (src.charAt(pos-indentation-1) == ' '
+                         || src.charAt(pos-indentation-1) == '\t')) {
+                // System.out.printf("src.charAt(pos-indentation-1 == %d-%d-1)='%s'%n",
+                //                   pos, indentation, src.charAt(pos-indentation-1));
+                indentation++;
+              }
+              if ((pos - indentation == 0)
+                  // horizontal whitespace
+                  || (src.charAt(pos-indentation-1) == '\f'
+                      || src.charAt(pos-indentation-1) == '\n'
+                      || src.charAt(pos-indentation-1) == '\r')) {
+                trailingWhitespace = fileLineSep + src.substring(pos-indentation, pos);
+                gotSeparateLine = true;
+              }
+            }
+
+            char precedingChar;
+            if (pos != 0) {
+              precedingChar = src.charAt(pos - 1);
+            } else {
+              precedingChar = '\0';
+            }
+
+            if (iToInsert.getKind() == Insertion.Kind.ANNOTATION) {
+              AnnotationInsertion ai = (AnnotationInsertion) iToInsert;
+              if (ai.isGenerateBound()) {  // avoid multiple ampersands
+                try {
+                  String s = src.substring(pos, pos+9);
+                  if ("Object & ".equals(s)) {
+                    ai.setGenerateBound(false);
+                    precedingChar = '.';  // suppress leading space
+                  }
+                } catch (StringIndexOutOfBoundsException e) {}
+              }
+              if (ai.isGenerateExtends()) {  // avoid multiple "extends"
+                try {
+                  String s = src.substring(pos, pos+9);
+                  if (" extends ".equals(s)) {
+                    ai.setGenerateExtends(false);
+                    pos += 8;
+                  }
+                } catch (StringIndexOutOfBoundsException e) {}
+              }
+            } else if (iToInsert.getKind() == Insertion.Kind.CAST) {
+                ((CastInsertion) iToInsert)
+                        .setOnArrayLiteral(src.charAt(pos) == '{');
+            } else if (iToInsert.getKind() == Insertion.Kind.RECEIVER) {
+              ReceiverInsertion ri = (ReceiverInsertion) iToInsert;
+              ri.setAnnotationsOnly(receiverInserted);
+              receiverInserted = true;
+            } else if (iToInsert.getKind() == Insertion.Kind.NEW) {
+              NewInsertion ni = (NewInsertion) iToInsert;
+              ni.setAnnotationsOnly(newInserted);
+              newInserted = true;
+            } else if (iToInsert.getKind() == Insertion.Kind.CONSTRUCTOR) {
+              ConstructorInsertion ci = (ConstructorInsertion) iToInsert;
+              if (constructorInserted) { ci.setAnnotationsOnly(true); }
+              constructorInserted = true;
+            }
+
+            String toInsert = iToInsert.getText(comments, abbreviate,
+                gotSeparateLine, pos, precedingChar) + trailingWhitespace;
+            if (seen.contains(toInsert)) { continue; }  // eliminate duplicates
+            seen.add(toInsert);
+
+            // If it's already there, don't re-insert.  This is a hack!
+            // Also, I think this is already checked when constructing the
+            // insertions.
+            int precedingTextPos = pos-toInsert.length()-1;
+            if (precedingTextPos >= 0) {
+              String precedingTextPlusChar
+                = src.getString().substring(precedingTextPos, pos);
+              if (toInsert.equals(
+                      precedingTextPlusChar.substring(0, toInsert.length()))
+                  || toInsert.equals(precedingTextPlusChar.substring(1))) {
+                dbug.debug(
+                    "Inserting %s at %d in code of length %d with preceding text '%s'%n",
+                    toInsert, pos, src.getString().length(),
+                    precedingTextPlusChar);
+                dbug.debug("Already present, skipping%n");
+                continue;
+              }
+            }
+
+            // TODO: Neither the above hack nor this check should be
+            // necessary.  Find out why re-insertions still occur and
+            // fix properly.
+            if (iToInsert.getInserted()) { continue; }
+            src.insert(pos, toInsert);
+            if (verbose && !debug) {
+              System.out.print(".");
+              num_insertions++;
+              if ((num_insertions % 50) == 0) {
+                System.out.println();   // terminate the line that contains dots
+              }
+            }
+            dbug.debug("Post-insertion source: %n" + src.getString());
+
+            Set<String> packageNames = iToInsert.getPackageNames();
+            if (!packageNames.isEmpty()) {
+              dbug.debug("Need import %s%n  due to insertion %s%n",
+                  packageNames, toInsert);
+              imports.addAll(packageNames);
+            }
+          }
+        }
+      }
+
+      if (convert_jaifs) {
+        for (Map.Entry<String, AScene> entry : scenes.entrySet()) {
+          String filename = entry.getKey();
+          AScene scene = entry.getValue();
+          try {
+            IndexFileWriter.write(scene, filename + ".converted");
+          } catch (DefException e) {
+            System.err.println(filename + ": " + " format error in conversion");
+            if (print_error_stack) {
+              e.printStackTrace();
+            }
+          }
+        }
+        return;  // done with conversion
+      }
+
+      if (dbug.isEnabled()) {
+        dbug.debug("%d imports to insert%n", imports.size());
+        for (String classname : imports) {
+          dbug.debug("  %s%n", classname);
+        }
+      }
+
+      // insert import statements
+      {
+        Pattern importPattern = Pattern.compile("(?m)^import\\b");
+        Pattern packagePattern = Pattern.compile("(?m)^package\\b.*;(\\n|\\r\\n?)");
+        int importIndex = 0;      // default: beginning of file
+        String srcString = src.getString();
+        Matcher m = importPattern.matcher(srcString);
+        Set<String> inSource = new TreeSet<String>();
+        if (m.find()) {
+          importIndex = m.start();
+          do {
+            int i = m.start();
+            int j = srcString.indexOf(System.lineSeparator(), i) + 1;
+            if (j <= 0) {
+              j = srcString.length();
+            }
+            String s = srcString.substring(i, j);
+            inSource.add(s);
+          } while (m.find());
+        } else {
+          // Debug.info("Didn't find import in " + srcString);
+          m = packagePattern.matcher(srcString);
+          if (m.find()) {
+            importIndex = m.end();
+          }
+        }
+        for (String classname : imports) {
+          String toInsert = "import " + classname + ";" + fileLineSep;
+          if (!inSource.contains(toInsert)) {
+            inSource.add(toInsert);
+            src.insert(importIndex, toInsert);
+            importIndex += toInsert.length();
+          }
+        }
+      }
+
+      // Write the source file.
+      File outfile = null;
+      try {
+        if (in_place) {
+          outfile = javafile;
+          if (verbose) {
+            System.out.printf("Renaming %s to %s%n", javafile, unannotated);
+          }
+          boolean success = javafile.renameTo(unannotated);
+          if (! success) {
+            throw new Error(String.format("Failed renaming %s to %s",
+                                          javafile, unannotated));
+          }
+        } else {
+          if (pkg.isEmpty()) {
+            outfile = new File(outdir, javafile.getName());
+          } else {
+            String[] pkgPath = pkg.split("\\.");
+            StringBuilder sb = new StringBuilder(outdir);
+            for (int i = 0 ; i < pkgPath.length ; i++) {
+              sb.append(fileSep).append(pkgPath[i]);
+            }
+            outfile = new File(sb.toString(), javafile.getName());
+          }
+          outfile.getParentFile().mkdirs();
+        }
+        OutputStream output = new FileOutputStream(outfile);
+        if (verbose) {
+          System.out.printf("Writing %s%n", outfile);
+        }
+        src.write(output);
+        output.close();
+      } catch (IOException e) {
+        System.err.println("Problem while writing file " + outfile);
+        e.printStackTrace();
+        System.exit(1);
+      }
+    }
+  }
+
+  public static String pathToString(TreePath path) {
+    if (path == null) {
+      return "null";
+    }
+    return treeToString(path.getLeaf());
+  }
+
+  public static String treeToString(Tree node) {
+    String asString = node.toString();
+    String oneLine = firstLine(asString);
+    return "\"" + oneLine + "\"";
+  }
+
+  /**
+   * Return the first non-empty line of the string, adding an ellipsis
+   * (...) if the string was truncated.
+   */
+  public static String firstLine(String s) {
+    while (s.startsWith("\n")) {
+      s = s.substring(1);
+    }
+    int newlineIndex = s.indexOf('\n');
+    if (newlineIndex == -1) {
+      return s;
+    } else {
+      return s.substring(0, newlineIndex) + "...";
+    }
+  }
+
+  /**
+   * Separates the annotation class from its arguments.
+   *
+   * @return given <code>@foo(bar)</code> it returns the pair <code>{ @foo, (bar) }</code>.
+   */
+  public static Pair<String,String> removeArgs(String s) {
+    int pidx = s.indexOf("(");
+    return (pidx == -1) ?
+        Pair.of(s, (String)null) :
+        Pair.of(s.substring(0, pidx), s.substring(pidx));
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/Source.java b/annotation-file-utilities/src/annotator/Source.java
new file mode 100644
index 0000000..b3cae71
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/Source.java
@@ -0,0 +1,194 @@
+package annotator;
+
+import java.io.*;
+import java.util.*;
+
+import javax.tools.*;
+import javax.tools.JavaCompiler.CompilationTask;
+
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.util.JavacTask;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.code.Types;
+
+/**
+ * Represents a Java source file. This class provides three major operations:
+ * parsing the source file to obtain a syntax tree (via JSR-199), inserting text
+ * into the source file at specified offsets, and writing the rewritten source
+ * file.
+ */
+public final class Source {
+
+    private JavaCompiler compiler;
+    private StandardJavaFileManager fileManager;
+    private JavacTask task;
+    private StringBuilder source;
+    private DiagnosticCollector<JavaFileObject> diagnostics;
+    private String path;
+    private Types types;
+
+    /**
+     * Signifies that a problem has occurred with the compiler that produces
+     * the syntax tree for this source file.
+     */
+    public static class CompilerException extends Exception {
+
+        private static final long serialVersionUID = -4751611137146719789L;
+
+        public CompilerException(String message) {
+            super(message);
+        }
+    }
+
+    /**
+     * Sets up a compiler for parsing the given Java source file.
+     *
+     * @throws CompilerException if the input file couldn't be read
+     */
+    public Source(String src) throws CompilerException, IOException {
+
+        // Get the JSR-199 compiler.
+        this.compiler = javax.tools.ToolProvider.getSystemJavaCompiler();
+        if (compiler == null) {
+            throw new CompilerException("could not get compiler instance");
+        }
+
+        diagnostics = new DiagnosticCollector<JavaFileObject>();
+
+        // Get the file manager for locating input files.
+        this.fileManager = compiler.getStandardFileManager(diagnostics, null, null);
+        if (fileManager == null) {
+            throw new CompilerException("could not get file manager");
+        }
+
+        Iterable<? extends JavaFileObject> fileObjs = fileManager
+            .getJavaFileObjectsFromStrings(Collections.singletonList(src));
+
+        // Compiler options.
+        // -Xlint:-options is a hack to get around Jenkins build problem:
+        // "target value 1.8 is obsolete and will be removed in a future release"
+        final String[] stringOpts = new String[] { "-g", "-Xlint:-options" };
+            // "-XDTA:noannotationsincomments"
+          // TODO: figure out if these options are necessary? "-source", "1.6x"
+        List<String> optsList = Arrays.asList(stringOpts);
+
+        // Create a task.
+        // This seems to require that the file names end in .java
+        CompilationTask cTask =
+            compiler.getTask(null, fileManager, diagnostics, optsList, null, fileObjs);
+        if (!(cTask instanceof JavacTask)) {
+            throw new CompilerException("could not get a valid JavacTask: " + cTask.getClass());
+        }
+        this.task = (JavacTask)cTask;
+        this.types = Types.instance(((JavacTaskImpl)cTask).getContext());
+
+        // Read the source file into a buffer.
+        path = src;
+        source = new StringBuilder();
+        FileInputStream in = new FileInputStream(src);
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        int c;
+        while ((c = in.read()) != -1) {
+            bytes.write(c);
+        }
+        in.close();
+        source.append(bytes.toString());
+        bytes.close();
+        fileManager.close();
+    }
+
+    /**
+     * @return an object that provides utility methods for types
+     */
+    public Types getTypes() { return types; }
+
+    /**
+     * Parse the input file, returning a set of Tree API roots (as
+     * <code>CompilationUnitTree</code>s).
+     *
+     * @return the Tree API roots for the input file
+     */
+    public Set<CompilationUnitTree> parse() {
+
+        try {
+            Set<CompilationUnitTree> compUnits = new HashSet<CompilationUnitTree>();
+
+            for (CompilationUnitTree tree : task.parse()) {
+                compUnits.add(tree);
+            }
+
+            List<Diagnostic<? extends JavaFileObject>> errors = diagnostics.getDiagnostics();
+            if (!diagnostics.getDiagnostics().isEmpty()) {
+                int numErrors = 0;
+                for (Diagnostic<? extends JavaFileObject> d : errors) {
+                    System.err.println(d);
+                    if (d.getKind() == Diagnostic.Kind.ERROR) { ++numErrors; }
+                }
+                if (numErrors > 0) {
+                    System.err.println(numErrors + " error" + (numErrors != 1 ? "s" : ""));
+                    System.err.println("WARNING: Error processing input source files. Please fix and try again.");
+                    System.exit(1);
+                }
+            }
+
+            // Add type information to the AST.
+            try {
+              task.analyze();
+            } catch (Throwable e) {
+              System.err.println("WARNING: " + path
+                  + ": type analysis failed; skipping");
+              System.err.println("(incomplete CLASSPATH?)");
+              return Collections.<CompilationUnitTree>emptySet();
+            }
+
+            return compUnits;
+
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new Error(e);
+        }
+
+        // return Collections.<CompilationUnitTree>emptySet();
+    }
+
+    // TODO: Can be a problem if offsets get thrown off by previous insertions?
+    /**
+     * Inserts the given string into the source file at the given offset.
+     * <p>
+     *
+     * Note that calling this can throw off indices in later parts of the
+     * file.  Therefore, when doing multiple insertions, you should perform
+     * them from the end of the file forward.
+     *
+     * @param offset the offset to place the start of the insertion text
+     * @param str the text to insert
+     */
+    public void insert(int offset, String str) {
+        source.insert(offset, str);
+    }
+
+    public char charAt(int index) {
+        return source.charAt(index);
+    }
+
+    public String substring(int start, int end) {
+        return source.substring(start, end);
+    }
+
+    public String getString() {
+        return source.toString();
+    }
+
+    /**
+     * Writes the modified source file to the given stream.
+     *
+     * @param out the stream for writing the file
+     * @throws IOException if the source file couldn't be written
+     */
+    public void write(OutputStream out) throws IOException {
+        out.write(source.toString().getBytes());
+        out.flush();
+        out.close();
+    }
+
+}
diff --git a/annotation-file-utilities/src/annotator/find/ASTPathCriterion.java b/annotation-file-utilities/src/annotator/find/ASTPathCriterion.java
new file mode 100644
index 0000000..d30844f
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ASTPathCriterion.java
@@ -0,0 +1,1149 @@
+package annotator.find;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.lang.model.element.Modifier;
+import javax.lang.model.type.TypeKind;
+
+import annotations.io.ASTPath;
+import annotator.Main;
+
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.AssertTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.CatchTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompoundAssignmentTree;
+import com.sun.source.tree.ConditionalExpressionTree;
+import com.sun.source.tree.DoWhileLoopTree;
+import com.sun.source.tree.EnhancedForLoopTree;
+import com.sun.source.tree.ExpressionStatementTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.ForLoopTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.IfTree;
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.LabeledStatementTree;
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MemberReferenceTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.ParenthesizedTree;
+import com.sun.source.tree.ReturnTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.SwitchTree;
+import com.sun.source.tree.SynchronizedTree;
+import com.sun.source.tree.ThrowTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TryTree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.tree.UnaryTree;
+import com.sun.source.tree.UnionTypeTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.WhileLoopTree;
+import com.sun.source.tree.WildcardTree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.util.SimpleTreeVisitor;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree;
+
+/**
+ * A criterion to determine if a node matches a path through the AST.
+ */
+public class ASTPathCriterion implements Criterion {
+
+    public static boolean debug = Main.debug;
+
+    /**
+     * The path through the AST to match.
+     */
+    ASTPath astPath;
+
+    /**
+     * Constructs a new ASTPathCriterion to match the given AST path.
+     * <p>
+     * This assumes that the astPath is valid. Specifically, that all of its
+     * arguments have been previously validated.
+     *
+     * @param astPath
+     *            the AST path to match
+     */
+    public ASTPathCriterion(ASTPath astPath) {
+        this.astPath = astPath;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+        assert path == null || path.getLeaf() == leaf;
+        return isSatisfiedBy(path);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public boolean isSatisfiedBy(TreePath path) {
+        if (path == null) {
+            return false;
+        }
+
+        // actualPath stores the path through the source code AST to this
+        // location (specified by the "path" parameter to this method). It is
+        // computed by traversing from this location up the source code AST
+        // until it reaches a method node (this gets only the part of the path
+        // within a method) or class node (this gets only the part of the path
+        // within a field).
+        List<Tree> actualPath = new ArrayList<Tree>();
+        Tree leaf = path.getLeaf();
+        Tree.Kind kind = leaf.getKind();
+        while (kind != Tree.Kind.METHOD && !ASTPath.isClassEquiv(kind)) {
+            actualPath.add(0, leaf);
+            path = path.getParentPath();
+            if (path == null) { break; }
+            leaf = path.getLeaf();
+            kind = leaf.getKind();
+        }
+
+        // If astPath starts with Method.* or Class.*, include the
+        // MethodTree or ClassTree on actualPath.
+        if (path != null && !astPath.isEmpty()) {
+            Tree.Kind entryKind = astPath.get(0).getTreeKind();
+            if (entryKind == Tree.Kind.METHOD
+                            && kind == Tree.Kind.METHOD
+                    || entryKind == Tree.Kind.CLASS
+                            && ASTPath.isClassEquiv(kind)) {
+                actualPath.add(0, leaf);
+            }
+        }
+
+        if (debug) {
+            System.out.println("ASTPathCriterion.isSatisfiedBy");
+            System.out.println("  " + astPath);
+            for (Tree t : actualPath) {
+                System.out.println("  " + t.getKind() + ": "
+                        + t.toString().replace('\n', ' '));
+            }
+        }
+
+        int astPathLen = astPath.size();
+        int actualPathLen = actualPath.size();
+        if (astPathLen == 0 || actualPathLen == 0) { return false; }
+        // if (actualPathLen != astPathLen + (isOnNewArrayType ? 0 : 1)) {
+        //    return false;
+        // }
+
+        Tree next = null;
+        int i = 0;
+        while (true) {
+            ASTPath.ASTEntry astNode = astPath.get(i);
+            Tree actualNode = actualPath.get(i);
+            if (!kindsMatch(astNode.getTreeKind(), actualNode.getKind())) {
+                return isBoundableWildcard(actualPath, i);
+            }
+
+            if (debug) {
+                System.out.println("astNode: " + astNode);
+                System.out.println("actualNode: " + actualNode.getKind());
+            }
+
+            // Based on the child selector and (optional) argument in "astNode",
+            // "next" will get set to the next source node below "actualNode".
+            // Then "next" will be compared with the node following "astNode"
+            // in "actualPath". If it's not a match, this is not the correct
+            // location. If it is a match, keep going.
+            next = getNext(actualNode, astPath, i);
+            if (next == null) {
+                return checkNull(actualPath, i);
+            }
+            if (!(next instanceof JCTree)) {
+                // converted from array type, not in source AST...
+                if (actualPathLen == i+1) {
+                // need to extend actualPath with "artificial" node
+                  actualPath.add(next);
+                  ++actualPathLen;
+                }
+            }
+            if (debug) {
+                System.out.println("next: " + next);
+            }
+
+            // if (++i >= astPathLen || i >= actualPathLen) { break; }
+            if (++i >= astPathLen) { break; }
+            if (i >= actualPathLen) {
+              return checkNull(actualPath, i-1);
+            }
+            if (!matchNext(next, actualPath.get(i))) {
+                if (debug) {
+                    System.out.println("no next match");
+                }
+              return false;
+            }
+        }
+
+        if (i < actualPathLen && matchNext(next, actualPath.get(i))
+                || i <= actualPathLen
+                        && next.getKind() == Tree.Kind.NEW_ARRAY) {
+            return true;
+        }
+
+        if (debug) {
+            System.out.println("no next match");
+        }
+        return false;
+    }
+
+    private boolean matchNext(Tree next, Tree node) {
+        boolean b1 = next instanceof JCTree;
+        boolean b2 = node instanceof JCTree;
+        if (b1 && !b2) {
+          next = Insertions.TypeTree.fromJCTree((JCTree) next);
+        } else if (b2 && !b1) {
+          node = Insertions.TypeTree.fromJCTree((JCTree) node);
+        }
+
+        try {
+            return next.accept(new SimpleTreeVisitor<Boolean, Tree>() {
+                @Override
+                public Boolean
+                defaultAction(Tree t1, Tree t2) {
+                    return t1 == t2;
+                }
+
+                @Override
+                public Boolean
+                visitIdentifier(IdentifierTree v, Tree t) {
+                    return v == t;
+                    // IdentifierTree i2 = (IdentifierTree) t;
+                    // return i1.getName().toString()
+                    //        .equals(i2.getName().toString());
+                }
+
+                @Override
+                public Boolean
+                visitAnnotatedType(AnnotatedTypeTree a1, Tree t) {
+                    AnnotatedTypeTree a2 = (AnnotatedTypeTree) t;
+                    return matchNext(a1.getUnderlyingType(),
+                            a2.getUnderlyingType());
+                }
+
+                // @Override
+                // public Boolean
+                // visitArrayType(ArrayTypeTree b1, Tree t) {
+                //    ArrayTypeTree b2 = (ArrayTypeTree) t;
+                //    return matchNext(b1.getType(), b2.getType());
+                // }
+
+                @Override
+                public Boolean
+                visitMemberSelect(MemberSelectTree c1, Tree t) {
+                    MemberSelectTree c2 = (MemberSelectTree) t;
+                    return c1.getIdentifier().toString()
+                                .equals(c2.getIdentifier().toString())
+                            && matchNext(c1.getExpression(),
+                                    c2.getExpression());
+                }
+
+                @Override
+                public Boolean
+                visitWildcard(WildcardTree d1, Tree t) {
+                    return d1 == (WildcardTree) t;
+                    // WildcardTree d2 = (WildcardTree) t;
+                    // Tree bound2 = d2.getBound();
+                    // Tree bound1 = d1.getBound();
+                    // return bound1 == bound2 || matchNext(bound1, bound2);
+                }
+
+                @Override
+                public Boolean
+                visitParameterizedType(ParameterizedTypeTree e1, Tree t) {
+                    ParameterizedTypeTree e2 = (ParameterizedTypeTree) t;
+                    List<? extends Tree> l2 = e2.getTypeArguments();
+                    List<? extends Tree> l1 = e1.getTypeArguments();
+                    if (l1.size() == l2.size()) {
+                        int i = 0;
+                        for (Tree t1 : l1) {
+                            Tree t2 = l2.get(i++);
+                            if (!matchNext(t1, t2)) { return false; }
+                        }
+                        return matchNext(e1.getType(), e2.getType());
+                    }
+                    return false;
+                }
+            }, node);
+        } catch (RuntimeException ex) {
+            return false;
+        }
+    }
+
+    private Tree getNext(Tree actualNode, ASTPath astPath, int ix) {
+        try {
+            ASTPath.ASTEntry astNode = astPath.get(ix);
+            switch (actualNode.getKind()) {
+            case ANNOTATED_TYPE: {
+                AnnotatedTypeTree annotatedType =
+                    (AnnotatedTypeTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.ANNOTATION)) {
+                    int arg = astNode.getArgument();
+                    List<? extends AnnotationTree> annos =
+                        annotatedType.getAnnotations();
+                    if (arg >= annos.size()) {
+                        return null;
+                    }
+                    return annos.get(arg);
+                } else {
+                    return annotatedType.getUnderlyingType();
+                }
+            }
+            case ARRAY_ACCESS: {
+                ArrayAccessTree arrayAccess = (ArrayAccessTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+                    return arrayAccess.getExpression();
+                } else {
+                    return arrayAccess.getIndex();
+                }
+            }
+            case ARRAY_TYPE: {
+                ArrayTypeTree arrayType = (ArrayTypeTree) actualNode;
+                return arrayType.getType();
+            }
+            case ASSERT: {
+                AssertTree azzert = (AssertTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+                    return azzert.getCondition();
+                } else {
+                    return azzert.getDetail();
+                }
+            }
+            case ASSIGNMENT: {
+                AssignmentTree assignment = (AssignmentTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
+                    return assignment.getVariable();
+                } else {
+                    return assignment.getExpression();
+                }
+            }
+            case BLOCK: {
+                BlockTree block = (BlockTree) actualNode;
+                int arg = astNode.getArgument();
+                List<? extends StatementTree> statements = block.getStatements();
+                if (arg >= block.getStatements().size()) {
+                    return null;
+                }
+                return statements.get(arg);
+            }
+            case CASE: {
+                CaseTree caze = (CaseTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+                    return caze.getExpression();
+                } else {
+                    int arg = astNode.getArgument();
+                    List<? extends StatementTree> statements = caze.getStatements();
+                    if (arg >= statements.size()) {
+                        return null;
+                    }
+                    return statements.get(arg);
+                }
+            }
+            case CATCH: {
+                CatchTree cach = (CatchTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
+                    return cach.getParameter();
+                } else {
+                    return cach.getBlock();
+                }
+            }
+            case ANNOTATION:
+            case CLASS:
+            case ENUM:
+            case INTERFACE: {
+                ClassTree clazz = (ClassTree) actualNode;
+                int arg = astNode.getArgument();
+                if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) {
+                    return clazz.getTypeParameters().get(arg);
+                } else if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
+                    int i = 0;
+                    for (Tree member : clazz.getMembers()) {
+                        if (member.getKind() == Tree.Kind.BLOCK && arg == i++) {
+                            return member;
+                        }
+                    }
+                    return null;
+                } else if (astNode.childSelectorIs(ASTPath.BOUND)) {
+                  return arg < 0 ? clazz.getExtendsClause()
+                          : clazz.getImplementsClause().get(arg);
+                } else {
+                    return null;
+                }
+            }
+            case CONDITIONAL_EXPRESSION: {
+                ConditionalExpressionTree conditionalExpression = (ConditionalExpressionTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+                    return conditionalExpression.getCondition();
+                } else if (astNode.childSelectorIs(ASTPath.TRUE_EXPRESSION)) {
+                    return conditionalExpression.getTrueExpression();
+                } else {
+                    return conditionalExpression.getFalseExpression();
+                }
+            }
+            case DO_WHILE_LOOP: {
+                DoWhileLoopTree doWhileLoop = (DoWhileLoopTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+                    return doWhileLoop.getCondition();
+                } else {
+                    return doWhileLoop.getStatement();
+                }
+            }
+            case ENHANCED_FOR_LOOP: {
+                EnhancedForLoopTree enhancedForLoop = (EnhancedForLoopTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
+                    return enhancedForLoop.getVariable();
+                } else if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+                    return enhancedForLoop.getExpression();
+                } else {
+                    return enhancedForLoop.getStatement();
+                }
+            }
+            case EXPRESSION_STATEMENT: {
+                ExpressionStatementTree expressionStatement = (ExpressionStatementTree) actualNode;
+                return expressionStatement.getExpression();
+            }
+            case FOR_LOOP: {
+                ForLoopTree forLoop = (ForLoopTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
+                    int arg = astNode.getArgument();
+                    List<? extends StatementTree> inits = forLoop.getInitializer();
+                    if (arg >= inits.size()) {
+                        return null;
+                    }
+                    return inits.get(arg);
+                } else if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+                    return forLoop.getCondition();
+                } else if (astNode.childSelectorIs(ASTPath.UPDATE)) {
+                    int arg = astNode.getArgument();
+                    List<? extends ExpressionStatementTree> updates = forLoop.getUpdate();
+                    if (arg >= updates.size()) {
+                        return null;
+                    }
+                    return updates.get(arg);
+                } else {
+                    return forLoop.getStatement();
+                }
+            }
+            case IF: {
+                IfTree iff = (IfTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+                    return iff.getCondition();
+                } else if (astNode.childSelectorIs(ASTPath.THEN_STATEMENT)) {
+                    return iff.getThenStatement();
+                } else {
+                    return iff.getElseStatement();
+                }
+            }
+            case INSTANCE_OF: {
+                InstanceOfTree instanceOf = (InstanceOfTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+                    return instanceOf.getExpression();
+                } else {
+                    return instanceOf.getType();
+                }
+            }
+            case LABELED_STATEMENT: {
+                LabeledStatementTree labeledStatement = (LabeledStatementTree) actualNode;
+                return labeledStatement.getStatement();
+            }
+            case LAMBDA_EXPRESSION: {
+                LambdaExpressionTree lambdaExpression = (LambdaExpressionTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
+                    int arg = astNode.getArgument();
+                    List<? extends VariableTree> params = lambdaExpression.getParameters();
+                    if (arg >= params.size()) {
+                        return null;
+                    }
+                    return params.get(arg);
+                } else {
+                    return lambdaExpression.getBody();
+                }
+            }
+            case MEMBER_REFERENCE: {
+                MemberReferenceTree memberReference = (MemberReferenceTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.QUALIFIER_EXPRESSION)) {
+                    return memberReference.getQualifierExpression();
+                } else {
+                    int arg = astNode.getArgument();
+                    List<? extends ExpressionTree> typeArgs = memberReference.getTypeArguments();
+                    if (arg >= typeArgs.size()) {
+                        return null;
+                    }
+                    return typeArgs.get(arg);
+                }
+            }
+            case MEMBER_SELECT: {
+                MemberSelectTree memberSelect = (MemberSelectTree) actualNode;
+                return memberSelect.getExpression();
+            }
+            case METHOD: {
+                MethodTree method = (MethodTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.TYPE)) {
+                    return method.getReturnType();
+                } else if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
+                    int arg = astNode.getArgument();
+                    List<? extends VariableTree> params =
+                            method.getParameters();
+                    return arg < 0 ? method.getReceiverParameter()
+                            : arg < params.size() ? params.get(arg) : null;
+                } else if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) {
+                    int arg = astNode.getArgument();
+                    return method.getTypeParameters().get(arg);
+                } else {    // BODY
+                    return method.getBody();
+                }
+            }
+            case METHOD_INVOCATION: {
+                MethodInvocationTree methodInvocation = (MethodInvocationTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
+                    int arg = astNode.getArgument();
+                    List<? extends Tree> typeArgs = methodInvocation.getTypeArguments();
+                    if (arg >= typeArgs.size()) {
+                        return null;
+                    }
+                    return typeArgs.get(arg);
+                } else if (astNode.childSelectorIs(ASTPath.METHOD_SELECT)) {
+                    return methodInvocation.getMethodSelect();
+                } else {
+                    int arg = astNode.getArgument();
+                    List<? extends ExpressionTree> args = methodInvocation.getArguments();
+                    if (arg >= args.size()) {
+                        return null;
+                    }
+                    return args.get(arg);
+                }
+            }
+            case NEW_ARRAY: {
+                NewArrayTree newArray = (NewArrayTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.TYPE)) {
+                    Type type = ((JCTree.JCNewArray) newArray).type;
+                    Tree typeTree = Insertions.TypeTree.fromType(type);
+                    int arg = astNode.getArgument();
+                    if (arg == 0 && astPath.size() == ix+1) {
+                        return newArray;
+                        // if (astPath.size() != ix+1) { return null; }
+                        // return typeTree;
+                        // return ((ArrayTypeTree) typeTree).getType();
+                        // return newArray;
+                    }
+                    typeTree = ((NewArrayTree) typeTree).getType();
+                    while (--arg > 0) {
+                        if (typeTree.getKind() != Tree.Kind.ARRAY_TYPE) {
+                            return null;
+                        }
+                        typeTree = ((ArrayTypeTree) typeTree).getType();
+                    }
+                    return typeTree;
+                } else if (astNode.childSelectorIs(ASTPath.DIMENSION)) {
+                    int arg = astNode.getArgument();
+                    List<? extends ExpressionTree> dims = newArray.getDimensions();
+                    return arg < dims.size() ? dims.get(arg) : null;
+                } else {
+                    int arg = astNode.getArgument();
+                    List<? extends ExpressionTree> inits =
+                          newArray.getInitializers();
+                    return arg < inits.size() ? inits.get(arg) : null;
+                }
+            }
+            case NEW_CLASS: {
+                NewClassTree newClass = (NewClassTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.ENCLOSING_EXPRESSION)) {
+                    return newClass.getEnclosingExpression();
+                } else if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
+                    int arg = astNode.getArgument();
+                    List<? extends Tree> typeArgs = newClass.getTypeArguments();
+                    if (arg >= typeArgs.size()) {
+                        return null;
+                    }
+                    return typeArgs.get(arg);
+                } else if (astNode.childSelectorIs(ASTPath.IDENTIFIER)) {
+                    return newClass.getIdentifier();
+                } else if (astNode.childSelectorIs(ASTPath.ARGUMENT)) {
+                    int arg = astNode.getArgument();
+                    List<? extends ExpressionTree> args = newClass.getArguments();
+                    if (arg >= args.size()) {
+                        return null;
+                    }
+                    return args.get(arg);
+                } else {
+                    return newClass.getClassBody(); // For anonymous classes
+                }
+            }
+            case PARAMETERIZED_TYPE: {
+                ParameterizedTypeTree parameterizedType = (ParameterizedTypeTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.TYPE)) {
+                    return parameterizedType.getType();
+                } else {
+                    int arg = astNode.getArgument();
+                    List<? extends Tree> typeArgs = parameterizedType.getTypeArguments();
+                    if (arg >= typeArgs.size()) {
+                        return null;
+                    }
+                    return typeArgs.get(arg);
+                }
+            }
+            case PARENTHESIZED: {
+                ParenthesizedTree parenthesized = (ParenthesizedTree) actualNode;
+                return parenthesized.getExpression();
+            }
+            case RETURN: {
+                ReturnTree returnn = (ReturnTree) actualNode;
+                return returnn.getExpression();
+            }
+            case SWITCH: {
+                SwitchTree zwitch = (SwitchTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+                    return zwitch.getExpression();
+                } else {
+                    int arg = astNode.getArgument();
+                    List<? extends CaseTree> cases = zwitch.getCases();
+                    if (arg >= cases.size()) {
+                        return null;
+                    }
+                    return cases.get(arg);
+                }
+            }
+            case SYNCHRONIZED: {
+                SynchronizedTree synchronizzed = (SynchronizedTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+                    return synchronizzed.getExpression();
+                } else {
+                    return synchronizzed.getBlock();
+                }
+            }
+            case THROW: {
+                ThrowTree throww = (ThrowTree) actualNode;
+                return throww.getExpression();
+            }
+            case TRY: {
+                TryTree tryy = (TryTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.BLOCK)) {
+                    return tryy.getBlock();
+                } else if (astNode.childSelectorIs(ASTPath.CATCH)) {
+                    int arg = astNode.getArgument();
+                    List<? extends CatchTree> catches = tryy.getCatches();
+                    if (arg >= catches.size()) {
+                        return null;
+                    }
+                    return catches.get(arg);
+                } else if (astNode.childSelectorIs(ASTPath.FINALLY_BLOCK)) {
+                    return tryy.getFinallyBlock();
+                } else {
+                    int arg = astNode.getArgument();
+                    List<? extends Tree> resources = tryy.getResources();
+                    if (arg >= resources.size()) {
+                        return null;
+                    }
+                    return resources.get(arg);
+                }
+            }
+            case TYPE_CAST: {
+                TypeCastTree typeCast = (TypeCastTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.TYPE)) {
+                    return typeCast.getType();
+                } else {
+                    return typeCast.getExpression();
+                }
+            }
+            case TYPE_PARAMETER: {
+                TypeParameterTree typeParam = (TypeParameterTree) actualNode;
+                List<? extends Tree> bounds = typeParam.getBounds();
+                int arg = astNode.getArgument();
+                return bounds.get(arg);
+            }
+            case UNION_TYPE: {
+                UnionTypeTree unionType = (UnionTypeTree) actualNode;
+                int arg = astNode.getArgument();
+                List<? extends Tree> typeAlts = unionType.getTypeAlternatives();
+                if (arg >= typeAlts.size()) {
+                    return null;
+                }
+                return typeAlts.get(arg);
+            }
+            case VARIABLE: {
+                // A VariableTree can have modifiers, but we only look at
+                // the initializer and type because modifiers can't be
+                // annotated. Any annotations on the LHS must be on the type.
+                VariableTree var = (VariableTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
+                    return var.getInitializer();
+                } else if (astNode.childSelectorIs(ASTPath.TYPE)) {
+                    return var.getType();
+                } else {
+                    return null;
+                }
+            }
+            case WHILE_LOOP: {
+                WhileLoopTree whileLoop = (WhileLoopTree) actualNode;
+                if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+                    return whileLoop.getCondition();
+                } else {
+                    return whileLoop.getStatement();
+                }
+            }
+            default: {
+                if (ASTPath.isBinaryOperator(actualNode.getKind())) {
+                    BinaryTree binary = (BinaryTree) actualNode;
+                    if (astNode.childSelectorIs(ASTPath.LEFT_OPERAND)) {
+                        return binary.getLeftOperand();
+                    } else {
+                        return binary.getRightOperand();
+                    }
+                } else if (ASTPath.isCompoundAssignment(actualNode.getKind())) {
+                    CompoundAssignmentTree compoundAssignment =
+                            (CompoundAssignmentTree) actualNode;
+                    if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
+                        return compoundAssignment.getVariable();
+                    } else {
+                        return compoundAssignment.getExpression();
+                    }
+                } else if (ASTPath.isUnaryOperator(actualNode.getKind())) {
+                    UnaryTree unary = (UnaryTree) actualNode;
+                    return unary.getExpression();
+                } else if (isWildcard(actualNode.getKind())) {
+                    WildcardTree wildcard = (WildcardTree) actualNode;
+                    return wildcard.getBound();
+                } else {
+                    throw new IllegalArgumentException("Illegal kind: "
+                            + actualNode.getKind());
+                }
+              }
+            }
+        } catch (RuntimeException ex) { return null; }
+    }
+
+    private boolean checkNull(List<Tree> path, int ix) {
+        Tree node = path.get(path.size()-1);
+        int last = astPath.size() - 1;
+        ASTPath.ASTEntry entry = astPath.get(ix);
+        Tree.Kind kind = entry.getTreeKind();
+
+        switch (kind) {
+        // case ANNOTATION:
+        // case INTERFACE:
+        case CLASS:  // "extends" clause?
+            return ASTPath.isClassEquiv(kind)
+                    && ix == last && entry.getArgument() == -1
+                    && entry.childSelectorIs(ASTPath.BOUND);
+        case TYPE_PARAMETER:
+            return node.getKind() == Tree.Kind.TYPE_PARAMETER
+                    && ix == last && entry.getArgument() == 0
+                    && entry.childSelectorIs(ASTPath.BOUND);
+        case METHOD:  // nullary constructor? receiver?
+            if (node.getKind() != Tree.Kind.METHOD) { return false; }
+            MethodTree method = (MethodTree) node;
+            List<? extends VariableTree> params = method.getParameters();
+            if ("<init>".equals(method.getName().toString())) {
+                if (ix == last) { return true; }
+                ASTPath.ASTEntry next = astPath.get(++ix);
+                String selector = next.getChildSelector();
+                Tree typeTree =
+                    ASTPath.TYPE_PARAMETER.equals(selector)
+                        ? method.getTypeParameters().get(next.getArgument())
+                  : ASTPath.PARAMETER.equals(selector)
+                        ? params.get(next.getArgument()).getType()
+                  : null;
+                return typeTree != null && checkTypePath(ix, typeTree);
+            } else if (entry.childSelectorIs(ASTPath.PARAMETER)
+                    && entry.getArgument() == -1) {
+                if (ix == last) { return true; }
+                VariableTree rcvrParam = method.getReceiverParameter();
+                if (rcvrParam == null) {  // TODO
+                  // ClassTree clazz = methodReceiverType(path);
+                  // return checkReceiverType(ix,
+                  //    ((JCTree.JCClassDecl) clazz).type);
+                } else {
+                  return checkTypePath(ix+1, rcvrParam.getType());
+                }
+            }
+            return false;
+        case NEW_ARRAY:
+            if (node.getKind() != Tree.Kind.NEW_ARRAY) { return false; }
+            NewArrayTree newArray = (NewArrayTree) node;
+            int arg = entry.getArgument();
+            if (entry.childSelectorIs(ASTPath.TYPE)) {
+                if (ix == last) { return true; }
+                // Tree t = newArray.getType();
+                // int depth = 1;
+                // while (t.getKind() == Tree.Kind.ARRAY_TYPE) {
+                //    t = ((ArrayTypeTree) t).getType();
+                //    ++depth;
+                // }
+                return arg == arrayDepth(newArray);
+            } else {
+                List<? extends ExpressionTree> typeTrees =
+                        entry.childSelectorIs(ASTPath.DIMENSION)
+                                ? newArray.getDimensions()
+                      : entry.childSelectorIs(ASTPath.INITIALIZER)
+                                ? newArray.getInitializers()
+                      : null;
+                return typeTrees != null && arg < typeTrees.size()
+                      && checkTypePath(ix+1, typeTrees.get(arg));
+            }
+        case UNBOUNDED_WILDCARD:
+            return isBoundableWildcard(path, path.size()-1);
+        default:  // TODO: casts?
+            return false;
+        }
+    }
+
+    private static int arrayDepth(Tree tree) {
+        if (tree.getKind() == Tree.Kind.NEW_ARRAY) {
+            NewArrayTree newArray = (NewArrayTree) tree;
+            Tree type = newArray.getType();
+            if (type != null) {
+                return type.accept(new SimpleTreeVisitor<Integer, Integer>() {
+                    @Override
+                    public Integer visitArrayType(ArrayTypeTree t, Integer i) {
+                        return t.getType().accept(this, i+1);
+                    }
+                    @Override
+                    public Integer defaultAction(Tree t, Integer i) {
+                        return i;
+                    }
+                }, 1);
+            }
+            int depth = newArray.getDimensions().size();
+            for (ExpressionTree elem : newArray.getInitializers()) {
+                Tree.Kind kind = elem.getKind();
+                if (kind == Tree.Kind.NEW_ARRAY
+                        || kind == Tree.Kind.ARRAY_TYPE) {
+                    depth = Math.max(depth, arrayDepth(elem)+1);
+                }
+            }
+            return depth;
+        } else if (tree.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+            return arrayDepth(((AnnotatedTypeTree) tree).getUnderlyingType());
+        } else if (tree.getKind() == Tree.Kind.ARRAY_TYPE) {
+            return 1 + arrayDepth(((ArrayTypeTree) tree).getType());
+        } else {
+            return 0;
+        }
+    }
+
+    private boolean checkReceiverType(int i, Type t) {
+        if (t == null) { return false; }
+        while (++i < astPath.size()) {
+            ASTPath.ASTEntry entry = astPath.get(i);
+            switch (entry.getTreeKind()) {
+            case ANNOTATED_TYPE:
+              break;
+            case ARRAY_TYPE:
+              if (t.getKind() != TypeKind.ARRAY) { return false; }
+              t = ((Type.ArrayType) t).getComponentType();
+              break;
+            case MEMBER_SELECT:
+              // TODO
+              break;
+            case PARAMETERIZED_TYPE:
+              if (entry.childSelectorIs(ASTPath.TYPE_PARAMETER)) {
+                if (!t.isParameterized()) { return false; }
+                List<Type> args = t.getTypeArguments();
+                int a = entry.getArgument();
+                if (a >= args.size()) { return false; }
+                t = args.get(a);
+              } // else TYPE -- stay?
+              break;
+            case TYPE_PARAMETER:
+              if (t.getKind() != TypeKind.WILDCARD) { return false; }
+              t = t.getLowerBound();
+              break;
+            case EXTENDS_WILDCARD:
+              if (t.getKind() != TypeKind.WILDCARD) { return false; }
+              t = ((Type.WildcardType) t).getExtendsBound();
+              break;
+            case SUPER_WILDCARD:
+              if (t.getKind() != TypeKind.WILDCARD) { return false; }
+              t = ((Type.WildcardType) t).getSuperBound();
+              break;
+            case UNBOUNDED_WILDCARD:
+              if (t.getKind() != TypeKind.WILDCARD) { return false; }
+              t = t.getLowerBound();
+              break;
+            default:
+              return false;
+            }
+            if (t == null) { return false; }
+        }
+        return true;
+    }
+
+    private static ClassTree methodReceiverType(TreePath path) {
+      Tree t = path.getLeaf();
+      if (t.getKind() != Tree.Kind.METHOD) { return null; }
+      JCTree.JCMethodDecl method = (JCTree.JCMethodDecl) t;
+      if ((method.mods.flags & Flags.STATIC) != 0) { return null; }
+
+      // Find the name of the class with type parameters to create the
+      // receiver. Walk up the tree and pick up class names to add to
+      // the receiver type. Since we're starting from the innermost
+      // class, the classes we get to at earlier iterations of the loop
+      // are inside of the classes we get to at later iterations.
+      TreePath parent = path.getParentPath();
+      Tree leaf = parent.getLeaf();
+      Tree.Kind kind = leaf.getKind();
+
+      // For an inner class constructor, the receiver comes from the
+      // superclass, so skip past the first type definition.
+      boolean skip = method.getReturnType() == null;
+
+      while (kind != Tree.Kind.COMPILATION_UNIT
+          && kind != Tree.Kind.NEW_CLASS) {
+        if (kind == Tree.Kind.CLASS
+            || kind == Tree.Kind.INTERFACE
+            || kind == Tree.Kind.ENUM
+            || kind == Tree.Kind.ANNOTATION_TYPE) {
+          JCTree.JCClassDecl clazz = (JCTree.JCClassDecl) leaf;
+          boolean isStatic = kind == Tree.Kind.INTERFACE
+              || kind == Tree.Kind.ENUM
+              || clazz.getModifiers().getFlags().contains(Modifier.STATIC);
+          skip &= !isStatic;
+          if (!skip || isStatic) { return clazz; }
+          skip = false;
+        }
+
+        parent = path.getParentPath();
+        leaf = parent.getLeaf();
+        kind = leaf.getKind();
+      }
+
+      throw new IllegalArgumentException("no receiver for non-inner constructor");
+    }
+
+    private boolean checkTypePath(int i, Tree typeTree) {
+        try {
+loop:       while (typeTree != null && i < astPath.size()) {
+                ASTPath.ASTEntry entry = astPath.get(i);
+                Tree.Kind kind = entry.getTreeKind();
+                switch (kind) {
+                case ANNOTATED_TYPE:
+                    typeTree = ((AnnotatedTypeTree) typeTree)
+                        .getUnderlyingType();
+                    continue;
+                case ARRAY_TYPE:
+                    typeTree = ((ArrayTypeTree) typeTree).getType();
+                    break;
+                case MEMBER_SELECT:
+                    typeTree = ((MemberSelectTree) typeTree).getExpression();
+                    break;
+                case PARAMETERIZED_TYPE:
+                    if (entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
+                        int arg = entry.getArgument();
+                        typeTree = ((ParameterizedTypeTree) typeTree)
+                                .getTypeArguments().get(arg);
+                    } else {  // TYPE
+                        typeTree = ((ParameterizedTypeTree) typeTree).getType();
+                    }
+                    break;
+                default:
+                    if (isWildcard(kind)) {
+                        return ++i == astPath.size();  // ???
+                    }
+                    break loop;
+                }
+                ++i;
+            }
+        } catch (RuntimeException ex) {}
+        return false;
+    }
+
+    /**
+     * Determines if the given kinds match, false otherwise. Two kinds match if
+     * they're exactly the same or if the two kinds are both compound
+     * assignments, unary operators, binary operators or wildcards.
+     * <p>
+     * This is necessary because in the JAIF file these kinds are represented by
+     * their general types (i.e. BinaryOperator, CompoundOperator, etc.) rather
+     * than their kind (i.e. PLUS, MINUS, PLUS_ASSIGNMENT, XOR_ASSIGNMENT,
+     * etc.). Internally, a single kind is used to represent each general type
+     * (i.e. PLUS is used for BinaryOperator, PLUS_ASSIGNMENT is used for
+     * CompoundAssignment, etc.). Yet, the actual source nodes have the correct
+     * kind. So if an AST path entry has a PLUS kind, that really means it could
+     * be any BinaryOperator, resulting in PLUS matching any other
+     * BinaryOperator.
+     *
+     * @param kind1
+     *            the first kind to match
+     * @param kind2
+     *            the second kind to match
+     * @return {@code true} if the kinds match as described above, {@code false}
+     *         otherwise.
+     */
+    private boolean kindsMatch(Tree.Kind kind1, Tree.Kind kind2) {
+        return kind1 == kind2 ? true
+              : ASTPath.isClassEquiv(kind1)
+                      ? ASTPath.isClassEquiv(kind2)
+              : ASTPath.isCompoundAssignment(kind1)
+                      ? ASTPath.isCompoundAssignment(kind2)
+              : ASTPath.isUnaryOperator(kind1)
+                      ? ASTPath.isUnaryOperator(kind2)
+              : ASTPath.isBinaryOperator(kind1)
+                      ? ASTPath.isBinaryOperator(kind2)
+              : ASTPath.isWildcard(kind1)
+                      ? ASTPath.isWildcard(kind2)
+              : false;
+    }
+
+    /**
+     * Determines if the given kind is a binary operator.
+     *
+     * @param kind
+     *            the kind to test
+     * @return true if the given kind is a binary operator
+     */
+    public boolean isBinaryOperator(Tree.Kind kind) {
+        return kind == Tree.Kind.MULTIPLY || kind == Tree.Kind.DIVIDE
+                || kind == Tree.Kind.REMAINDER || kind == Tree.Kind.PLUS
+                || kind == Tree.Kind.MINUS || kind == Tree.Kind.LEFT_SHIFT
+                || kind == Tree.Kind.RIGHT_SHIFT
+                || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT
+                || kind == Tree.Kind.LESS_THAN
+                || kind == Tree.Kind.GREATER_THAN
+                || kind == Tree.Kind.LESS_THAN_EQUAL
+                || kind == Tree.Kind.GREATER_THAN_EQUAL
+                || kind == Tree.Kind.EQUAL_TO || kind == Tree.Kind.NOT_EQUAL_TO
+                || kind == Tree.Kind.AND || kind == Tree.Kind.XOR
+                || kind == Tree.Kind.OR || kind == Tree.Kind.CONDITIONAL_AND
+                || kind == Tree.Kind.CONDITIONAL_OR;
+    }
+
+    public boolean isExpression(Tree.Kind kind) {
+        switch (kind) {
+        case ARRAY_ACCESS:
+        case ASSIGNMENT:
+        case CONDITIONAL_EXPRESSION:
+        case EXPRESSION_STATEMENT:
+        case MEMBER_SELECT:
+        case MEMBER_REFERENCE:
+        case IDENTIFIER:
+        case INSTANCE_OF:
+        case METHOD_INVOCATION:
+        case NEW_ARRAY:
+        case NEW_CLASS:
+        case LAMBDA_EXPRESSION:
+        case PARENTHESIZED:
+        case TYPE_CAST:
+        case POSTFIX_INCREMENT:
+        case POSTFIX_DECREMENT:
+        case PREFIX_INCREMENT:
+        case PREFIX_DECREMENT:
+        case UNARY_PLUS:
+        case UNARY_MINUS:
+        case BITWISE_COMPLEMENT:
+        case LOGICAL_COMPLEMENT:
+        case MULTIPLY:
+        case DIVIDE:
+        case REMAINDER:
+        case PLUS:
+        case MINUS:
+        case LEFT_SHIFT:
+        case RIGHT_SHIFT:
+        case UNSIGNED_RIGHT_SHIFT:
+        case LESS_THAN:
+        case GREATER_THAN:
+        case LESS_THAN_EQUAL:
+        case GREATER_THAN_EQUAL:
+        case EQUAL_TO:
+        case NOT_EQUAL_TO:
+        case AND:
+        case XOR:
+        case OR:
+        case CONDITIONAL_AND:
+        case CONDITIONAL_OR:
+        case MULTIPLY_ASSIGNMENT:
+        case DIVIDE_ASSIGNMENT:
+        case REMAINDER_ASSIGNMENT:
+        case PLUS_ASSIGNMENT:
+        case MINUS_ASSIGNMENT:
+        case LEFT_SHIFT_ASSIGNMENT:
+        case RIGHT_SHIFT_ASSIGNMENT:
+        case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+        case AND_ASSIGNMENT:
+        case XOR_ASSIGNMENT:
+        case OR_ASSIGNMENT:
+        case INT_LITERAL:
+        case LONG_LITERAL:
+        case FLOAT_LITERAL:
+        case DOUBLE_LITERAL:
+        case BOOLEAN_LITERAL:
+        case CHAR_LITERAL:
+        case STRING_LITERAL:
+        case NULL_LITERAL:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /**
+     * Determines if the given kind is a wildcard.
+     *
+     * @param kind
+     *            the kind to test
+     * @return true if the given kind is a wildcard
+     */
+    private boolean isWildcard(Tree.Kind kind) {
+        return kind == Tree.Kind.UNBOUNDED_WILDCARD
+                || kind == Tree.Kind.EXTENDS_WILDCARD
+                || kind == Tree.Kind.SUPER_WILDCARD;
+    }
+
+    // The following check is necessary because Oracle has decided that
+    //   x instanceof Class<? extends Object>
+    // will remain illegal even though it means the same thing as
+    //   x instanceof Class<?>.
+    private boolean isBoundableWildcard(List<Tree> actualPath, int i) {
+        if (i <= 0) { return false; }
+        Tree actualNode = actualPath.get(i);
+        if (actualNode.getKind() == Tree.Kind.UNBOUNDED_WILDCARD) {
+          // isWildcard(actualNode.getKind())
+          // TODO: refactor GenericArrayLoc to use same code?
+          Tree ancestor = actualPath.get(i-1);
+          if (ancestor.getKind() == Tree.Kind.INSTANCE_OF) {
+            TreeFinder.warn.debug("WARNING: wildcard bounds not allowed "
+                + "in 'instanceof' expression; skipping insertion%n");
+            return false;
+          } else if (i > 1 && ancestor.getKind() ==
+              Tree.Kind.PARAMETERIZED_TYPE) {
+            ancestor = actualPath.get(i-2);
+            if (ancestor.getKind() == Tree.Kind.ARRAY_TYPE) {
+              TreeFinder.warn.debug("WARNING: wildcard bounds not allowed "
+                  + "in 'instanceof' expression; skipping insertion%n");
+              return false;
+            }
+          }
+          return true;
+        }
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Kind getKind() {
+        return Kind.AST_PATH;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public String toString() {
+        return "ASTPathCriterion: " + astPath;
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/find/AnnotationInsertion.java b/annotation-file-utilities/src/annotator/find/AnnotationInsertion.java
new file mode 100644
index 0000000..2e28b87
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/AnnotationInsertion.java
@@ -0,0 +1,141 @@
+package annotator.find;
+
+import plume.Pair;
+
+/**
+ * Specifies an annotation to be inserted into a source file.
+ */
+public class AnnotationInsertion extends Insertion {
+
+    /**
+     * The annotation to insert.
+     */
+    private final String annotation;
+    private String type;
+    private boolean generateBound;
+    private boolean generateExtends;
+    private boolean wasGenerateExtends;
+
+    /**
+     * Creates a new insertion.
+     *
+     * @param annotation the annotation to insert, which starts with "@"
+     * @param criteria where to insert the annotation
+     * @param separateLine whether to insert the annotation on its own
+     */
+    public AnnotationInsertion(String annotation, Criteria criteria, boolean separateLine) {
+        super(criteria, separateLine);
+        this.annotation = annotation;
+        type = null;
+        generateBound = false;
+        generateExtends = false;
+        wasGenerateExtends = false;
+    }
+
+    /**
+     * Creates a new insertion with an empty criteria and the text inserted on
+     * the same line.
+     *
+     * @param annotation the text to insert
+     */
+    public AnnotationInsertion(String annotation) {
+        this(annotation, new Criteria(), false);
+    }
+
+    public boolean isGenerateExtends() {
+        return generateExtends;
+    }
+
+    public boolean isGenerateBound() {
+        return generateBound;
+    }
+
+    public void setGenerateExtends(boolean generateExtends) {
+        this.generateExtends = generateExtends;
+        this.wasGenerateExtends |= generateExtends;
+    }
+
+    public void setGenerateBound(boolean b) {
+        generateBound = b;
+    }
+
+    /**
+     * Gets the insertion text.
+     *
+     * @param comments
+     *            if true, the annotation will be surrounded by block comments
+     * @param abbreviate
+     *            if true, the package name will be removed from the annotation.
+     *            The package name can be retrieved again by calling the
+     *            {@link #getPackageName()} method.
+     * @return the text to insert
+     */
+    protected String getText(boolean comments, boolean abbreviate) {
+        String result = annotation;
+        if (abbreviate) {
+            Pair<String, String> ps = removePackage(result);
+            String packageName = ps.a;
+            if (packageName != null) {
+                packageNames.add(packageName);
+                result = ps.b;
+            }
+        }
+        if (!result.startsWith("@")) {
+            throw new Error("Illegal insertion, must start with @: " + result);
+        }
+
+        // We insert a "new " when annotating a variable initializer that is a
+        // bare array expression (e.g., as in "int[] a = {0, 1};")  Since the
+        // syntax doesn't permit adding the type annotation in front of the
+        // expression, we generate the explicit "new"
+        // (as in "int[] a = new int[] {0, 1}") to provide a legal insertion site.
+
+        if (type != null) {
+            result = "new " + result + " " + type;
+        } else if (generateBound) {
+            result += " Object &";
+        } else if (generateExtends) {
+            result = " extends " + result + " Object";
+        }
+        return comments ? "/*" + result + "*/" : result;
+    }
+
+    /**
+     * Gets the raw, unmodified annotation that was passed into the constructor.
+     * @return the annotation
+     */
+    public String getAnnotation() {
+        return annotation;
+    }
+
+    /** {@inheritDoc} */
+    protected boolean addLeadingSpace(boolean gotSeparateLine, int pos,
+            char precedingChar) {
+        if (generateExtends || precedingChar == '.') {
+            return false;
+        }
+        return super.addLeadingSpace(gotSeparateLine, pos, precedingChar);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected boolean addTrailingSpace(boolean gotSeparateLine) {
+        // Never add a trailing space on a type parameter bound.
+        return !wasGenerateExtends && super.addTrailingSpace(gotSeparateLine);
+    }
+
+    /** {@inheritDoc} */
+    public Kind getKind() {
+        return Kind.ANNOTATION;
+    }
+
+    public String toString() {
+        return annotation + " " + super.toString();
+    }
+
+    public void setType(String s) {
+        this.type = s;
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/find/BoundLocationCriterion.java b/annotation-file-utilities/src/annotator/find/BoundLocationCriterion.java
new file mode 100644
index 0000000..84abb70
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/BoundLocationCriterion.java
@@ -0,0 +1,138 @@
+package annotator.find;
+
+import java.util.List;
+
+import annotations.el.BoundLocation;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+
+public class BoundLocationCriterion implements Criterion {
+
+  private Criterion parentCriterion;
+  private final int boundIndex;
+  private final int paramIndex;
+
+
+  public BoundLocationCriterion(BoundLocation boundLoc) {
+    this(boundLoc.boundIndex, boundLoc.paramIndex);
+  }
+
+  private BoundLocationCriterion(int boundIndex, int paramIndex) {
+    this.boundIndex = boundIndex;
+    this.paramIndex = paramIndex;
+
+    if (boundIndex != -1) {
+      this.parentCriterion = new BoundLocationCriterion(-1, paramIndex);
+    } else if (paramIndex != -1) {
+      this.parentCriterion = null;
+    }
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    // System.out.printf("BoundLocationCriterion.isSatisfiedBy(%s):%n  leaf=%s (%s)%n", path, leaf, leaf.getClass());
+
+    TreePath parentPath = path.getParentPath();
+    if (parentPath == null) {
+      return false;
+    }
+
+    Tree parent = parentPath.getLeaf();
+    if (parent == null) {
+      return false;
+    }
+
+    boolean returnValue = false;
+
+    // System.out.printf("BoundLocationCriterion.isSatisfiedBy(%s):%n  leaf=%s (%s)%n  parent=%s (%s)%n", path, leaf, leaf.getClass(), parent, parent.getClass());
+
+    // if boundIndex is not null, need to check that this is right bound
+    // in parent
+    if (boundIndex != -1) {
+      if (parent instanceof TypeParameterTree) {
+        List<? extends Tree> bounds = ((TypeParameterTree) parent).getBounds();
+        int ix = boundIndex;
+        if (!bounds.isEmpty() && isInterface((JCExpression) bounds.get(0))) {
+          --ix;
+        }
+        if (ix < 0 || ix < bounds.size() && bounds.get(ix) == leaf) {
+          returnValue = parentCriterion.isSatisfiedBy(parentPath);
+        }
+      } else if (boundIndex == 0 && leaf instanceof TypeParameterTree) {
+        List<? extends Tree> bounds = ((TypeParameterTree) leaf).getBounds();
+        if (bounds.isEmpty() || isInterface((JCExpression) bounds.get(0))) {
+          // If the bound is implicit (i.e., a missing "extends Object"),
+          // then permit the match here.
+          returnValue = parentCriterion.isSatisfiedBy(path);
+        } else {
+          Type type = ((JCExpression) bounds.get(0)).type;
+          if (type != null && type.tsym != null && type.tsym.isInterface()) {
+            returnValue = parentCriterion.isSatisfiedBy(parentPath);
+          }
+        }
+      }
+    } else if (paramIndex != -1) {
+      // if paramIndex is not null, need to ensure this present
+      // typeparameter tree represents the correct parameter
+      if (parent instanceof MethodTree || parent instanceof ClassTree) {
+        List<? extends TypeParameterTree> params = null;
+
+        if (parent instanceof MethodTree) {
+          params = ((MethodTree) parent).getTypeParameters();
+        } else if (parent instanceof ClassTree) {
+          params = ((ClassTree) parent).getTypeParameters();
+        }
+
+        if (paramIndex < params.size()) {
+          if (params.get(paramIndex) == leaf) {
+            returnValue = true;
+          }
+        }
+      }
+    }
+
+    if (!returnValue) {
+      return this.isSatisfiedBy(parentPath);
+    } else {
+      return true;
+    }
+  }
+
+  private boolean isInterface(JCExpression bound) {
+    Type type = bound.type;
+    return type != null && type.tsym != null && type.tsym.isInterface();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Kind getKind() {
+    return Kind.BOUND_LOCATION;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public String toString() {
+    return "BoundCriterion: at param index: " + paramIndex +
+      " at bound index: " + boundIndex;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/CallCriterion.java b/annotation-file-utilities/src/annotator/find/CallCriterion.java
new file mode 100644
index 0000000..de30898
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/CallCriterion.java
@@ -0,0 +1,60 @@
+package annotator.find;
+
+import annotations.el.RelativeLocation;
+import annotator.scanner.MethodCallScanner;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class CallCriterion implements Criterion {
+  private final String methodName;
+  private final RelativeLocation loc;
+
+  public CallCriterion(String methodName, RelativeLocation loc) {
+    this.methodName = methodName;
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    if (leaf.getKind() == Tree.Kind.METHOD_INVOCATION) {
+      int indexInSource = MethodCallScanner.indexOfMethodCallTree(path, leaf);
+      boolean b;
+      if (loc.isBytecodeOffset()) {
+        int indexInClass =
+            MethodCallScanner.getMethodCallIndex(methodName, loc.offset);
+        b = (indexInSource == indexInClass);
+      } else {
+        b = (indexInSource == loc.index);
+      }
+      return b;
+    } else {
+      boolean b = this.isSatisfiedBy(path.getParentPath());
+      return b;
+    }
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.METHOD_CALL;
+  }
+
+  @Override
+  public String toString() {
+    return "CallCriterion: in method: " + methodName + " location: " + loc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/CastCriterion.java b/annotation-file-utilities/src/annotator/find/CastCriterion.java
new file mode 100644
index 0000000..824e387
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/CastCriterion.java
@@ -0,0 +1,66 @@
+package annotator.find;
+
+import annotations.el.RelativeLocation;
+import annotator.scanner.CastScanner;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * Criterion for being a specific type cast expression.
+ */
+public class CastCriterion implements Criterion {
+
+  private final String methodName;
+  private final RelativeLocation loc;
+
+  public CastCriterion(String methodName, RelativeLocation loc) {
+    this.methodName = methodName.substring(0, methodName.lastIndexOf(")") + 1);
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    if (leaf.getKind() == Tree.Kind.TYPE_CAST) {
+      int indexInSource = CastScanner.indexOfCastTree(path, leaf);
+      boolean b;
+      if (loc.isBytecodeOffset()) {
+        int indexInClass = CastScanner.getMethodCastIndex(methodName, loc.offset);
+        b = (indexInSource == indexInClass);
+      } else {
+        b = (indexInSource == loc.index);
+      }
+      return b;
+
+    } else {
+      boolean b = this.isSatisfiedBy(path.getParentPath());
+      return b;
+    }
+  }
+
+  public RelativeLocation getLocation() { return loc; }
+
+  @Override
+  public Kind getKind() {
+    return Kind.CAST;
+  }
+
+  @Override
+  public String toString() {
+    return "CastCriterion: in method: " + methodName + " location: " + loc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/CastInsertion.java b/annotation-file-utilities/src/annotator/find/CastInsertion.java
new file mode 100644
index 0000000..5c74564
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/CastInsertion.java
@@ -0,0 +1,94 @@
+package annotator.find;
+
+import type.Type;
+
+/**
+ * Specifies an insertion of a cast into a source file. Stores the type of cast
+ * to insert in addition to the annotation and location.
+ * <p>
+ * In order to restrict the cast to only the specified expression, a cast
+ * insertion is of the form:
+ *
+ * <pre>
+ * ((<em>cast type</em>) (<em>original expression</em>))
+ * </pre>
+ *
+ * This insertion inserts the cast type and parentheses that go before the
+ * original expression. A {@link CloseParenthesisInsertion} must be used
+ * after the expression to close the parentheses left open by this insertion.
+ */
+public class CastInsertion extends Insertion {
+
+  /**
+   * The type to cast to.
+   */
+  private Type type;
+
+  /**
+   * Whether insertion is to take place on a bare array literal.
+   */
+  public boolean onArrayLiteral = false;
+
+  /**
+   * Creates a new CastInsertion.
+   *
+   * @param criteria where to insert the text
+   * @param type the un-annotated type to cast to
+   */
+  public CastInsertion(Criteria criteria, Type type) {
+    super(criteria, false);
+    this.type = type;
+  }
+
+  /**
+   * Gets the type for this insertion. It is assumed that the returned value will be modified
+   * to update the type to be inserted.
+   * @return the type
+   */
+  public Type getType() {
+      return type;
+  }
+
+  protected void setType(Type t) {
+      type = t;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected String getText(boolean comments, boolean abbreviate) {
+    String result = onArrayLiteral
+        ? "((new " + typeToString(type, comments, abbreviate) + " "
+        : "((" + typeToString(type, comments, abbreviate) + ") (";
+    return result;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected boolean addLeadingSpace(boolean gotSeparateLine, int pos,
+      char precedingChar) {
+    // Don't add a leading space if this cast is on the index of an array access.
+    return super.addLeadingSpace(gotSeparateLine, pos, precedingChar)
+           && precedingChar != '[';
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected boolean addTrailingSpace(boolean gotSeparateLine) {
+    // Never add a trailing space after the first part of a cast insertion.
+    return false;
+  }
+
+  public boolean isOnArrayLiteral() {
+    return onArrayLiteral;
+  }
+
+  public void setOnArrayLiteral(boolean onArrayLiteral) {
+    this.onArrayLiteral = onArrayLiteral;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Kind getKind() {
+    return Kind.CAST;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/ClassBoundCriterion.java b/annotation-file-utilities/src/annotator/find/ClassBoundCriterion.java
new file mode 100644
index 0000000..77b4f92
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ClassBoundCriterion.java
@@ -0,0 +1,49 @@
+package annotator.find;
+
+import annotations.el.BoundLocation;
+
+import com.sun.source.util.TreePath;
+import com.sun.source.tree.Tree;
+
+public class ClassBoundCriterion implements Criterion {
+
+  private final String className;
+  public final BoundLocation boundLoc;
+  private final Criterion notInMethodCriterion;
+  private final Criterion boundLocCriterion;
+
+  public ClassBoundCriterion(String className, BoundLocation boundLoc) {
+    this.className = className;
+    this.boundLoc = boundLoc;
+    this.notInMethodCriterion = Criteria.notInMethod();
+    this.boundLocCriterion = Criteria.atBoundLocation(boundLoc);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    return boundLocCriterion.isSatisfiedBy(path) &&
+      notInMethodCriterion.isSatisfiedBy(path);
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.CLASS_BOUND;
+  }
+
+  @Override
+  public String toString() {
+    return "ClassBoundCriterion: for " + className + " at " + boundLoc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/CloseParenthesisInsertion.java b/annotation-file-utilities/src/annotator/find/CloseParenthesisInsertion.java
new file mode 100644
index 0000000..083afa0
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/CloseParenthesisInsertion.java
@@ -0,0 +1,40 @@
+package annotator.find;
+
+/**
+ * This insertion adds two closing parentheses to close the unclosed parentheses
+ * left by a {@link CastInsertion}. This should be inserted after the expression
+ * that's being casted.
+ */
+public class CloseParenthesisInsertion extends Insertion {
+
+    public CloseParenthesisInsertion(Criteria criteria,
+            boolean separateLine) {
+        super(criteria, separateLine);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getText(boolean comments, boolean abbreviate) {
+        return "))";
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean addLeadingSpace(boolean gotSeparateLine, int pos,
+            char precedingChar) {
+        // Never add a leading space when inserting closing parentheses.
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean addTrailingSpace(boolean gotSeparateLine) {
+        // Never add a trailing space when inserting closing parentheses.
+        return false;
+    }
+
+    /** {@inheritDoc} */
+    public Kind getKind() {
+        return Kind.CLOSE_PARENTHESIS;
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/find/ConstructorInsertion.java b/annotation-file-utilities/src/annotator/find/ConstructorInsertion.java
new file mode 100644
index 0000000..e43c61e
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ConstructorInsertion.java
@@ -0,0 +1,102 @@
+package annotator.find;
+
+import java.util.List;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import type.Type;
+
+public class ConstructorInsertion extends TypedInsertion {
+  private ReceiverInsertion receiverInsertion = null;
+  private Set<Insertion> declarationInsertions = new LinkedHashSet<Insertion>();
+
+  /**
+   * Construct a ConstructorInsertion.
+   * <p>
+   * To insert the annotation and the constructor (for example,
+   * {@code @Anno Type this}) the name should be set to the type to insert.
+   * This can either be done before calling this constructor, or by modifying
+   * the return value of {@link #getType()}.
+   *
+   * @param type the type to use when inserting the constructor
+   * @param criteria where to insert the text
+   * @param innerTypeInsertions the inner types to go on this constructor
+   */
+  public ConstructorInsertion(Type type, Criteria criteria,
+      List<Insertion> innerTypeInsertions) {
+    super(type, criteria, true, innerTypeInsertions);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected String getText(boolean comments, boolean abbreviate) {
+    StringBuilder b = new StringBuilder();
+    if (annotationsOnly) {
+      // List<String> annotations = type.getAnnotations();
+      // if (annotations.isEmpty()) { return ""; }
+      // for (String a : annotations) {
+      //  b.append(a);
+      //  b.append(' ');
+      // }
+      // return new AnnotationInsertion(b.toString(), getCriteria(),
+      //    getSeparateLine()).getText(comments, abbreviate);
+      return "";
+    } else {
+      boolean commentAnnotation =
+          comments && getBaseType().getName().isEmpty();
+      String typeString = typeToString(type, commentAnnotation, true);
+      int ix = typeString.lastIndexOf('$');  // FIXME: exclude '$' in source
+      typeString = typeString.substring(ix+1);
+
+      for (Insertion i : declarationInsertions) {
+        b.append(i.getText(commentAnnotation, abbreviate)).append("\n");
+        if (abbreviate) {
+          packageNames.addAll(i.getPackageNames());
+        }
+      }
+      b.append("public ").append(typeString).append("(");
+      if (receiverInsertion != null && !receiverInsertion.getInserted()) {
+        b.append(receiverInsertion.getText(comments, abbreviate));
+      }
+      b.append(") { super(); }");
+      return b.toString();
+    }
+  }
+
+  protected ReceiverInsertion getReceiverInsertion() {
+    return receiverInsertion;
+  }
+
+  public void addReceiverInsertion(ReceiverInsertion recv) {
+    if (receiverInsertion == null) {
+      receiverInsertion = recv;
+    } else {
+      receiverInsertion.getInnerTypeInsertions()
+          .addAll(recv.getInnerTypeInsertions());
+    }
+  }
+
+  public void addDeclarationInsertion(Insertion ins) {
+    declarationInsertions.add(ins);
+    ins.setInserted(true);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected boolean addLeadingSpace(boolean gotSeparateLine, int pos,
+      char precedingChar) {
+    return false;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected boolean addTrailingSpace(boolean gotSeparateLine) {
+    return false;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Kind getKind() {
+    return Kind.CONSTRUCTOR;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/Criteria.java b/annotation-file-utilities/src/annotator/find/Criteria.java
new file mode 100644
index 0000000..c565d29
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/Criteria.java
@@ -0,0 +1,534 @@
+package annotator.find;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import annotations.el.BoundLocation;
+import annotations.el.InnerTypeLocation;
+import annotations.el.LocalLocation;
+import annotations.el.RelativeLocation;
+import annotations.el.TypeIndexLocation;
+import annotations.io.ASTPath;
+import annotations.io.DebugWriter;
+import annotator.Main;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * Represents a set of Criterion objects for locating a program element in
+ * a source tree.
+ * <p>
+ *
+ * This class also contains static factory methods for creating a {@code
+ * Criterion}.
+ */
+public final class Criteria {
+  public static DebugWriter dbug = new DebugWriter();
+
+  /** The set of criterion objects, indexed by kind. */
+  private final Map<Criterion.Kind, Criterion> criteria;
+
+  /**
+   * Creates a new {@code Criteria} without any {@code Criterion}.
+   */
+  public Criteria() {
+    this.criteria = new LinkedHashMap<Criterion.Kind, Criterion>();
+  }
+
+  /**
+   * Add a {@code Criterion} to this {@code Criteria}.
+   *
+   * @param c the criterion to add
+   */
+  public void add(Criterion c) {
+    criteria.put(c.getKind(), c);
+  }
+
+  /**
+   * Determines whether or not the program element at the leaf of the
+   * specified path is satisfied by these criteria.
+   *
+   * @param path the tree path to check against
+   * @param leaf the tree at the leaf of the path; only relevant when the path
+   *        is null, in which case the leaf is a CompilationUnitTree
+   * @return true if all of these criteria are satisfied by the given path,
+   * false otherwise
+   */
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    for (Criterion c : criteria.values()) {
+      if (! c.isSatisfiedBy(path, leaf)) {
+        dbug.debug("UNsatisfied criterion:%n    %s%n    %s%n",
+            c, Main.pathToString(path));
+        return false;
+      } else {
+        dbug.debug("satisfied criterion:%n    %s%n    %s%n",
+            c, Main.pathToString(path));
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Determines whether or not the program element at the leaf of the
+   * specified path is satisfied by these criteria.
+   *
+   * @param path the tree path to check against
+   * @return true if all of these criteria are satisfied by the given path,
+   * false otherwise
+   */
+  public boolean isSatisfiedBy(TreePath path) {
+    for (Criterion c : criteria.values()) {
+      if (! c.isSatisfiedBy(path)) {
+        dbug.debug("UNsatisfied criterion: %s%n", c);
+        return false;
+      } else {
+        dbug.debug("satisfied criterion: %s%n", c);
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Determines whether this is the criteria on a receiver.
+   *
+   * @return true iff this is the criteria on a receiver
+   */
+  public boolean isOnReceiver() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.RECEIVER) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Determines whether this is the criteria on a package.
+   *
+   * @return true iff this is the criteria on a package
+   */
+  public boolean isOnPackage() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.PACKAGE) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Determines whether this is the criteria on a return type.
+   *
+   * @return true iff this is the criteria on a return type
+   */
+  public boolean isOnReturnType() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.RETURN_TYPE) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Determines whether this is the criteria on a local variable.
+   *
+   * @return true iff this is the criteria on a local variable
+   */
+  public boolean isOnLocalVariable() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.LOCAL_VARIABLE) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Determines whether this is the criteria on the RHS of an occurrence
+   * of 'instanceof'.
+   */
+  public boolean isOnInstanceof() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.INSTANCE_OF) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Determines whether this is the criteria on an object initializer.
+   */
+  public boolean isOnNew() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.NEW) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Determines whether this is the criteria on a class {@code extends} bound.
+   */
+  public boolean isOnTypeDeclarationExtendsClause() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.EXTIMPLS_LOCATION) {
+        return ((ExtImplsLocationCriterion) c).getIndex() == -1;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns true if this Criteria is on the given method.
+   */
+  public boolean isOnMethod(String methodname) {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.IN_METHOD) {
+        if (((InMethodCriterion) c).name.equals(methodname)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns true if this Criteria is on the given method.
+   */
+  public boolean isOnFieldDeclaration() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.FIELD
+          && ((FieldCriterion) c).isDeclaration) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Gives the AST path specified in the criteria, if any.
+   *
+   * @return AST path from {@link ASTPathCriterion}, or null if none present
+   */
+  public ASTPath getASTPath() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.AST_PATH) {
+        return ((ASTPathCriterion) c).astPath;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns the name of the class specified in the Criteria, if any.
+   *
+   * @return class name from {@link InClassCriterion}, or null if none present
+   */
+  public String getClassName() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.IN_CLASS) {
+        return ((InClassCriterion) c).className;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns the name of the method specified in the Criteria, if any.
+   *
+   * @return method name from {@link InMethodCriterion}, or null if none present
+   */
+  public String getMethodName() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.IN_METHOD) {
+        return ((InMethodCriterion) c).name;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns the name of the member field specified in the Criteria, if any.
+   *
+   * @return field name from {@link FieldCriterion}, or null if none present
+   */
+  public String getFieldName() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.FIELD) {
+        return ((FieldCriterion) c).varName;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * @return a GenericArrayLocationCriterion if this has one, else null
+   */
+  public GenericArrayLocationCriterion getGenericArrayLocation() {
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.GENERIC_ARRAY_LOCATION) {
+        return (GenericArrayLocationCriterion) c;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * @return a RelativeCriterion if this has one, else null
+   */
+  public RelativeLocation getCastRelativeLocation() {
+    RelativeLocation result = null;
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.CAST) {
+        result = ((CastCriterion) c).getLocation();
+      }
+    }
+    return result;
+  }
+
+  // Returns the last one. Should really return the outermost one.
+  // However, there should not be more than one unless all are equivalent.
+  /**
+   * @return an InClassCriterion if this has one, else null
+   */
+  public InClassCriterion getInClass() {
+    InClassCriterion result = null;
+    for (Criterion c : criteria.values()) {
+      if (c.getKind() == Criterion.Kind.IN_CLASS) {
+        result = (InClassCriterion) c;
+      }
+    }
+    return result;
+  }
+
+  /**
+   * @return true if this is on the zeroth bound of a type
+   */
+  // Used when determining whether an annotation is on an implicit upper
+  // bound (the "extends Object" that is customarily omitted).
+  public boolean onBoundZero() {
+    for (Criterion c : criteria.values()) {
+      switch (c.getKind()) {
+      case CLASS_BOUND:
+        if (((ClassBoundCriterion) c).boundLoc.boundIndex != 0) { break; }
+        return true;
+      case METHOD_BOUND:
+        if (((MethodBoundCriterion) c).boundLoc.boundIndex != 0) { break; }
+        return true;
+      case AST_PATH:
+        ASTPath astPath = ((ASTPathCriterion) c).astPath;
+        if (!astPath.isEmpty()) {
+          ASTPath.ASTEntry entry = astPath.get(-1);
+          if (entry.childSelectorIs(ASTPath.BOUND)
+              && entry.getArgument() == 0) {
+            return true;
+          }
+        }
+        break;
+      default:
+        break;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return criteria.toString();
+  }
+
+
+  ///////////////////////////////////////////////////////////////////////////
+  /// Factory methods
+  ///
+
+  /**
+   * Creates an "is" criterion: that a program element has the specified
+   * kind and name.
+   *
+   * @param kind the program element's kind
+   * @param name the program element's name
+   * @return an "is" criterion
+   */
+  public final static Criterion is(Tree.Kind kind, String name) {
+    return new IsCriterion(kind, name);
+  }
+
+  /**
+   * Creates an "enclosed by" criterion: that a program element is enclosed
+   * by the specified kind of program element.
+   *
+   * @param kind the kind of enclosing program element
+   * @return an "enclosed by" criterion
+   */
+  public final static Criterion enclosedBy(Tree.Kind kind) {
+    return new EnclosedByCriterion(kind);
+  }
+
+  /**
+   * Creates an "in package" criterion: that a program element is enclosed
+   * by the specified package.
+   *
+   * @param name the name of the enclosing package
+   * @return an "in package" criterion
+   */
+  public final static Criterion inPackage(String name) {
+    return new InPackageCriterion(name);
+  }
+
+  /**
+   * Creates an "in class" criterion: that a program element is enclosed
+   * by the specified class.
+   *
+   * @param name the name of the enclosing class
+   * @param exact whether to match only in the class itself, not in its inner classes
+   * @return an "in class" criterion
+   */
+  public final static Criterion inClass(String name, boolean exact) {
+    return new InClassCriterion(name, /*exactmatch=*/ true);
+  }
+
+  /**
+   * Creates an "in method" criterion: that a program element is enclosed
+   * by the specified method.
+   *
+   * @param name the name of the enclosing method
+   * @return an "in method" criterion
+   */
+  public final static Criterion inMethod(String name) {
+    return new InMethodCriterion(name);
+  }
+
+  /**
+   * Creates a "not in method" criterion: that a program element is not
+   * enclosed by any method.
+   *
+   * @return a "not in method" criterion
+   */
+  public final static Criterion notInMethod() {
+    return new NotInMethodCriterion();
+  }
+
+  public final static Criterion packageDecl(String packageName) {
+    return new PackageCriterion(packageName);
+  }
+
+  public final static Criterion atLocation() {
+    return new GenericArrayLocationCriterion();
+  }
+
+  public final static Criterion atLocation(InnerTypeLocation loc) {
+    return new GenericArrayLocationCriterion(loc);
+  }
+
+  @Deprecated
+  public final static Criterion field(String varName) {
+    return new FieldCriterion(varName);
+  }
+
+  public final static Criterion field(String varName, boolean isOnDeclaration) {
+    return new FieldCriterion(varName, isOnDeclaration);
+  }
+
+  public final static Criterion inStaticInit(int blockID) {
+    return new InInitBlockCriterion(blockID, true);
+  }
+
+  public final static Criterion inInstanceInit(int blockID) {
+    return new InInitBlockCriterion(blockID, false);
+  }
+
+  public final static Criterion inFieldInit(String varName) {
+    return new InFieldInitCriterion(varName);
+  }
+
+  public final static Criterion receiver(String methodName) {
+    return new ReceiverCriterion(methodName);
+  }
+
+  public final static Criterion returnType(String className, String methodName) {
+    return new ReturnTypeCriterion(className, methodName);
+  }
+
+  public final static Criterion isSigMethod(String methodName) {
+    return new IsSigMethodCriterion(methodName);
+  }
+
+
+  public final static Criterion param(String methodName, Integer pos) {
+    return new ParamCriterion(methodName, pos);
+  }
+//
+//  public final static Criterion param(String methodName, Integer pos, InnerTypeLocation loc) {
+//    return new ParamCriterion(methodName, pos, loc);
+//  }
+
+
+  public final static Criterion local(String methodName, LocalLocation loc) {
+    return new LocalVariableCriterion(methodName, loc);
+  }
+
+  public final static Criterion cast(String methodName, RelativeLocation loc) {
+    return new CastCriterion(methodName, loc);
+  }
+
+  public final static Criterion newObject(String methodName, RelativeLocation loc) {
+    return new NewCriterion(methodName, loc);
+  }
+
+  public final static Criterion instanceOf(String methodName, RelativeLocation loc) {
+    return new InstanceOfCriterion(methodName, loc);
+  }
+
+  public static Criterion memberReference(String methodName, RelativeLocation loc) {
+    return new MemberReferenceCriterion(methodName, loc);
+  }
+
+  public static Criterion methodCall(String methodName, RelativeLocation loc) {
+    return new CallCriterion(methodName, loc);
+  }
+
+  public final static Criterion typeArgument(String methodName, RelativeLocation loc) {
+    return new TypeArgumentCriterion(methodName, loc);
+  }
+
+  public final static Criterion lambda(String methodName, RelativeLocation loc) {
+    return new LambdaCriterion(methodName, loc);
+  }
+
+  public final static Criterion atBoundLocation(BoundLocation loc) {
+    return new BoundLocationCriterion(loc);
+  }
+
+  public final static Criterion atExtImplsLocation(String className, TypeIndexLocation loc) {
+    return new ExtImplsLocationCriterion(className, loc);
+  }
+
+  public final static Criterion methodBound(String methodName, BoundLocation boundLoc) {
+    return new MethodBoundCriterion(methodName, boundLoc);
+  }
+
+  public final static Criterion classBound(String className, BoundLocation boundLoc) {
+    return new ClassBoundCriterion(className, boundLoc);
+  }
+
+  public final static Criterion astPath(ASTPath astPath) {
+    return new ASTPathCriterion(astPath);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/Criterion.java b/annotation-file-utilities/src/annotator/find/Criterion.java
new file mode 100644
index 0000000..1b874c3
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/Criterion.java
@@ -0,0 +1,86 @@
+package annotator.find;
+
+import com.sun.source.util.TreePath;
+import com.sun.source.tree.Tree;
+
+/**
+ * A criterion for locating a program element in an AST.  A Criterion does
+ * not actually give a location.  Given a location, the isSatisfiedBy
+ * method indicates whether that location is a desired one.
+ */
+public interface Criterion {
+
+    /**
+     * Types of criterion.
+     */
+    public static enum Kind {
+        IN_METHOD,
+        /*
+         * Used for classes, interfaces, enums, annotation types.
+         * What would be a better name?
+         * Also see Criteria.isClassEquiv
+         */
+        IN_CLASS,
+        ENCLOSED_BY,
+        HAS_KIND,
+        NOT_IN_METHOD,
+        TYPE_PARAM,
+        GENERIC_ARRAY_LOCATION,
+        RECEIVER,
+        RETURN_TYPE,
+        SIG_METHOD,
+        PARAM,
+        CAST,
+        LOCAL_VARIABLE,
+        FIELD,
+        NEW,
+        INSTANCE_OF,
+        TYPE_ARGUMENT,
+        METHOD_CALL,
+        METHOD_REFERENCE,
+        LAMBDA_EXPRESSION,
+        BOUND_LOCATION,
+        EXTIMPLS_LOCATION,
+        INTERSECT_LOCATION,
+        METHOD_BOUND,
+        CLASS_BOUND,
+        IN_PACKAGE,
+        AST_PATH,
+        IN_STATIC_INIT,
+        IN_INSTANCE_INIT,
+        IN_FIELD_INIT,
+        /*
+         * This constant is never used. What is the difference to IN_CLASS?
+         * Is one for anywhere within a class and this one only for the
+         * class declaration itself?
+         */
+        CLASS,
+        PACKAGE;
+    }
+
+    /**
+     * Determines if the given tree path is satisfied by this criterion.
+     *
+     * @param path the tree path to check against
+     * @return true if this criterion is satisfied by the given path,
+     * false otherwise
+     */
+    public boolean isSatisfiedBy(TreePath path, Tree tree);
+
+    /**
+     * Determines if the given tree path is satisfied by this criterion.
+     *
+     * @param path the tree path to check against
+     * @return true if this criterion is satisfied by the given path,
+     * false otherwise
+     */
+    public boolean isSatisfiedBy(TreePath path);
+
+    /**
+     * Gets the type of this criterion.
+     *
+     * @return this criterion's kind
+     */
+    public Kind getKind();
+
+}
diff --git a/annotation-file-utilities/src/annotator/find/EnclosedByCriterion.java b/annotation-file-utilities/src/annotator/find/EnclosedByCriterion.java
new file mode 100644
index 0000000..a160187
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/EnclosedByCriterion.java
@@ -0,0 +1,54 @@
+package annotator.find;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * Represents the criterion that a program element is enclosed (directly or
+ * indirect) by a program element of a certain type.
+ */
+final class EnclosedByCriterion implements Criterion {
+
+  private final Tree.Kind kind;
+
+  EnclosedByCriterion(Tree.Kind kind) {
+    this.kind = kind;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Kind getKind() {
+    return Kind.ENCLOSED_BY;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+
+    if (path == null) {
+      return false;
+    }
+
+    for (Tree tree : path) {
+      if (tree.getKind() == kind) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return "enclosed by '" + kind + "'";
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/ExtImplsLocationCriterion.java b/annotation-file-utilities/src/annotator/find/ExtImplsLocationCriterion.java
new file mode 100644
index 0000000..c985039
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ExtImplsLocationCriterion.java
@@ -0,0 +1,103 @@
+package annotator.find;
+
+import java.util.List;
+
+import annotations.el.TypeIndexLocation;
+import annotator.scanner.CommonScanner;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.tree.JCTree;
+
+/**
+ * A criterion to find a given extends or implements clause.
+ */
+public class ExtImplsLocationCriterion implements Criterion {
+
+  private final String classname;
+  private final Integer index;
+
+  /**
+   * @param classname the class name; for debugging purposes only, not used to constrain
+   * @param tyLoc -1 for an extends clause, $ge; 0 for the zero-based implements clause
+   */
+  public ExtImplsLocationCriterion(String classname, TypeIndexLocation tyLoc) {
+    this.classname = classname;
+    this.index = tyLoc.typeIndex;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    // System.out.printf("ExtImplsLocationCriterion.isSatisfiedBy(%s):%n  leaf=%s (%s)%n", path, leaf, leaf.getClass());
+
+    TreePath parentPath = path.getParentPath();
+    if (parentPath == null) {
+      return false;
+    }
+
+    Tree parent = parentPath.getLeaf();
+    if (parent == null) {
+      return false;
+    }
+
+    // System.out.printf("ExtImplsLocationCriterion.isSatisfiedBy(%s):%n  leaf=%s (%s)%n  parent=%s (%s)%n", path, leaf, leaf.getClass(), parent, parent.getClass());
+
+    boolean returnValue = false;
+
+    if (index == -1 && leaf.getKind() == Tree.Kind.CLASS) {
+        return ((JCTree.JCClassDecl) leaf).getExtendsClause() == null;
+    }
+    if (CommonScanner.hasClassKind(parent)) {
+        ClassTree ct = (ClassTree) parent;
+
+        if (index==-1) {
+            Tree ext = ct.getExtendsClause();
+            if (ext == leaf) {
+                returnValue = true;
+            }
+        } else {
+            List<? extends Tree> impls = ct.getImplementsClause();
+            if (index < impls.size() && impls.get(index) == leaf) {
+                returnValue = true;
+            }
+        }
+    }
+
+    if (!returnValue) {
+        return this.isSatisfiedBy(parentPath);
+    } else {
+        return true;
+    }
+  }
+
+  public Integer getIndex() {
+    return index;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Kind getKind() {
+    return Kind.EXTIMPLS_LOCATION;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public String toString() {
+    return "ExtImplsLocationCriterion: class " + classname + " at type index: " + index;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/FieldCriterion.java b/annotation-file-utilities/src/annotator/find/FieldCriterion.java
new file mode 100644
index 0000000..a4fec51
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/FieldCriterion.java
@@ -0,0 +1,56 @@
+package annotator.find;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class FieldCriterion implements Criterion {
+
+  public final String varName;
+  public final boolean isDeclaration;
+  public final Criterion varCriterion;
+  public final Criterion notInMethodCriterion;
+
+  public FieldCriterion(String varName) {
+    this(varName, false);
+  }
+
+  public FieldCriterion(String varName, boolean isDeclaration) {
+    this.varName = varName;
+    this.isDeclaration = isDeclaration;
+    this.varCriterion = Criteria.is(Tree.Kind.VARIABLE, varName);
+    this.notInMethodCriterion = Criteria.notInMethod();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null || (isDeclaration
+            && path.getLeaf().getKind() != Tree.Kind.VARIABLE)) {
+      return false;
+    }
+
+    if (varCriterion.isSatisfiedBy(path) &&
+        notInMethodCriterion.isSatisfiedBy(path)) {
+      return true;
+    } else {
+      return this.isSatisfiedBy(path.getParentPath());
+    }
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.FIELD;
+  }
+
+  @Override
+  public String toString() {
+    return "FieldCriterion: " + varName;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/GenericArrayLocationCriterion.java b/annotation-file-utilities/src/annotator/find/GenericArrayLocationCriterion.java
new file mode 100644
index 0000000..4595df2
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/GenericArrayLocationCriterion.java
@@ -0,0 +1,512 @@
+package annotator.find;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.lang.model.type.TypeKind;
+
+import annotations.el.InnerTypeLocation;
+import annotator.Main;
+
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.WildcardTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+
+/**
+ * GenericArrayLocationCriterion represents the criterion specifying the location
+ * of an element in the generic/array hierarchy as specified by the
+ * JSR 308 proposal.
+ */
+public class GenericArrayLocationCriterion implements Criterion {
+  private static final boolean debug = false;
+
+  // the full location list
+  private final List<TypePathEntry> location;
+
+  // represents all but the last element of the location list
+  // TODO: this field is initialized, but never read!
+  // I removed it, see the version control history.
+  // private Criterion parentCriterion;
+
+  /**
+   * Creates a new GenericArrayLocationCriterion specifying that the element
+   * is an outer type, such as:
+   *  <code>@A List&lt;Integer&gt;</code>
+   * or
+   *  <code>Integer @A []</code>
+   */
+  public GenericArrayLocationCriterion() {
+    this(new ArrayList<TypePathEntry>());
+  }
+
+  /**
+   * Creates a new GenericArrayLocationCriterion representing the given
+   * location.
+   *
+   * @param innerTypeLoc the location of the element being represented
+   */
+  public GenericArrayLocationCriterion(InnerTypeLocation innerTypeLoc) {
+    this(innerTypeLoc.location);
+  }
+
+  private GenericArrayLocationCriterion(List<TypePathEntry> location) {
+    this.location = location;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /**
+   * Determines if the given list holds only {@link TypePathEntry}s with the tag
+   * {@link TypePathEntryKind#ARRAY}.
+   *
+   * @param location the list to check
+   * @return {@code true} if the list only contains
+   *         {@link TypePathEntryKind#ARRAY}, {@code false} otherwise.
+   */
+  private boolean containsOnlyArray(List<TypePathEntry> location) {
+    for (TypePathEntry tpe : location) {
+      if (tpe.tag != TypePathEntryKind.ARRAY) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null || path.getParentPath() == null) {
+      if (debug) {
+        System.out.println("GenericArrayLocationCriterion.isSatisfiedBy() with null path gives false.");
+      }
+      return false;
+    }
+
+    if (debug) {
+      System.out.printf("GenericArrayLocationCriterion.isSatisfiedBy():%n  leaf of path: %s%n  searched location: %s%n",
+              path.getLeaf(), location);
+    }
+
+    TreePath pathRemaining = path;
+    Tree leaf = path.getLeaf();
+
+    // Don't allow annotations directly on these tree kinds if the child type is
+    // a MEMBER_SELECT. This way we'll continue to traverse deeper in the tree
+    // to find the correct MEMBER_SELECT.
+    Tree child = null;
+    if (leaf.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+      child = ((ParameterizedTypeTree) leaf).getType();
+    } else if (leaf.getKind() == Tree.Kind.VARIABLE) {
+      child = ((VariableTree) leaf).getType();
+    } else if (leaf.getKind() == Tree.Kind.NEW_CLASS) {
+      child = ((NewClassTree) leaf).getIdentifier();
+    } else if (leaf.getKind() == Tree.Kind.NEW_ARRAY && !location.isEmpty()) {
+      child = ((NewArrayTree) leaf).getType();
+    }
+    if (child != null && child.getKind() == Tree.Kind.MEMBER_SELECT) {
+      JCExpression exp = ((JCFieldAccess) child).getExpression();
+      if (exp.type != null && exp.type.getKind() == TypeKind.PACKAGE
+          || location.isEmpty()
+          || (location.get(location.size()-1)).tag
+              != TypePathEntryKind.INNER_TYPE) {
+          return false;
+      }
+    }
+
+    if (leaf.getKind() == Tree.Kind.MEMBER_SELECT) {
+      JCFieldAccess fieldAccess = (JCFieldAccess) leaf;
+      if (isStatic(fieldAccess)) {
+        // If this MEMBER_SELECT is for a static class...
+        if (location.isEmpty()) {
+          // ...and it does not go on a compound type, this is the right place.
+          return true;
+        } else if (isGenericOrArray(path.getParentPath().getLeaf())
+            && isGenericOrArray(path.getParentPath().getParentPath().getLeaf())) {
+          // If the two parents above this are compound types, then skip
+          // the compound type directly above. For example, to get to Outer.Inner
+          // of Outer.Inner<K, V> we had to get through the PARAMETERIZED_TYPE
+          // node. But we didn't go deeper in the compound type in the way the
+          // type path defines, we just went deeper to get to the outer type. So
+          // skip this node later when checking to make sure that we're in the
+          // correct part of the compound type.
+          pathRemaining = path.getParentPath();
+        }
+      } else {
+        JCExpression exp = fieldAccess.getExpression();
+        if (exp.getKind() == Tree.Kind.MEMBER_SELECT && exp.type != null
+            && exp.type.getKind() == TypeKind.PACKAGE) {
+          if (location.isEmpty()) {
+            return true;
+          } // else, keep going to make sure we're in the right part of the
+            // compound type
+        } else {
+          if (!location.isEmpty()
+              && location.get(location.size()-1).tag
+                  != TypePathEntryKind.INNER_TYPE) {
+            return false;
+          }
+        }
+      }
+    }
+
+    if (location.isEmpty()) {
+      // no inner type location, want to annotate outermost type
+      // e.g.,  @Nullable List list;
+      //        @Nullable List<String> list;
+      //        String @Nullable [] array;
+      leaf = path.getLeaf();
+      Tree parent = path.getParentPath().getLeaf();
+
+      boolean result = ((leaf.getKind() == Tree.Kind.NEW_ARRAY)
+                        || (leaf.getKind() == Tree.Kind.NEW_CLASS)
+                        || (leaf.getKind() == Tree.Kind.ANNOTATED_TYPE
+                            && isSatisfiedBy(TreePath.getPath(path,
+                              ((AnnotatedTypeTree) leaf).getUnderlyingType())))
+                        || ((isGenericOrArray(leaf)
+                             // or, it might be a raw type
+                             || (leaf.getKind() == Tree.Kind.IDENTIFIER) // IdentifierTree
+                             || (leaf.getKind() == Tree.Kind.METHOD) // MethodTree
+                             || (leaf.getKind() == Tree.Kind.TYPE_PARAMETER) // TypeParameterTree
+                             // I don't know why a GenericArrayLocationCriterion
+                             // is being created in this case, but it is.
+                             || (leaf.getKind() == Tree.Kind.PRIMITIVE_TYPE) // PrimitiveTypeTree
+                             // TODO: do we need wildcards here?
+                             // || leaf instanceof WildcardTree
+                             )
+                            && ! isGenericOrArray(parent)));
+      if (debug) {
+        System.out.printf("GenericArrayLocationCriterion.isSatisfiedBy: locationInParent==null%n  leaf=%s (%s)%n  parent=%s (%s)%n  => %s (%s %s)%n",
+                  leaf, leaf.getClass(), parent, parent.getClass(), result, isGenericOrArray(leaf), ! isGenericOrArray(parent));
+      }
+
+      return result;
+    }
+
+    // If we've made it this far then we've determined that *if* this is the right
+    // place to insert the annotation this is the MEMBER_SELECT it should be
+    // inserted on. So remove the rest of the MEMBER_SELECTs to get down to the
+    // compound type and make sure the compound type location matches.
+    while (pathRemaining.getParentPath().getLeaf().getKind() == Tree.Kind.MEMBER_SELECT) {
+      pathRemaining = pathRemaining.getParentPath();
+    }
+
+    List<TypePathEntry> locationRemaining = new ArrayList<TypePathEntry>(location);
+
+    while (locationRemaining.size() != 0) {
+      // annotating an inner type
+      leaf = pathRemaining.getLeaf();
+      if ((leaf.getKind() == Tree.Kind.NEW_ARRAY)
+          && containsOnlyArray(locationRemaining)) {
+        if (debug) {
+          System.out.println("Found a matching NEW_ARRAY");
+        }
+        return true;
+      }
+      TreePath parentPath = pathRemaining.getParentPath();
+      if (parentPath == null) {
+        if (debug) {
+          System.out.println("Parent path is null and therefore false.");
+        }
+        return false;
+      }
+      Tree parent = parentPath.getLeaf();
+
+      if (parent.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+          // If the parent is an annotated type, we did not really go up a level.
+          // Therefore, skip up one more level.
+          parentPath = parentPath.getParentPath();
+          parent = parentPath.getLeaf();
+      }
+
+      if (debug) {
+        System.out.printf("locationRemaining: %s, leaf: %s parent: %s %s%n",
+                         locationRemaining, Main.treeToString(leaf), Main.treeToString(parent), parent.getClass());
+      }
+
+      TypePathEntry loc = locationRemaining.get(locationRemaining.size()-1);
+      if (loc.tag == TypePathEntryKind.INNER_TYPE) {
+        if (leaf.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+          leaf = parent;
+          parentPath = parentPath.getParentPath();
+          parent = parentPath.getLeaf();
+        }
+        if (leaf.getKind() != Tree.Kind.MEMBER_SELECT) { return false; }
+
+        JCFieldAccess fieldAccess = (JCFieldAccess) leaf;
+        if (isStatic(fieldAccess)) {
+          return false;
+        }
+        locationRemaining.remove(locationRemaining.size()-1);
+        leaf = fieldAccess.selected;
+        pathRemaining = parentPath;
+            // TreePath.getPath(pathRemaining.getCompilationUnit(), leaf);
+      } else if (loc.tag == TypePathEntryKind.WILDCARD
+          && leaf.getKind() == Tree.Kind.UNBOUNDED_WILDCARD) {
+        // Check if the leaf is an unbounded wildcard instead of the parent, since unbounded
+        // wildcard has no members so it can't be the parent of anything.
+        if (locationRemaining.size() == 0) {
+          return false;
+        }
+
+        // The following check is necessary because Oracle has decided that
+        //   x instanceof Class<? extends Object>
+        // will remain illegal even though it means the same thing as
+        //   x instanceof Class<?>.
+        TreePath gpath = parentPath.getParentPath();
+        if (gpath != null) {  // TODO: skip over existing annotations?
+          Tree gparent = gpath.getLeaf();
+          if (gparent.getKind() == Tree.Kind.INSTANCE_OF) {
+            TreeFinder.warn.debug("WARNING: wildcard bounds not allowed "
+                + "in 'instanceof' expression; skipping insertion%n");
+            return false;
+          } else if (gparent.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+            gpath = gpath.getParentPath();
+            if (gpath != null
+                && gpath.getLeaf().getKind() == Tree.Kind.ARRAY_TYPE) {
+              TreeFinder.warn.debug("WARNING: wildcard bounds not allowed "
+                  + "in generic array type; skipping insertion%n");
+              return false;
+            }
+          }
+        }
+        locationRemaining.remove(locationRemaining.size() - 1);
+      } else if (parent.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+        if (loc.tag != TypePathEntryKind.TYPE_ARGUMENT) {
+          return false;
+        }
+
+        // Find the outermost type in the AST; if it has parameters,
+        // pop the stack once for each inner type on the end of the type
+        // path and check the parameter.
+        Tree inner = ((ParameterizedTypeTree) parent).getType();
+        int i = locationRemaining.size() - 1;  // last valid type path index
+        locationRemaining.remove(i--);
+        while (inner.getKind() == Tree.Kind.MEMBER_SELECT
+            && !isStatic((JCFieldAccess) inner)) {
+          // fieldAccess.type != null && fieldAccess.type.getKind() == TypeKind.DECLARED
+          // && fieldAccess.type.tsym.isStatic()
+          // TODO: check whether MEMBER_SELECT indicates inner or qualifier?
+          if (i < 0) { break; }
+          if (locationRemaining.get(i).tag != TypePathEntryKind.INNER_TYPE) {
+            return false;
+          }
+          locationRemaining.remove(i--);
+          inner = ((MemberSelectTree) inner).getExpression();
+          if (inner.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+            inner = ((AnnotatedTypeTree) inner).getUnderlyingType();
+          }
+          if (inner.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+            inner = ((ParameterizedTypeTree) inner).getType();
+          }
+        }
+        if (i >= 0 && locationRemaining.get(i).tag ==
+            TypePathEntryKind.INNER_TYPE) {
+          return false;
+        }
+
+        // annotating List<@A Integer>
+        // System.out.printf("parent instanceof ParameterizedTypeTree: %s loc=%d%n",
+        //                   Main.treeToString(parent), loc);
+        List<? extends Tree> childTrees =
+            ((ParameterizedTypeTree) parent).getTypeArguments();
+        boolean found = false;
+        if (childTrees.size() > loc.arg) {
+          Tree childi = childTrees.get(loc.arg);
+          if (childi.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+            childi = ((AnnotatedTypeTree) childi).getUnderlyingType();
+          }
+          if (childi == leaf) {
+            for (TreePath outerPath = parentPath.getParentPath();
+                outerPath.getLeaf().getKind() == Tree.Kind.MEMBER_SELECT
+                    && !isStatic((JCFieldAccess) outerPath.getLeaf());
+                outerPath = outerPath.getParentPath()) {
+              outerPath = outerPath.getParentPath();
+              if (outerPath.getLeaf().getKind() == Tree.Kind.ANNOTATED_TYPE) {
+                outerPath = outerPath.getParentPath();
+              }
+              if (outerPath.getLeaf().getKind() != Tree.Kind.PARAMETERIZED_TYPE) {
+                break;
+              }
+              parentPath = outerPath;
+            }
+            pathRemaining = parentPath;
+            found = true;
+          }
+        }
+        if (!found) {
+          if (debug) {
+            System.out.printf("Generic failed for leaf: %s: nr children: %d loc: %s child: %s%n",
+                             leaf, childTrees.size(), loc,
+                             ((childTrees.size() > loc.arg) ? childTrees.get(loc.arg) : null));
+          }
+          return false;
+        }
+      } else if (parent.getKind() == Tree.Kind.EXTENDS_WILDCARD
+                 || parent.getKind() == Tree.Kind.SUPER_WILDCARD) {
+        if (loc.tag != TypePathEntryKind.WILDCARD || locationRemaining.size() == 1) {
+          // If there's only one location left, this can't be a match since a wildcard
+          // needs to be in another kind of compound type.
+          return false;
+        }
+        locationRemaining.remove(locationRemaining.size() - 1);
+        // annotating List<? extends @A Integer>
+        // System.out.printf("parent instanceof extends WildcardTree: %s loc=%d%n",
+        //                   Main.treeToString(parent), loc);
+        WildcardTree wct = (WildcardTree) parent;
+        Tree boundTree = wct.getBound();
+
+        if (debug) {
+          String wildcardType;
+          if (parent.getKind() == Tree.Kind.EXTENDS_WILDCARD) {
+            wildcardType = "ExtendsWildcard";
+          } else {
+            wildcardType = "SuperWildcard";
+          }
+          System.out.printf("%s with bound %s gives %s%n", wildcardType, boundTree, boundTree.equals(leaf));
+        }
+
+        if (boundTree.equals(leaf)) {
+          if (locationRemaining.isEmpty()) {
+            return true;
+          } else {
+            pathRemaining = parentPath;
+          }
+        } else {
+          return false;
+        }
+      } else if (parent.getKind() == Tree.Kind.ARRAY_TYPE) {
+        if (loc.tag != TypePathEntryKind.ARRAY) {
+          return false;
+        }
+        locationRemaining.remove(locationRemaining.size() - 1);
+        // annotating Integer @A []
+        parentPath = TreeFinder.largestContainingArray(parentPath);
+        parent = parentPath.getLeaf();
+        // System.out.printf("parent instanceof ArrayTypeTree: %s loc=%d%n",
+        //                   parent, loc);
+        Tree elt = ((ArrayTypeTree) parent).getType();
+        while (locationRemaining.size() > 0
+                && locationRemaining.get(locationRemaining.size() - 1).tag == TypePathEntryKind.ARRAY) {
+          if (elt.getKind() != Tree.Kind.ARRAY_TYPE) { // ArrayTypeTree
+            if (debug) {
+              System.out.printf("Element: %s is not an ArrayTypeTree and therefore false.\n", elt);
+            }
+            return false;
+          }
+          elt = ((ArrayTypeTree) elt).getType();
+          locationRemaining.remove(locationRemaining.size() - 1);
+        }
+
+        boolean b = elt.equals(leaf);
+        if (debug) {
+          System.out.printf("parent %s instanceof ArrayTypeTree: %b %s %s %s%n",
+                           parent, elt.equals(leaf), elt, leaf, loc);
+          System.out.printf("b=%s elt=%s leaf=%s%n", b, elt, leaf);
+        }
+
+        // TODO:  The parent criterion should be exact, not just "in".
+        // Otherwise the criterion [1]  matches  [5 4 3 2 1].
+        // This is a disadvantage of working from the inside out instead of the outside in.
+        if (b) {
+          pathRemaining = parentPath;
+        } else {
+          return false;
+        }
+      } else if (parent.getKind() == Tree.Kind.NEW_ARRAY) {
+        if (loc.tag != TypePathEntryKind.ARRAY) {
+          return false;
+        }
+        if (debug) {
+          System.out.println("Parent is a NEW_ARRAY and always gives true.");
+        }
+        return true;
+      } else {
+        if (debug) {
+          System.out.printf("unrecognized parent kind = %s%n", parent.getKind());
+        }
+        return false;
+      }
+    }
+
+    // no (remaining) inner type location, want to annotate outermost type
+    // e.g.,  @Nullable List list;
+    //        @Nullable List<String> list;
+    TreePath parentPath = pathRemaining.getParentPath();
+    if (parentPath == null) {
+      if (debug) {
+        System.out.println("Parent path is null and therefore false.");
+      }
+      return false;
+    }
+    Tree parent = pathRemaining.getParentPath().getLeaf();
+    if (debug) {
+      leaf = pathRemaining.getLeaf();
+      System.out.printf("No (remaining) inner type location:%n  leaf: %s %b%n  parent: %s %b%n  result: %s%n",
+            Main.treeToString(leaf), isGenericOrArray(leaf),
+            Main.treeToString(parent), isGenericOrArray(parent),
+            ! isGenericOrArray(parent));
+    }
+
+    return ! isGenericOrArray(parent);
+  }
+
+  /**
+   * @param fieldAccess
+   * @return
+   */
+  private boolean isStatic(JCFieldAccess fieldAccess) {
+    return fieldAccess.type != null
+        && fieldAccess.type.getKind() == TypeKind.DECLARED
+        && fieldAccess.type.tsym.isStatic();
+  }
+
+  private boolean isGenericOrArray(Tree t) {
+    return ((t.getKind() == Tree.Kind.PARAMETERIZED_TYPE)
+            || (t.getKind() == Tree.Kind.ARRAY_TYPE)
+            || (t.getKind() == Tree.Kind.EXTENDS_WILDCARD)
+            || (t.getKind() == Tree.Kind.SUPER_WILDCARD)
+            || (t.getKind() == Tree.Kind.ANNOTATED_TYPE &&
+                    isGenericOrArray(((AnnotatedTypeTree)t).getUnderlyingType()))
+            // Monolithic:  one node for entire "new".  So, handle specially.
+            // || (t.getKind() == Tree.Kind.NEW_ARRAY)
+            );
+  }
+
+  @Override
+  public Kind getKind() {
+    return Criterion.Kind.GENERIC_ARRAY_LOCATION;
+  }
+
+  @Override
+  public String toString() {
+    return "GenericArrayLocationCriterion at " +
+    ((location.isEmpty())
+     ? "outermost type"
+     : ("( " + location.toString() + " )"));
+  }
+
+  /**
+   * Gets the type path location of this criterion.
+   *
+   * @return an unmodifiable list of {@link TypePathEntry}s
+   */
+  public List<TypePathEntry> getLocation() {
+    return Collections.unmodifiableList(location);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/InClassCriterion.java b/annotation-file-utilities/src/annotator/find/InClassCriterion.java
new file mode 100644
index 0000000..ee418bf
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/InClassCriterion.java
@@ -0,0 +1,280 @@
+package annotator.find;
+
+import java.util.*;
+import java.util.regex.*;
+
+import javax.lang.model.element.Name;
+
+import annotator.scanner.AnonymousClassScanner;
+import annotator.scanner.LocalClassScanner;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+// If there are dollar signs in a name, then there are two
+// possibilities regarding how the dollar sign got there.
+//  1. Inserted by the compiler, for inner classes.
+//  2. Written by the programmer (or by a tool that creates .class files).
+// We need to account for both possibilities (and all combinations of them).
+
+// Example names
+//   annotator.tests.FullClassName
+//   annotator.tests.FullClassName$InnerClass
+//   annotator.tests.FullClassName$0
+
+/**
+ * Represents the criterion that a program element is in a class with a
+ * particular name.
+ */
+public final class InClassCriterion implements Criterion {
+
+  static boolean debug = false;
+
+  public final String className;
+  private final boolean exactMatch;
+
+  /** The argument is a fully-qualified class name. */
+  public InClassCriterion(String className, boolean exactMatch) {
+    this.className = className;
+    this.exactMatch = exactMatch;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Kind getKind() {
+    return Kind.IN_CLASS;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    return InClassCriterion.isSatisfiedBy(path, className, exactMatch);
+  }
+
+  static Pattern anonclassPattern;
+  static Pattern localClassPattern;
+  static {
+    // for JDK 7: anonclassPattern = Pattern.compile("^(?<num>[0-9]+)(\\$(?<remaining>.*))?$");
+    anonclassPattern = Pattern.compile("^([0-9]+)(\\$(.*))?$");
+    localClassPattern = Pattern.compile("^([0-9]+)([^$]+)(\\$(.*))?$");
+  }
+
+  public static boolean isSatisfiedBy(TreePath path, String className, boolean exactMatch) {
+    if (path == null) {
+      return false;
+    }
+
+    // However much of the class name remains to match.
+    String cname = className;
+
+    // It is wrong to work from the leaf up to the root of the tree, which
+    // would fail if the criterion is a.b.c and the actual is a.b.c.c.
+    List<Tree> trees = new ArrayList<Tree>();
+    for (Tree tree : path) {
+      trees.add(tree);
+    }
+    Collections.reverse(trees);
+
+    boolean insideMatch = false;
+    for (int i = 0; i < trees.size(); i++) {
+      Tree tree = trees.get(i);
+      boolean checkAnon = false;
+      boolean checkLocal = false;
+
+      switch (tree.getKind()) {
+      case COMPILATION_UNIT:
+        debug("InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+        ExpressionTree packageTree = ((CompilationUnitTree) tree).getPackageName();
+        if (packageTree == null) {
+          // compilation unit is in default package; nothing to do
+        } else {
+          String declaredPackage = packageTree.toString();
+          if (cname.startsWith(declaredPackage + ".")) {
+            cname = cname.substring(declaredPackage.length()+1);
+          } else {
+            debug("false[COMPILATION_UNIT; bad declaredPackage = %s] InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", declaredPackage, cname, tree);
+            return false;
+          }
+        }
+        break;
+      case CLASS:
+      case INTERFACE:
+      case ENUM:
+      case ANNOTATION_TYPE:
+        if (i > 0 && trees.get(i - 1).getKind() == Tree.Kind.NEW_CLASS) {
+          // For an anonymous class, the CLASS tree is always directly inside of
+          // a NEW_CLASS tree. If that's the case here then skip this iteration
+          // since we've already looked at the new class tree in the previous
+          // iteration.
+          break;
+        }
+        debug("InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+
+        if (i > 0 && trees.get(i - 1).getKind() == Tree.Kind.BLOCK) {
+          // Section 14.3 of the JLS says "every local class declaration
+          // statement is immediately contained by a block".
+          checkLocal = true;
+          debug("found local class: InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+          break;
+        }
+
+        // all four Kinds are represented by ClassTree
+        ClassTree c = (ClassTree)tree;
+        Name csn = c.getSimpleName();
+
+        if (csn == null || csn.length() == 0) {
+          debug("empty getSimpleName: InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+          checkAnon = true;
+          break;
+        }
+        String treeClassName = csn.toString();
+        if (cname.equals(treeClassName)) {
+          if (exactMatch) {
+            cname = "";
+          } else {
+            debug("true InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+            return true;
+          }
+        } else if (cname.startsWith(treeClassName + "$")
+                   || (cname.startsWith(treeClassName + "."))) {
+          cname = cname.substring(treeClassName.length()+1);
+        } else if (!treeClassName.isEmpty()) {
+          // treeClassName is empty for anonymous inner class
+          // System.out.println("cname else: " + cname);
+          debug("false InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+          return false;
+        }
+        break;
+      case NEW_CLASS:
+        // When matching the "new Class() { ... }" expression itself, we
+        // should not use the anonymous class name.  But when matching
+        // within the braces, we should.
+        debug("InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+        if (cname.equals("")) {
+          insideMatch = true;
+        } else {
+          NewClassTree nc = (NewClassTree) tree;
+          checkAnon = nc.getClassBody() != null;
+        }
+        break;
+      case METHOD:
+      case VARIABLE:
+        // Avoid searching inside inner classes of the matching class,
+        // lest a homographic inner class lead to a spurious match.
+        if (insideMatch) {
+          debug("false InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+          return false;
+        }
+        break;
+      default:
+        // nothing to do
+        break;
+      }
+
+      if (checkAnon) {
+        // If block is anonymous class, and cname starts with an
+        // anonymous class index, see if they match.
+
+        Matcher anonclassMatcher = anonclassPattern.matcher(cname);
+        if (! anonclassMatcher.matches()) {
+          debug("false[anonclassMatcher] InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+          return false;
+        }
+        // for JDK 7: String anonclassNumString = anonclassMatcher.group("num");
+        // for JDK 7: cname = anonclassMatcher.group("remaining");
+        String anonclassNumString = anonclassMatcher.group(1);
+        cname = anonclassMatcher.group(3);
+        if (cname == null) {
+          cname = "";
+        }
+        int anonclassNum;
+        try {
+          anonclassNum = Integer.parseInt(anonclassNumString);
+        } catch (NumberFormatException e) {
+          throw new Error("This can't happen: " + cname + "$" + anonclassNumString, e);
+        }
+
+        int actualIndexInSource = AnonymousClassScanner.indexOfClassTree(path, tree);
+
+        if (anonclassNum != actualIndexInSource) {
+          debug("false[anonclassNum %d %d] InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", anonclassNum, actualIndexInSource, cname, tree);
+          return false;
+        }
+      } else if (checkLocal) {
+        ClassTree c = (ClassTree) tree;
+        String treeClassName = c.getSimpleName().toString();
+
+        Matcher localClassMatcher = localClassPattern.matcher(cname);
+        if (!localClassMatcher.matches()) {
+          debug("false[localClassMatcher] InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname, tree);
+          return false;
+        }
+        String localClassNumString = localClassMatcher.group(1);
+        String localClassName = localClassMatcher.group(2);
+        int localClassNum = Integer.parseInt(localClassNumString);
+
+        int actualIndexInSource = LocalClassScanner.indexOfClassTree(path, c);
+
+        if (actualIndexInSource == localClassNum && treeClassName.startsWith(localClassName)) {
+          cname = localClassMatcher.group(4);
+          if (cname == null) {
+            cname = "";
+          }
+        } else {
+          debug("false[localClassNum %d %d] InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", localClassNum, actualIndexInSource, cname, tree);
+          return false;
+        }
+      }
+    }
+
+    debug("%s InClassCriterion.isSatisfiedBy:%n  cname=%s%n  tree=%s%n", cname.equals(""), cname, path.getLeaf());
+    return cname.equals("");
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return "In class '" + className + "'" + (exactMatch ? " (exactly)" : "");
+  }
+
+  /**
+   * Return an array of Strings representing the characters between
+   * successive instances of the delimiter character.
+   * Always returns an array of length at least 1 (it might contain only the
+   * empty string).
+   * @see #split(String s, String delim)
+   */
+  /*
+  private static List<String> split(String s, char delim) {
+    List<String> result = new ArrayList<String>();
+    for (int delimpos = s.indexOf(delim); delimpos != -1; delimpos = s.indexOf(delim)) {
+      result.add(s.substring(0, delimpos));
+      s = s.substring(delimpos+1);
+    }
+    result.add(s);
+    return result;
+  }
+  */
+
+  private static void debug(String message, Object... args) {
+    if (debug) {
+      System.out.printf(message, args);
+    }
+  }
+
+}
diff --git a/annotation-file-utilities/src/annotator/find/InFieldInitCriterion.java b/annotation-file-utilities/src/annotator/find/InFieldInitCriterion.java
new file mode 100644
index 0000000..246baba
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/InFieldInitCriterion.java
@@ -0,0 +1,49 @@
+package annotator.find;
+
+import annotator.scanner.CommonScanner;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * Criterion for being within a specific field initializer.
+ */
+public class InFieldInitCriterion implements Criterion {
+
+  public final String varName;
+  public final Criterion varCriterion;
+
+  public InFieldInitCriterion(String varName) {
+    this.varName = varName;
+    this.varCriterion = Criteria.is(Tree.Kind.VARIABLE, varName);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    while (path != null) {
+      if (CommonScanner.isFieldInit(path)) {
+        return varCriterion.isSatisfiedBy(path);
+      }
+      path = path.getParentPath();
+    }
+    return false;
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.IN_FIELD_INIT;
+  }
+
+  @Override
+  public String toString() {
+    return "In field initializer for field '" + varName + "'";
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/InInitBlockCriterion.java b/annotation-file-utilities/src/annotator/find/InInitBlockCriterion.java
new file mode 100644
index 0000000..51f0677
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/InInitBlockCriterion.java
@@ -0,0 +1,55 @@
+package annotator.find;
+
+import annotator.scanner.CommonScanner;
+import annotator.scanner.InitBlockScanner;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * Criterion for being within a specific initializer.
+ *
+ * @author dbro
+ */
+public class InInitBlockCriterion implements Criterion {
+  public final int blockID;
+  public final boolean isStatic;
+  public final Criterion notInMethodCriterion;
+
+  public InInitBlockCriterion(int blockID, boolean isStatic) {
+    this.blockID = blockID;
+    this.isStatic = isStatic;
+    this.notInMethodCriterion = Criteria.notInMethod();
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    while (path != null) {
+      if (CommonScanner.isInitBlock(path, isStatic)) {
+        int indexInSource = InitBlockScanner.indexOfInitTree(path, isStatic);
+        return indexInSource == blockID;
+      }
+      path = path.getParentPath();
+    }
+    return false;
+  }
+
+  @Override
+  public Kind getKind() {
+    return isStatic ? Kind.IN_STATIC_INIT : Kind.IN_INSTANCE_INIT;
+  }
+
+  @Override
+  public String toString() {
+    return "In " + (isStatic ? "static" : "instance")
+        + " initializer with index " + blockID;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/InMethodCriterion.java b/annotation-file-utilities/src/annotator/find/InMethodCriterion.java
new file mode 100644
index 0000000..d70c7f5
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/InMethodCriterion.java
@@ -0,0 +1,73 @@
+package annotator.find;
+
+import javax.lang.model.element.Modifier;
+
+import annotator.Main;
+
+import com.sun.source.tree.*;
+import com.sun.source.util.TreePath;
+
+/**
+ * Represents the criterion that a program element is in a method with a
+ * certain name.
+ */
+final class InMethodCriterion implements Criterion {
+
+  public final String name;
+  private final IsSigMethodCriterion sigMethodCriterion;
+
+  InMethodCriterion(String name) {
+    this.name = name;
+    sigMethodCriterion = new IsSigMethodCriterion(name);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Kind getKind() {
+    return Kind.IN_METHOD;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    Criteria.dbug.debug("InMethodCriterion.isSatisfiedBy(%s); this=%s%n",
+        Main.pathToString(path), this.toString());
+    boolean staticDecl = false;
+    boolean result = false;
+
+    do {
+      if (path.getLeaf().getKind() == Tree.Kind.METHOD) {
+        boolean b = sigMethodCriterion.isSatisfiedBy(path);
+        Criteria.dbug.debug("%s%n", "InMethodCriterion.isSatisfiedBy => b");
+        return b;
+      }
+      if (path.getLeaf().getKind() == Tree.Kind.VARIABLE) {
+        ModifiersTree mods = ((VariableTree) path.getLeaf()).getModifiers();
+        staticDecl = mods.getFlags().contains(Modifier.STATIC);
+      }
+      path = path.getParentPath();
+    } while (path != null && path.getLeaf() != null);
+
+    result = (staticDecl ? "<clinit>()V" : "<init>()V").equals(name);
+
+    Criteria.dbug.debug("InMethodCriterion.isSatisfiedBy => %s%n", result);
+    return result;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return "in method '" + name + "'";
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/InPackageCriterion.java b/annotation-file-utilities/src/annotator/find/InPackageCriterion.java
new file mode 100644
index 0000000..5ab88d9
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/InPackageCriterion.java
@@ -0,0 +1,71 @@
+package annotator.find;
+
+import annotator.Main;
+
+import com.sun.source.tree.*;
+import com.sun.source.util.TreePath;
+
+/**
+ * Represents the criterion that a program element is in a package with a
+ * certain name.
+ */
+final class InPackageCriterion implements Criterion {
+
+  private final String name;
+
+  InPackageCriterion(String name) {
+    this.name = name;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Kind getKind() {
+    return Kind.IN_PACKAGE;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Criteria.dbug.debug("InPackageCriterion.isSatisfiedBy(%s); this=%s",
+        Main.pathToString(path), this.toString());
+
+    do {
+      Tree tree = path.getLeaf();
+      if (tree.getKind() == Tree.Kind.COMPILATION_UNIT) {
+        CompilationUnitTree cu = (CompilationUnitTree)tree;
+        ExpressionTree pn = cu.getPackageName();
+        if (pn == null) {
+          return name == null || name.equals("");
+        } else {
+          String packageName = pn.toString();
+          return name != null && (name.equals(packageName));
+        }
+      }
+      path = path.getParentPath();
+    } while (path != null && path.getLeaf() != null);
+
+    Criteria.dbug.debug("InPackageCriterion.isSatisfiedBy => false");
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return "in package '" + name + "'";
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/Insertion.java b/annotation-file-utilities/src/annotator/find/Insertion.java
new file mode 100644
index 0000000..68a1b2f
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/Insertion.java
@@ -0,0 +1,554 @@
+package annotator.find;
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import annotations.io.ASTPath;
+
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+import plume.Pair;
+import type.ArrayType;
+import type.DeclaredType;
+import type.Type;
+import type.BoundedType;
+
+/**
+ * Specifies something that needs to be inserted into a source file, including
+ * the "what" and the "where".
+ */
+public abstract class Insertion {
+
+    public enum Kind {
+        ANNOTATION,
+        CAST,
+        CONSTRUCTOR,
+        METHOD,
+        NEW,
+        RECEIVER,
+        CLOSE_PARENTHESIS
+    }
+
+    private final Criteria criteria;
+    // If non-null, then try to put annotation on its own line,
+    // horizontally aligned with the location.
+    private final boolean separateLine;
+
+    /**
+     * Whether this insertion has already been inserted into source code.
+     */
+    private boolean inserted;
+
+    /**
+     * The package names for the annotations being inserted by this Insertion.
+     * This will be empty unless {@link #getText(boolean, boolean)} is called
+     * with abbreviate true.
+     */
+    protected Set<String> packageNames;
+
+    /**
+     * Set of annotation names that should always be qualified, even
+     *  when {@link getText(boolean, boolean)} is called with abbreviate true.
+     */
+    protected static Set<String> alwaysQualify = new LinkedHashSet<String>();
+
+    /**
+     * Creates a new insertion.
+     *
+     * @param criteria where to insert the text
+     * @param separateLine whether to insert the text on its own
+     */
+    public Insertion(Criteria criteria, boolean separateLine) {
+        this.criteria = criteria;
+        this.separateLine = separateLine;
+        this.packageNames = new LinkedHashSet<String>();
+        this.inserted = false;
+    }
+
+    /**
+     * Gets the insertion criteria.
+     *
+     * @return the criteria
+     */
+    public Criteria getCriteria() {
+        return criteria;
+    }
+
+    /**
+     * Gets the insertion text (not commented or abbreviated, and without added
+     * leading or trailing whitespace).
+     *
+     * @return the text to insert
+     */
+    public String getText() {
+        return getText(false, false, true, 0, '\0');
+    }
+
+    /**
+     * Gets the insertion text with a leading and/or trailing space added based
+     * on the values of the {@code gotSeparateLine}, {@code pos}, and
+     * {@code precedingChar} parameters.
+     *
+     * @param comments
+     *            if true, Java 8 features will be surrounded in comments
+     * @param abbreviate
+     *            if true, the package name will be removed from the annotations.
+     *            The package name can be retrieved again by calling the
+     *            {@link #getPackageNames()} method.
+     * @param gotSeparateLine
+     *            {@code true} if this insertion is actually added on a separate
+     *            line.
+     * @param pos
+     *            the source position where this insertion will be inserted
+     * @param precedingChar
+     *            the character directly preceding where this insertion will be
+     *            inserted. This value will be ignored if {@code pos} is 0.
+     *
+     * @return the text to insert
+     */
+    public String getText(boolean comments, boolean abbreviate,
+            boolean gotSeparateLine, int pos, char precedingChar) {
+        String toInsert = getText(comments, abbreviate);
+        if (!toInsert.isEmpty()) {
+            if (addLeadingSpace(gotSeparateLine, pos, precedingChar)) {
+                toInsert = " " + toInsert;
+            }
+            if (addTrailingSpace(gotSeparateLine)) {
+                toInsert = toInsert + " ";
+            }
+        }
+        return toInsert;
+    }
+
+    /**
+     * Gets the insertion text.
+     *
+     * @param comments
+     *            if true, Java 8 features will be surrounded in comments
+     * @param abbreviate
+     *            if true, the package name will be removed from the annotations.
+     *            The package name can be retrieved again by calling the
+     *            {@link #getPackageNames()} method.
+     * @return the text to insert
+     */
+    protected abstract String getText(boolean comments, boolean abbreviate);
+
+    /**
+     * Indicates if a preceding space should be added to this insertion.
+     * Subclasses may override this method for custom leading space rules.
+     *
+     * @param gotSeparateLine
+     *            {@code true} if this insertion is actually added on a separate
+     *            line.
+     * @param pos
+     *            the source position where this insertion will be inserted
+     * @param precedingChar
+     *            the character directly preceding where this insertion will be
+     *            inserted. This value will be ignored if {@code pos} is 0.
+     * @return {@code true} if a leading space should be added, {@code false}
+     *         otherwise.
+     */
+    protected boolean addLeadingSpace(boolean gotSeparateLine, int pos,
+            char precedingChar) {
+        // Don't add a preceding space if this insertion is on its own line,
+        // it's at the beginning of the file, the preceding character is already
+        // whitespace, or the preceding character is the first formal or generic
+        // parameter.
+        return !gotSeparateLine && pos != 0
+                && !Character.isWhitespace(precedingChar)
+                && precedingChar != '(' && precedingChar != '<';
+    }
+
+    /**
+     * Indicates if a trailing space should be added to this insertion.
+     * Subclasses may override this method for custom trailing space rules.
+     *
+     * @param gotSeparateLine
+     *            {@code true} if this insertion is actually added on a separate
+     *            line.
+     * @return {@code} true if a trailing space should be added, {@code false}
+     * otherwise.
+     */
+    protected boolean addTrailingSpace(boolean gotSeparateLine) {
+        // Don't added a trailing space if this insertion is on its own line.
+        return !gotSeparateLine;
+    }
+
+    /**
+     * Gets the package name.
+     *
+     * @return the package name of the annotation being inserted by this
+     *         Insertion. This will be empty unless
+     *         {@link #getText(boolean, boolean)} is called with abbreviate true.
+     */
+    public Set<String> getPackageNames() {
+        return packageNames;
+    }
+
+    /**
+     * Gets the set of annotation names that should always be qualified.
+     */
+    public static Set<String> getAlwaysQualify() {
+        return alwaysQualify;
+    }
+
+    /**
+     * Sets the set of annotation names that should always be qualified.
+     */
+    public static void setAlwaysQualify(Set<String> set) {
+        alwaysQualify = set;
+    }
+
+    /**
+     * Gets whether the insertion goes on a separate line.
+     *
+     * @return whether the insertion goes on a separate line
+     */
+    public boolean getSeparateLine() {
+        return separateLine;
+    }
+
+    /**
+     * Gets whether this insertion has already been inserted into source code.
+     * @return {@code true} if this insertion has already been inserted,
+     *         {@code false} otherwise.
+     */
+    public boolean getInserted() {
+        return inserted;
+    }
+
+    /**
+     * Sets whether this insertion has already been inserted into source code.
+     * @param inserted {@code true} if this insertion has already been inserted,
+     *         {@code false} otherwise.
+     */
+    public void setInserted(boolean inserted) {
+        this.inserted = inserted;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return String.format("(nl=%b) @ %s", separateLine, criteria);
+    }
+
+    /**
+     * Gets the kind of this insertion.
+     */
+    public abstract Kind getKind();
+
+    /**
+     * Removes the leading package.
+     *
+     * @return given <code>@com.foo.bar(baz)</code> it returns the pair
+     *         <code>{ com.foo, @bar(baz) }</code>.
+     */
+    public static Pair<String, String> removePackage(String s) {
+        int nameEnd = s.indexOf("(");
+        if (nameEnd == -1) {
+            nameEnd = s.length();
+        }
+        int dotIndex = s.lastIndexOf(".", nameEnd);
+        if (dotIndex != -1) {
+            String basename = s.substring(dotIndex + 1);
+            if (!alwaysQualify.contains(basename)) {
+                String packageName = s.substring(0, nameEnd);
+                if (packageName.startsWith("@")) {
+                    return Pair.of(packageName.substring(1),
+                            "@" + basename);
+                } else {
+                    return Pair.of(packageName, basename);
+                }
+            }
+        }
+        return Pair.of((String) null, s);
+    }
+
+    /**
+     * Converts the given type to a String. This method can't be in the
+     * {@link Type} class because this method relies on the {@link Insertion}
+     * class to format annotations, and the {@link Insertion} class is not
+     * available from {@link Type}.
+     *
+     * @param type
+     *            the type to convert
+     * @param comments
+     *            if true, Java 8 features will be surrounded in comments
+     * @param abbreviate
+     *            if true, the package name will be removed from the annotations.
+     *            The package name can be retrieved again by calling the
+     *            {@link #getPackageNames()} method.
+     * @return the type as a string
+     */
+    public String typeToString(Type type, boolean comments, boolean abbreviate) {
+        StringBuilder result = new StringBuilder();
+
+        switch (type.getKind()) {
+        case DECLARED:
+            DeclaredType declaredType = (DeclaredType) type;
+            String typeName = declaredType.getName();
+            int sep = typeName.lastIndexOf('.') + 1;
+            if (abbreviate) {
+                typeName = typeName.substring(sep);
+            } else if (sep > 0) {
+                result.append(typeName.substring(0, sep));
+                typeName = typeName.substring(sep);
+            }
+            writeAnnotations(type, result, comments, abbreviate);
+            result.append(typeName);
+            if (!declaredType.isWildcard()) {
+                List<Type> typeArguments = declaredType.getTypeParameters();
+                if (!typeArguments.isEmpty()) {
+                    result.append('<');
+                    result.append(typeToString(typeArguments.get(0), comments, abbreviate));
+                    for (int i = 1; i < typeArguments.size(); i++) {
+                        result.append(", ");
+                        result.append(typeToString(typeArguments.get(i), comments, abbreviate));
+                    }
+                    result.append('>');
+                }
+                Type innerType = declaredType.getInnerType();
+                if (innerType != null) {
+                    result.append('.');
+                    result.append(typeToString(innerType, comments, abbreviate));
+                }
+            }
+            break;
+        case ARRAY:
+            ArrayType arrayType = (ArrayType) type;
+            result.append(typeToString(arrayType.getComponentType(), comments, abbreviate));
+            if (!arrayType.getAnnotations().isEmpty()) {
+                result.append(' ');
+            }
+            writeAnnotations(type, result, comments, abbreviate);
+            result.append("[]");
+            break;
+        case BOUNDED:
+            BoundedType boundedType = (BoundedType) type;
+            result.append(typeToString(boundedType.getName(), comments, abbreviate));
+            result.append(' ');
+            result.append(boundedType.getBoundKind());
+            result.append(' ');
+            result.append(typeToString(boundedType.getBound(), comments, abbreviate));
+            break;
+        default:
+            throw new RuntimeException("Illegal kind: " + type.getKind());
+        }
+        // There will be extra whitespace at the end if this is only annotations, so trim
+        return result.toString().trim();
+    }
+
+    /**
+     * Writes the annotations on the given type to the given
+     * {@link StringBuilder}.
+     *
+     * @param type
+     *            contains the annotations to write. Only the annotations
+     *            directly on the type will be written. Subtypes will be
+     *            ignored.
+     * @param result
+     *            where to write the annotations
+     * @param comments
+     *            if {@code true}, Java 8 features will be surrounded in
+     *            comments.
+     * @param abbreviate
+     *            if {@code true}, the package name will be removed from the
+     *            annotations. The package name can be retrieved again by
+     *            calling the {@link #getPackageNames()} method.
+     */
+    private void writeAnnotations(Type type, StringBuilder result,
+            boolean comments, boolean abbreviate) {
+        for (String annotation : type.getAnnotations()) {
+            AnnotationInsertion ins = new AnnotationInsertion(annotation);
+            result.append(ins.getText(comments, abbreviate));
+            result.append(" ");
+            if (abbreviate) {
+                packageNames.addAll(ins.getPackageNames());
+            }
+        }
+    }
+
+    /**
+     * Adds each of the given inner type insertions to the correct part of the
+     * type, based on the insertion's type path.
+     *
+     * @param innerTypeInsertions
+     *          the insertions to add to the type. These must be inner type
+     *          insertions, meaning each of the insertions' {@link Criteria}
+     *          must contain a {@link GenericArrayLocationCriterion} and
+     *          {@link GenericArrayLocationCriterion#getLocation()} must return a
+     *          non-empty list.
+     * @param outerType the type to add the insertions to
+     */
+    public static void decorateType(List<Insertion> innerTypeInsertions, final Type outerType) {
+        decorateType(innerTypeInsertions, outerType, null);
+    }
+
+    public static void decorateType(List<Insertion> innerTypeInsertions,
+            final Type outerType, ASTPath outerPath) {
+        for (Insertion innerInsertion : innerTypeInsertions) {
+            // Set each annotation as inserted (even if it doesn't actually get
+            // inserted because of an error) to "disable" the insertion in the global
+            // insertion list.
+            innerInsertion.setInserted(true);
+
+            try {
+                if (innerInsertion.getKind() != Insertion.Kind.ANNOTATION) {
+                    throw new RuntimeException("Expected 'ANNOTATION' insertion kind, got '"
+                            + innerInsertion.getKind() + "'.");
+                }
+                GenericArrayLocationCriterion c = innerInsertion.getCriteria().getGenericArrayLocation();
+                String annos =
+                    ((AnnotationInsertion) innerInsertion).getAnnotation();
+                if (c == null) {
+                    ASTPath astPath = innerInsertion.getCriteria().getASTPath();
+                    if (outerPath != null && astPath != null) {
+                        decorateType(astPath, annos, outerType, outerPath);
+                        continue;
+                    }
+                    throw new RuntimeException("Missing type path.");
+                }
+
+                List<TypePathEntry> location = c.getLocation();
+                Type type = outerType;
+
+                // Use the type path entries to traverse through the type. Throw an
+                // exception and move on to the next inner type insertion if the type
+                // path and actual type don't match up.
+                for (TypePathEntry tpe : location) {
+                      switch (tpe.tag) {
+                      case ARRAY:
+                          if (type.getKind() == Type.Kind.ARRAY) {
+                              type = ((ArrayType) type).getComponentType();
+                          } else {
+                              throw new RuntimeException("Incorrect type path.");
+                          }
+                          break;
+                    case INNER_TYPE:
+                          if (type.getKind() == Type.Kind.DECLARED) {
+                            DeclaredType declaredType = (DeclaredType) type;
+                            if (declaredType.getInnerType() == null) {
+                              throw new RuntimeException("Incorrect type path: "
+                                      + "expected inner type but none exists.");
+                            }
+                            type = declaredType.getInnerType();
+                        } else {
+                            throw new RuntimeException("Incorrect type path.");
+                        }
+                        break;
+                    case WILDCARD:
+                        if (type.getKind() == Type.Kind.BOUNDED) {
+                            BoundedType boundedType = (BoundedType) type;
+                            if (boundedType.getBound() == null) {
+                              throw new RuntimeException("Incorrect type path: "
+                                      + "expected type bound but none exists.");
+                            }
+                            type = boundedType.getBound();
+                        } else {
+                            throw new RuntimeException("Incorrect type path.");
+                        }
+                        break;
+                    case TYPE_ARGUMENT:
+                        if (type.getKind() == Type.Kind.DECLARED) {
+                            DeclaredType declaredType = (DeclaredType) type;
+                            if (0 <= tpe.arg && tpe.arg <
+                                    declaredType.getTypeParameters().size()) {
+                                type = declaredType.getTypeParameter(tpe.arg);
+                            } else {
+                                throw new RuntimeException("Incorrect type argument index: " + tpe.arg);
+                            }
+                        } else {
+                            throw new RuntimeException("Incorrect type path.");
+                        }
+                        break;
+                    default:
+                        throw new RuntimeException("Illegal TypePathEntryKind: " + tpe.tag);
+                    }
+                }
+                if (type.getKind() == Type.Kind.BOUNDED) {
+                    // Annotations aren't allowed directly on the BoundedType, see BoundedType
+                    type = ((BoundedType) type).getName();
+                }
+                type.addAnnotation(annos);
+            } catch (Throwable e) {
+                TreeFinder.reportInsertionError(innerInsertion, e);
+            }
+        }
+    }
+
+    private static void decorateType(ASTPath astPath,
+            String annos, Type type, ASTPath outerPath) {
+        // type.addAnnotation(annos);  // TODO
+        Iterator<ASTPath.ASTEntry> ii = astPath.iterator();
+        Iterator<ASTPath.ASTEntry> oi = outerPath.iterator();
+
+        while (oi.hasNext()) {
+            if (!ii.hasNext() || !oi.next().equals(ii.next())) {
+                throw new RuntimeException("Incorrect AST path.");
+            }
+        }
+
+        while (ii.hasNext()) {
+            ASTPath.ASTEntry entry = ii.next();
+            Tree.Kind kind = entry.getTreeKind();
+            switch (kind) {
+            case ARRAY_TYPE:
+                if (type.getKind() == Type.Kind.ARRAY) {
+                    type = ((ArrayType) type).getComponentType();
+                } else {
+                    throw new RuntimeException("Incorrect type path.");
+                }
+                break;
+            case MEMBER_SELECT:
+                if (type.getKind() == Type.Kind.DECLARED) {
+                    DeclaredType declaredType = (DeclaredType) type;
+                    if (declaredType.getInnerType() == null) {
+                        throw new RuntimeException("Incorrect type path: "
+                            + "expected inner type but none exists.");
+                    }
+                    type = declaredType.getInnerType();
+                } else {
+                    throw new RuntimeException("Incorrect type path.");
+                }
+                break;
+            case PARAMETERIZED_TYPE:
+                if (type.getKind() == Type.Kind.DECLARED) {
+                    int arg = entry.getArgument();
+                    DeclaredType declaredType = (DeclaredType) type;
+                    if (0 <= arg && arg < declaredType.getTypeParameters().size()) {
+                        type = declaredType.getTypeParameter(arg);
+                    } else {
+                        throw new RuntimeException("Incorrect type argument index: " + arg);
+                    }
+                } else {
+                    throw new RuntimeException("Incorrect type path.");
+                }
+                break;
+            case UNBOUNDED_WILDCARD:
+                if (type.getKind() == Type.Kind.BOUNDED) {
+                    BoundedType boundedType = (BoundedType) type;
+                    if (boundedType.getBound() == null) {
+                        throw new RuntimeException("Incorrect type path: "
+                            + "expected type bound but none exists.");
+                    }
+                    type = boundedType.getBound();
+                } else {
+                    throw new RuntimeException("Incorrect type path.");
+                }
+                break;
+            default:
+                throw new RuntimeException("Illegal TreeKind: " + kind);
+            }
+        }
+        if (type.getKind() == Type.Kind.BOUNDED) {
+            // Annotations aren't allowed directly on the BoundedType, see BoundedType
+            type = ((BoundedType) type).getName();
+        }
+        type.addAnnotation(annos);
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/find/Insertions.java b/annotation-file-utilities/src/annotator/find/Insertions.java
new file mode 100644
index 0000000..0f70e2a
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/Insertions.java
@@ -0,0 +1,1700 @@
+package annotator.find;
+
+import java.util.AbstractSet;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import javax.lang.model.element.Name;
+import javax.lang.model.type.TypeKind;
+
+import annotations.el.InnerTypeLocation;
+import annotations.io.ASTIndex;
+import annotations.io.ASTPath;
+import annotations.io.ASTRecord;
+import type.*;
+
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.PrimitiveTypeTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TreeVisitor;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.tree.WildcardTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Kinds;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.util.Pair;
+
+/**
+ * @author dbro
+ *
+ * An indexed collection (though not {@link java.util.Collection}, only
+ * {@link java.lang.Iterable}) of {@link Insertion}s with methods to
+ * select those specified for a given class or for an outer class along
+ * with its local classes.  This class is especially useful when a
+ * single JAIF stores annotations for many source files, as it reduces
+ * the number of insertions to be considered for any AST node.
+ *
+ * The class now serves a second purpose, which should probably be
+ * separated out (according to OO dogma, at least): It attaches
+ * {@link annotations.io.ASTPath}-based inner type {@link Insertion}s to
+ * a {@link TypedInsertion} on the outer type if one exists (see
+ * {@link #organizeTypedInsertions(CompilationUnitTree, String, Collection)}.
+ * Since getting these insertions right depends on this organization,
+ * this class is now essential for correctness, not merely for
+ * performance.
+ */
+public class Insertions implements Iterable<Insertion> {
+  private static int kindLevel(Insertion i) {
+    // ordered so insertion that depends on another gets inserted after other
+    switch (i.getKind()) {
+    case CONSTRUCTOR:
+      return 3;
+    case NEW:
+    case RECEIVER:
+      return 2;
+    case CAST:
+        return 1;
+    // case ANNOTATION:
+    // case CLOSE_PARENTHESIS:
+    default:
+      return 0;
+    }
+  }
+
+  private static final Comparator<Insertion> byASTRecord =
+      new Comparator<Insertion>() {
+        @Override
+        public int compare(Insertion o1, Insertion o2) {
+          Criteria c1 = o1.getCriteria();
+          Criteria c2 = o2.getCriteria();
+          ASTPath p1 = c1.getASTPath();
+          ASTPath p2 = c2.getASTPath();
+          ASTRecord r1 = new ASTRecord(null,
+              c1.getClassName(), c1.getMethodName(), c1.getFieldName(),
+              p1 == null ? ASTPath.empty() : p1);
+          ASTRecord r2 = new ASTRecord(null,
+              c2.getClassName(), c2.getMethodName(), c2.getFieldName(),
+              p2 == null ? ASTPath.empty() : p2);
+          int c = r1.compareTo(r2);
+          if (c == 0) {
+            // c = o1.getKind().compareTo(o2.getKind());
+            c = Integer.compare(kindLevel(o2), kindLevel(o1));  // descending
+            if (c == 0) { c = o1.toString().compareTo(o2.toString()); }
+          }
+          return c;
+        }
+      };
+
+  // store indexes insertions by (qualified) outer class name and inner
+  // class path (if any)
+  private Map<String, Map<String, Set<Insertion>>> store;
+  private int size;
+
+  private Pair<String, String> nameSplit(String name) {
+    int i = name.indexOf('$');  // FIXME: don't split on '$' in source
+    return i < 0
+        ? Pair.of(name, "")
+        : Pair.of(name.substring(0, i), name.substring(i));
+  }
+
+  public Insertions() {
+    store = new HashMap<String, Map<String, Set<Insertion>>>();
+    size = 0;
+  }
+
+  // auxiliary for following two methods
+  private void forClass(CompilationUnitTree cut,
+      String qualifiedClassName, Set<Insertion> result) {
+    Pair<String, String> pair = nameSplit(qualifiedClassName);
+    Map<String, Set<Insertion>> map = store.get(pair.fst);
+    if (map != null) {
+      Set<Insertion> set = new TreeSet<Insertion>(byASTRecord);
+      set.addAll(map.get(pair.snd));
+      if (set != null) {
+        set = organizeTypedInsertions(cut, qualifiedClassName, set);
+        result.addAll(set);
+      }
+    }
+  }
+
+  /**
+   * Selects {@link Insertion}s relevant to a given class.
+   *
+   * @param cut the current compilation unit
+   * @param qualifiedClassName the fully qualified class name
+   * @return {@link java.util.Set} of {@link Insertion}s with an
+   *          {@link InClassCriterion} for the given class
+   */
+  public Set<Insertion> forClass(CompilationUnitTree cut,
+      String qualifiedClassName) {
+    Set<Insertion> set = new LinkedHashSet<Insertion>();
+    forClass(cut, qualifiedClassName, set);
+    return set;
+  }
+
+  /**
+   * Selects {@link Insertion}s relevant to a given outer class and its
+   * local classes.
+   *
+   * @param cut the current compilation unit
+   * @param qualifiedOuterClassName the fully qualified outer class name
+   * @return {@link java.util.Set} of {@link Insertion}s with an
+   *          {@link InClassCriterion} for the given outer class or one
+   *          of its local classes
+   */
+  public Set<Insertion> forOuterClass(CompilationUnitTree cut,
+      String qualifiedOuterClassName) {
+    Map<String, Set<Insertion>> map = store.get(qualifiedOuterClassName);
+    if (map == null || map.isEmpty()) {
+      return Collections.<Insertion>emptySet();
+    } else {
+      Set<Insertion> set = new LinkedHashSet<Insertion>();
+      for (String key : map.keySet()) {
+        String qualifiedClassName = qualifiedOuterClassName + key;
+        forClass(cut, qualifiedClassName, set);
+      }
+      return set;
+    }
+  }
+
+  /**
+   * Add an {@link Insertion} to this collection.
+   */
+  public void add(Insertion ins) {
+    InClassCriterion icc = ins.getCriteria().getInClass();
+    String k1 = "";
+    String k2 = "";
+    Map<String, Set<Insertion>> map;
+    Set<Insertion> set;
+
+    if (icc != null) {
+      Pair<String, String> triple = nameSplit(icc.className);
+      k1 = triple.fst;
+      k2 = triple.snd;
+    }
+    map = store.get(k1);
+    if (map == null) {
+      map = new HashMap<String, Set<Insertion>>();
+      store.put(k1, map);
+    }
+    set = map.get(k2);
+    if (set == null) {
+      set = new LinkedHashSet<Insertion>();
+      map.put(k2, set);
+    }
+
+    size -= set.size();
+    set.add(ins);
+    size += set.size();
+  }
+
+  /**
+   * Add all {@link Insertion}s in the given
+   * {@link java.util.Collection} to this collection.
+   */
+  public void addAll(Collection<? extends Insertion> c) {
+    for (Insertion ins : c) {
+      add(ins);
+    }
+  }
+
+  /**
+   * Returns the number of {@link Insertion}s in this collection.
+   */
+  public int size() {
+    return size;
+  }
+
+  @Override
+  public Iterator<Insertion> iterator() {
+    return new Iterator<Insertion>() {
+      private Iterator<Map<String, Set<Insertion>>> miter =
+          store.values().iterator();
+      private Iterator<Set<Insertion>> siter =
+          Collections.<Set<Insertion>>emptySet().iterator();
+      private Iterator<Insertion> iiter =
+          Collections.<Insertion>emptySet().iterator();
+
+      @Override
+      public boolean hasNext() {
+        if (iiter.hasNext()) { return true; }
+        while (siter.hasNext()) {
+          iiter = siter.next().iterator();
+          if (iiter.hasNext()) { return true; }
+        }
+        while (miter.hasNext()) {
+          siter = miter.next().values().iterator();
+          while (siter.hasNext()) {
+            iiter = siter.next().iterator();
+            if (iiter.hasNext()) { return true; }
+          }
+        }
+        return false;
+      }
+
+      @Override
+      public Insertion next() {
+        if (hasNext()) { return iiter.next(); }
+        throw new NoSuchElementException();
+      }
+
+      @Override
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+
+  /**
+   * Returns a {@link java.util.List} containing all {@link Insertion}s
+   * in this collection.
+   */
+  public List<Insertion> toList() {
+    List<Insertion> list = new ArrayList<Insertion>(size);
+    for (Insertion ins : this) { list.add(ins); }
+    return null;
+  }
+
+  /*
+   * This method detects inner type relationships among ASTPath-based
+   * insertion specifications and organizes the insertions accordingly.
+   * This step is necessary because 1) insertion proceeds from the end to
+   * the beginning of the source and 2) the insertion location does not
+   * always exist prior to the top-level type insertion.
+   */
+  private Set<Insertion> organizeTypedInsertions(CompilationUnitTree cut,
+      String className, Collection<Insertion> insertions) {
+    ASTRecordMap<TypedInsertion> map = new ASTRecordMap<TypedInsertion>();
+    Set<Insertion> organized = new LinkedHashSet<Insertion>();
+    Set<Insertion> unorganized = new LinkedHashSet<Insertion>();
+    List<Insertion> list = new ArrayList<Insertion>();
+
+    // First divide the insertions into three buckets: TypedInsertions
+    // on outer types (map), ASTPath-based insertions on local types
+    // (unorganized -- built as list and then sorted, since building as
+    // a set spuriously removes "duplicates" according to the
+    // comparator), and everything else (organized -- where all
+    // eventually land).
+    for (Insertion ins : insertions) {
+      if (ins.getInserted()) { continue; }
+      Criteria criteria = ins.getCriteria();
+      GenericArrayLocationCriterion galc =
+          criteria.getGenericArrayLocation();
+      ASTPath p = criteria.getASTPath();
+      if (p == null || p.isEmpty()
+          || galc != null && !galc.getLocation().isEmpty()
+          || ins instanceof CastInsertion
+          || ins instanceof CloseParenthesisInsertion) {
+        organized.add(ins);
+      } else {
+        ASTRecord rec = new ASTRecord(cut, criteria.getClassName(),
+            criteria.getMethodName(), criteria.getFieldName(), p);
+        ASTPath.ASTEntry entry = rec.astPath.get(-1);
+        Tree node;
+
+        if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY
+            && entry.childSelectorIs(ASTPath.TYPE)
+            && entry.getArgument() == 0) {
+          ASTPath temp = rec.astPath.getParentPath();
+          node = ASTIndex.getNode(cut, rec.replacePath(temp));
+          node = node instanceof JCTree.JCNewArray
+              ? TypeTree.fromType(((JCTree.JCNewArray) node).type)
+              : null;
+        } else {
+          node = ASTIndex.getNode(cut, rec);
+        }
+
+        if (ins instanceof TypedInsertion) {
+          TypedInsertion tins = map.get(rec);
+          if (ins instanceof NewInsertion) {
+            NewInsertion nins = (NewInsertion) ins;
+            if (entry.getTreeKind() == Tree.Kind.NEW_ARRAY
+                && entry.childSelectorIs(ASTPath.TYPE)) {
+              int a = entry.getArgument();
+              List<TypePathEntry> loc0 = new ArrayList<TypePathEntry>(a);
+              ASTRecord rec0 = null;
+              if (a == 0) {
+                rec0 = rec.replacePath(p.getParentPath());
+                Tree t = ASTIndex.getNode(cut, rec0);
+                if (t == null || t.toString().startsWith("{")) {
+                  rec0 = null;
+                } else {
+                  rec = rec0;
+                  rec0 = rec.extend(Tree.Kind.NEW_ARRAY,
+                      ASTPath.TYPE, 0);
+                }
+              } else if (node != null
+                  && !nins.getInnerTypeInsertions().isEmpty()) {
+                if (node.getKind() == Tree.Kind.IDENTIFIER) {
+                  node = ASTIndex.getNode(cut,
+                      rec.replacePath(p.getParentPath()));
+                }
+                if ((node.getKind() == Tree.Kind.NEW_ARRAY
+                    || node.getKind() == Tree.Kind.ARRAY_TYPE)
+                    && !node.toString().startsWith("{")) {
+                  rec = rec.replacePath(p.getParentPath());
+
+                  Collections.fill(loc0, TypePathEntry.ARRAY);
+                  // irec = rec;
+                  // if (node.getKind() == Tree.Kind.NEW_ARRAY) {
+                  rec0 = rec.extend(Tree.Kind.NEW_ARRAY,
+                      ASTPath.TYPE, 0);
+                  // }
+                }
+              }
+
+              if (rec0 != null) {
+                for (Insertion inner : nins.getInnerTypeInsertions()) {
+                  Criteria icriteria = inner.getCriteria();
+                  GenericArrayLocationCriterion igalc =
+                      icriteria.getGenericArrayLocation();
+                  if (igalc != null) {
+                    ASTRecord rec1;
+                    int b = igalc.getLocation().size();
+                    List<TypePathEntry> loc =
+                        new ArrayList<TypePathEntry>(a + b);
+                    loc.addAll(loc0);
+                    loc.addAll(igalc.getLocation());
+                    rec1 = extendToInnerType(rec0, loc, node);
+                    icriteria.add(new GenericArrayLocationCriterion());
+                    icriteria.add(new ASTPathCriterion(rec1.astPath));
+                    inner.setInserted(false);
+                    organized.add(inner);
+                  }
+                }
+                nins.getInnerTypeInsertions().clear();
+              }
+            }
+          }
+          if (tins == null) {
+            map.put(rec, (TypedInsertion) ins);
+          } else if (tins.getType().equals(((TypedInsertion) ins).getType())) {
+            mergeTypedInsertions(tins, (TypedInsertion) ins);
+          }
+        } else {
+          int d = newArrayInnerTypeDepth(p);
+          if (d > 0) {
+            ASTPath temp = p;
+            while (!temp.isEmpty()
+                && (node == null || node.getKind() != Tree.Kind.NEW_ARRAY)) {
+              // TODO: avoid repeating work of newArrayInnerTypeDepth()
+              temp = temp.getParentPath();
+              node = ASTIndex.getNode(cut, rec.replacePath(temp));
+            }
+            if (node == null) {
+              // TODO: ???
+            }
+            temp = temp.extend(
+                new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0));
+            if (node.toString().startsWith("{")) {
+              TypedInsertion tins = map.get(rec.replacePath(temp));
+              if (tins == null) {
+                // TODO
+              } else {
+                tins.getInnerTypeInsertions().add(ins);
+                ins.setInserted(true);
+              }
+            } else {
+              List<? extends ExpressionTree> dims =
+                  ((NewArrayTree) node).getDimensions();
+              ASTRecord irec = rec.replacePath(p.getParentPath())
+                  .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0);
+              GenericArrayLocationCriterion igalc =
+                  criteria.getGenericArrayLocation();
+              for (int i = 0 ; i < d; i++) {
+                irec = irec.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
+              }
+              if (igalc != null) {
+                List<TypePathEntry> loc = igalc.getLocation();
+                if (!loc.isEmpty()) {
+                  try {
+                    Tree dim = dims.get(d-1);
+                    irec = extendToInnerType(irec, loc, dim);
+                    criteria.add(new ASTPathCriterion(irec.astPath));
+                    criteria.add(new GenericArrayLocationCriterion());
+                  } catch (RuntimeException e) {}
+                }
+              }
+            }
+          }
+          list.add(ins);
+        }
+      }
+    }
+    // if (map.isEmpty()) {
+    //  organized.addAll(unorganized);
+    //  return organized;
+    // }
+    Collections.sort(list, byASTRecord);
+    unorganized.addAll(list);
+
+    // Each Insertion in unorganized gets attached to a TypedInsertion
+    // in map if possible; otherwise, it gets dumped into organized.
+    for (Insertion ins : unorganized) {
+      Criteria criteria = ins.getCriteria();
+      String methodName = criteria.getMethodName();
+      String fieldName = criteria.getFieldName();
+      ASTPath ap1 = criteria.getASTPath();
+      List<TypePathEntry> tpes = new ArrayList<TypePathEntry>();
+      if (ap1 == null) {
+          // || methodName == null && fieldName == null)
+        organized.add(ins);
+        continue;
+      }
+
+      // First find the relevant "top-level" insertion, if any.
+      // ap0: path to top-level type; ap1: path to local type
+      ASTRecord rec;
+      Tree.Kind kind;
+      Deque<ASTPath> astack = new ArrayDeque<ASTPath>(ap1.size());
+      ASTPath ap0 = ap1;
+      do {
+        astack.push(ap0);
+        ap0 = ap0.getParentPath();
+      } while (!ap0.isEmpty());
+      do {
+        ap0 = astack.pop();
+        kind = ap0.get(-1).getTreeKind();
+        rec = new ASTRecord(cut, className, methodName, fieldName, ap0);
+      } while (!(astack.isEmpty() || map.containsKey(rec)));
+
+      TypedInsertion tins = map.get(rec);
+      TreePath path = ASTIndex.getTreePath(cut, rec);
+      Tree node = path == null ? null : path.getLeaf();
+      if (node == null && ap0.isEmpty()) {
+        organized.add(ins);
+        continue;
+      }
+
+      // Try to create a top-level insertion if none exists (e.g., if
+      // there is an insertion for NewArray.type 1 but not for 0).
+      if (tins == null) {
+        GenericArrayLocationCriterion galc =
+            criteria.getGenericArrayLocation();
+        if (node == null) {
+          // TODO: figure out from rec?
+          organized.add(ins);
+          continue;
+        } else {
+          Tree t = path.getLeaf();
+          switch (t.getKind()) {
+          case NEW_ARRAY:
+            int d = 0;
+            ASTPath.ASTEntry e = ap1.get(-1);
+            List<TypePathEntry> loc = null;
+            List<Insertion> inners = new ArrayList<Insertion>();
+            Type type = TypeTree.conv(((JCTree.JCNewArray) t).type);
+            if (e.getTreeKind() == Tree.Kind.NEW_ARRAY) {
+              d += e.getArgument();
+            }
+            if (galc != null) {
+              loc = galc.getLocation();
+              int n = loc.size();
+              while (--n >= 0 && loc.get(n).tag == TypePathEntryKind.ARRAY) {
+                ++d;
+              }
+              loc = n < 0 ? null : loc.subList(0, ++n);
+            }
+            criteria.add(new ASTPathCriterion(
+                rec.astPath.getParentPath().extendNewArray(d)));
+            criteria.add(loc == null || loc.isEmpty()
+                ? new GenericArrayLocationCriterion()
+                : new GenericArrayLocationCriterion(
+                    new InnerTypeLocation(loc)));
+            inners.add(ins);
+            tins = new NewInsertion(type, criteria, inners);
+            tins.setInserted(true);
+            map.put(rec, tins);
+            break;
+          default:
+            break;
+          }
+          path = path.getParentPath();
+        }
+      }
+
+      // The sought node may or may not be found in the tree; if not, it
+      // may need to be created later.  Use whatever part of the path
+      // exists already to distinguish MEMBER_SELECT nodes that indicate
+      // qualifiers from those that indicate local types.  Assume any
+      // MEMBER_SELECTs in the AST path that don't correspond to
+      // existing nodes are part of a type use.
+      if (node == null) {
+        ASTPath ap = ap0;
+        if (!ap.isEmpty()) {
+          do {
+            ap = ap.getParentPath();
+            node = ASTIndex.getNode(cut, rec.replacePath(ap));
+          } while (node == null && !ap.isEmpty());
+        }
+        if (node == null) {
+          organized.add(ins);
+          continue;
+        }
+
+        // find actual type
+        ClassSymbol csym = null;
+        switch (tins.getKind()) {
+        case CONSTRUCTOR:
+          if (node instanceof JCTree.JCMethodDecl) {
+            MethodSymbol msym = ((JCTree.JCMethodDecl) node).sym;
+            csym = (ClassSymbol) msym.owner;
+            node = TypeTree.fromType(csym.type);
+            break;
+          } else if (node instanceof JCTree.JCClassDecl) {
+            csym = ((JCTree.JCClassDecl) node).sym;
+            if (csym.owner instanceof ClassSymbol) {
+              csym = (ClassSymbol) csym.owner;
+              node = TypeTree.fromType(csym.type);
+              break;
+            }
+          }
+          throw new RuntimeException();
+        case NEW:
+          if (node instanceof JCTree.JCNewArray) {
+            if (node.toString().startsWith("{")) {
+              node = TypeTree.fromType(((JCTree.JCNewArray) node).type);
+              break;
+            } else {
+              organized.add(ins);
+              continue;
+            }
+          }
+          throw new RuntimeException();
+        case RECEIVER:
+          if (node instanceof JCTree.JCMethodDecl) {
+            JCTree.JCMethodDecl jmd = (JCTree.JCMethodDecl) node;
+            csym = (ClassSymbol) jmd.sym.owner;
+            if ("<init>".equals(jmd.name.toString())) {
+              csym = (ClassSymbol) csym.owner;
+            }
+          } else if (node instanceof JCTree.JCClassDecl) {
+            csym = ((JCTree.JCClassDecl) node).sym;
+          }
+          if (csym != null) {
+            node = TypeTree.fromType(csym.type);
+            break;
+          }
+          throw new RuntimeException();
+        default:
+          throw new RuntimeException();
+        }
+      }
+
+      /*
+       * Inner types require special consideration due to the
+       * structural differences between an AST that represents a type
+       * (subclass of com.sun.source.Tree) and the type's logical
+       * representation (subclass of type.Type).  The differences are
+       * most prominent in the case of a type with a parameterized
+       * local type.  For example, the AST for A.B.C<D> looks like
+       * this:
+       *
+       *                     ParameterizedType
+       *                    /                 \
+       *               MemberSelect       Identifier
+       *              /            \           |
+       *        MemberSelect      (Name)       D
+       *         /      \           |
+       *  Identifier   (Name)       C
+       *        |        |
+       *        A        B
+       *
+       * (Technically, the Names are not AST nodes but rather
+       * attributes of their parent MemberSelect nodes.)  The logical
+       * representation seems more intuitive:
+       *
+       *       DeclaredType
+       *      /     |      \
+       *    Name  Params  Inner
+       *     |      |       |
+       *     A      -  DeclaredType
+       *              /     |      \
+       *            Name  Params  Inner
+       *             |      |       |
+       *             B      -  DeclaredType
+       *                      /     |      \
+       *                    Name  Params  Inner
+       *                     |      |       |
+       *                     C      D       -
+       *
+       * The opposing "chirality" of local type nesting means that the
+       * usual recursive descent strategy doesn't work for finding a
+       * logical type path in an AST; in effect, local types have to
+       * be "turned inside-out".
+       *
+       * Worse yet, the actual tree structure may not exist in the tree!
+       * It is possible to recover the actual type from the symbol
+       * table, but the methods to create AST nodes are not visible
+       * here.  Hence, the conversion relies on custom implementations
+       * of the interfaces in com.sun.source.tree.Tree, which are
+       * defined in the local class TypeTree.
+       */
+      int i = ap0.size();
+      int n = ap1.size();
+      int actualDepth = 0;  // inner type levels seen
+      int expectedDepth = 0;  // inner type levels anticipated
+
+      // skip any declaration nodes
+      while (i < n) {
+        ASTPath.ASTEntry entry = ap1.get(i);
+        kind = entry.getTreeKind();
+        if (kind != Tree.Kind.METHOD && kind != Tree.Kind.VARIABLE) {
+          break;
+        }
+        ++i;
+      }
+
+      // now build up the type path in JVM's format
+      while (i < n) {
+        ASTPath.ASTEntry entry = ap1.get(i);
+        rec = rec.extend(entry);
+        kind = entry.getTreeKind();
+
+        while (node.getKind() == Tree.Kind.ANNOTATED_TYPE) {  // skip
+          node = ((AnnotatedTypeTree) node).getUnderlyingType();
+        }
+        if (expectedDepth == 0) {
+          expectedDepth = localDepth(node);
+        }
+
+        switch (kind) {
+        case ARRAY_TYPE:
+          if (expectedDepth == 0 && node.getKind() == kind) {
+            node = ((ArrayTypeTree) node).getType();
+            while (--actualDepth >= 0) {
+              tpes.add(TypePathEntry.INNER_TYPE);
+            }
+            tpes.add(TypePathEntry.ARRAY);
+            break;
+          }
+          throw new RuntimeException();
+
+        case MEMBER_SELECT:
+          if (--expectedDepth >= 0) {  // otherwise, shouldn't have MEMBER_SELECT
+            node = ((MemberSelectTree) node).getExpression();
+            ++actualDepth;
+            break;
+          }
+          throw new RuntimeException();
+
+        case NEW_ARRAY:
+          assert tpes.isEmpty();
+          ap0 = ap0.add(new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY,
+              ASTPath.TYPE, 0));
+          if (expectedDepth == 0 && node.getKind() == kind) {
+            if (node instanceof JCTree.JCNewArray) {
+              int arg = entry.getArgument();
+              if (arg > 0) {
+                node = ((JCTree.JCNewArray) node).elemtype;
+                tpes.add(TypePathEntry.ARRAY);
+                while (--arg > 0 && node instanceof JCTree.JCArrayTypeTree) {
+                  node = ((JCTree.JCArrayTypeTree) node).elemtype;
+                  tpes.add(TypePathEntry.ARRAY);
+                }
+                if (arg > 0) { throw new RuntimeException(); }
+              } else {
+                node = TypeTree.fromType(((JCTree.JCNewArray) node).type);
+              }
+            } else {
+              throw new RuntimeException("NYI");  // TODO
+            }
+            break;
+          }
+          throw new RuntimeException();
+
+        case PARAMETERIZED_TYPE:
+          if (node.getKind() == kind) {
+            ParameterizedTypeTree ptt = (ParameterizedTypeTree) node;
+            if (entry.childSelectorIs(ASTPath.TYPE)) {
+              node = ptt.getType();
+              break;  // ParameterizedType.type is "transparent" wrt type path
+            } else if (expectedDepth == 0
+                && entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
+              List<? extends Tree> typeArgs = ptt.getTypeArguments();
+              int j = entry.getArgument();
+              if (j >= 0 && j < typeArgs.size()) {
+                // make sure any inner types are accounted for
+                actualDepth = 0;
+                expectedDepth = localDepth(ptt.getType());
+                while (--expectedDepth >= 0) {
+                  tpes.add(TypePathEntry.INNER_TYPE);
+                }
+                node = typeArgs.get(j);
+                tpes.add(
+                    new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, j));
+                break;
+              }
+            }
+          }
+          throw new RuntimeException();
+
+        case UNBOUNDED_WILDCARD:
+          if (ASTPath.isWildcard(node.getKind())) {
+            if (expectedDepth == 0
+                && (i < 1
+                    || ap1.get(i-1).getTreeKind() != Tree.Kind.INSTANCE_OF)
+                && (i < 2
+                    || ap1.get(i-2).getTreeKind() != Tree.Kind.ARRAY_TYPE)) {
+              while (--actualDepth >= 0) {
+                tpes.add(TypePathEntry.INNER_TYPE);
+              }
+              tpes.add(TypePathEntry.WILDCARD);
+              break;
+            }
+          }
+          throw new RuntimeException();
+
+        default:
+          node = ASTIndex.getNode(cut, rec);
+          break;
+        }
+
+        ++i;
+      }
+
+      while (--actualDepth >= 0) {
+        tpes.add(TypePathEntry.INNER_TYPE);
+      }
+
+      organized.add(ins);
+      if (tpes.isEmpty()) {
+        // assert ap1.equals(ap0) && !map.containsKey(ap0);
+//        organized.add(ins);
+        // map.put(rec, (TypedInsertion) ins);
+      } else {
+        criteria.add(new ASTPathCriterion(ap0));
+        criteria.add(new GenericArrayLocationCriterion(
+            new InnerTypeLocation(tpes)));
+        tins.getInnerTypeInsertions().add(ins);
+      }
+    }
+    organized.addAll(map.values());
+    return organized;
+  }
+
+  private int newArrayInnerTypeDepth(ASTPath path) {
+    int d = 0;
+    if (path != null) {
+      while (!path.isEmpty()) {
+        ASTPath.ASTEntry entry = path.get(-1);
+        switch (entry.getTreeKind()) {
+        case ANNOTATED_TYPE:
+        case MEMBER_SELECT:
+        case PARAMETERIZED_TYPE:
+        case UNBOUNDED_WILDCARD:
+          d = 0;
+          break;
+        case ARRAY_TYPE:
+          ++d;
+          break;
+        case NEW_ARRAY:
+          if (entry.childSelectorIs(ASTPath.TYPE) && entry.hasArgument()) {
+            d += entry.getArgument();
+          }
+          return d;
+        default:
+          return 0;
+        }
+        path = path.getParentPath();
+      }
+    }
+    return 0;
+  }
+
+  /**
+   * Find an {@link ASTRecord} for the tree corresponding to a nested
+   * type of the type (use) to which the given record corresponds.
+   *
+   * @param rec record of (outer) type AST to be annotated
+   * @param loc inner type path
+   * @return record that locates the (nested) type in the source
+   */
+  private ASTRecord extendToInnerType(ASTRecord rec, List<TypePathEntry> loc) {
+    ASTRecord r = rec;
+    Iterator<TypePathEntry> iter = loc.iterator();
+    int depth = 0;
+
+    while (iter.hasNext()) {
+      TypePathEntry tpe = iter.next();
+      switch (tpe.tag) {
+      case ARRAY:
+        while (depth-- > 0) {
+          r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
+        }
+        r = r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
+        break;
+      case INNER_TYPE:
+        ++depth;
+        break;
+      case TYPE_ARGUMENT:
+        depth = 0;
+        r = r.extend(Tree.Kind.PARAMETERIZED_TYPE, ASTPath.TYPE_ARGUMENT,
+            tpe.arg);
+        break;
+      case WILDCARD:
+        while (depth-- > 0) {
+          r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
+        }
+        r = r.extend(Tree.Kind.UNBOUNDED_WILDCARD, ASTPath.BOUND);
+        break;
+      default:
+        throw new RuntimeException();
+      }
+    }
+    while (depth-- > 0) {
+      r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
+    }
+    return r;
+  }
+
+  /**
+   * Find an {@link ASTRecord} for the tree corresponding to a nested
+   * type of the type (use) to which the given tree and record
+   * correspond.
+   *
+   * @param rec record that locates {@code node} in the source
+   * @param loc inner type path
+   * @param node starting point for inner type path
+   * @return record that locates the nested type in the source
+   */
+  private ASTRecord extendToInnerType(ASTRecord rec,
+      List<TypePathEntry> loc, Tree node) {
+    ASTRecord r = rec;
+    Tree t = node;
+    Iterator<TypePathEntry> iter = loc.iterator();
+    TypePathEntry tpe = iter.next();
+
+outer:
+    while (true) {
+      int d = localDepth(node);
+
+      switch (t.getKind()) {
+      case ANNOTATED_TYPE:
+        r = r.extend(Tree.Kind.ANNOTATED_TYPE, ASTPath.TYPE);
+        t = ((JCTree.JCAnnotatedType) t).getUnderlyingType();
+        break;
+
+      case ARRAY_TYPE:
+        if (d == 0 && tpe.tag == TypePathEntryKind.ARRAY) {
+          int a = 0;
+          if (!r.astPath.isEmpty()) {
+            ASTPath.ASTEntry e = r.astPath.get(-1);
+            if (e.getTreeKind() == Tree.Kind.NEW_ARRAY
+                && e.childSelectorIs(ASTPath.TYPE)) {
+              a = 1 + e.getArgument();
+            }
+          }
+          r = a > 0
+              ? r.replacePath(r.astPath.getParentPath())
+                  .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, a)
+              : r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
+          t = ((ArrayTypeTree) t).getType();
+          break;
+        }
+        throw new RuntimeException();
+
+      case MEMBER_SELECT:
+        if (d > 0 && tpe.tag == TypePathEntryKind.INNER_TYPE) {
+          Tree temp = t;
+          do {
+            temp = ((JCTree.JCFieldAccess) temp).getExpression();
+            if (!iter.hasNext()) {
+              do {
+                r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
+              } while (--d > 0);
+              return r;
+            }
+            tpe = iter.next();
+            if (--d == 0) {
+              continue outer;  // avoid next() at end of loop
+            }
+          } while (tpe.tag == TypePathEntryKind.INNER_TYPE);
+        }
+        throw new RuntimeException();
+
+      case NEW_ARRAY:
+        if (d == 0) {
+          if (!r.astPath.isEmpty()) {
+            ASTPath.ASTEntry e = r.astPath.get(-1);
+            if (e.getTreeKind() == Tree.Kind.NEW_ARRAY) {
+              int a = 0;
+              while (tpe.tag == TypePathEntryKind.ARRAY) {
+                ++a;
+                if (!iter.hasNext()) { break; }
+                tpe = iter.next();
+              }
+              r = r.replacePath(r.astPath.getParentPath())
+                  .extend(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, a);
+              break;
+            }
+          }
+          r = r.extend(Tree.Kind.ARRAY_TYPE, ASTPath.TYPE);
+          t = ((JCTree.JCArrayTypeTree) t).getType();
+          break;
+        }
+        throw new RuntimeException();
+
+      case PARAMETERIZED_TYPE:
+        if (d == 0 && tpe.tag == TypePathEntryKind.TYPE_ARGUMENT) {
+          r = r.extend(Tree.Kind.PARAMETERIZED_TYPE,
+              ASTPath.TYPE_ARGUMENT, tpe.arg);
+          t = ((JCTree.JCTypeApply) t).getTypeArguments().get(tpe.arg);
+          break;
+        } else if (d > 0 && tpe.tag == TypePathEntryKind.INNER_TYPE) {
+          Tree temp = ((JCTree.JCTypeApply) t).getType();
+          r = r.extend(Tree.Kind.PARAMETERIZED_TYPE, ASTPath.TYPE);
+          t = temp;
+          do {
+            temp = ((JCTree.JCFieldAccess) temp).getExpression();
+            if (!iter.hasNext()) {
+              do {
+                r = r.extend(Tree.Kind.MEMBER_SELECT, ASTPath.EXPRESSION);
+              } while (--d > 0);
+              return r;
+            }
+            tpe = iter.next();
+            if (--d == 0) {
+              continue outer;  // avoid next() at end of loop
+            }
+          } while (tpe.tag == TypePathEntryKind.INNER_TYPE);
+        }
+        throw new RuntimeException();
+
+      case EXTENDS_WILDCARD:
+      case SUPER_WILDCARD:
+      case UNBOUNDED_WILDCARD:
+        if (tpe.tag == TypePathEntryKind.WILDCARD) {
+          t = ((JCTree.JCWildcard) t).getBound();
+          break;
+        }
+        throw new RuntimeException();
+
+      default:
+        if (iter.hasNext()) {
+          throw new RuntimeException();
+        }
+      }
+
+      if (!iter.hasNext()) { return r; }
+      tpe = iter.next();
+    }
+  }
+
+  // merge annotations, assuming types are structurally identical
+  private void mergeTypedInsertions(TypedInsertion ins0, TypedInsertion ins1) {
+    mergeTypes(ins0.getType(), ins1.getType());
+  }
+
+  private void mergeTypes(Type t0, Type t1) {
+    if (t0 == t1) { return; }
+    switch (t0.getKind()) {
+    case ARRAY:
+      {
+        ArrayType at0 = (ArrayType) t0;
+        ArrayType at1 = (ArrayType) t1;
+        mergeTypes(at0.getComponentType(), at1.getComponentType());
+        return;
+      }
+    case BOUNDED:
+      {
+        BoundedType bt0 = (BoundedType) t0;
+        BoundedType bt1 = (BoundedType) t1;
+        if (bt0.getBoundKind() != bt1.getBoundKind()) { break; }
+        mergeTypes(bt0.getBound(), bt1.getBound());
+        mergeTypes(bt0.getName(), bt1.getName());
+        return;
+      }
+    case DECLARED:
+      {
+        DeclaredType dt0 = (DeclaredType) t0;
+        DeclaredType dt1 = (DeclaredType) t1;
+        List<Type> tps0 = dt0.getTypeParameters();
+        List<Type> tps1 = dt1.getTypeParameters();
+        int n = tps0.size();
+        if (tps1.size() != n) { break; }
+        mergeTypes(dt0.getInnerType(), dt1.getInnerType());
+        for (String anno : dt1.getAnnotations()) {
+          if (!dt0.getAnnotations().contains(anno)) {
+            dt0.addAnnotation(anno);
+          }
+        }
+        for (int i = 0; i < n; i++) {
+          mergeTypes(tps0.get(i), tps1.get(i));
+        }
+        return;
+      }
+    }
+    throw new RuntimeException();
+  }
+
+  // Returns the depth of the innermost local type of a type AST.
+  private int localDepth(Tree node) {
+    Tree t = node;
+    int n = 0;
+loop:
+    while (t != null) {
+      switch (t.getKind()) {
+      case ANNOTATED_TYPE:
+        t = ((AnnotatedTypeTree) t).getUnderlyingType();
+        break;
+      case MEMBER_SELECT:
+        if (t instanceof JCTree.JCFieldAccess) {
+          JCTree.JCFieldAccess jfa = (JCTree.JCFieldAccess) t;
+          if (jfa.sym.kind == Kinds.PCK) {
+            t = jfa.getExpression();
+            continue;
+          }
+        }
+        t = ((MemberSelectTree) t).getExpression();
+        ++n;
+        break;
+      default:
+        break loop;
+      }
+    }
+    return n;
+  }
+
+  // Provides an additional level of indexing.
+  class ASTRecordMap<E> implements Map<ASTRecord, E> {
+    Map<ASTRecord, SortedMap<ASTPath, E>> back;
+
+    ASTRecordMap() {
+      back = new HashMap<ASTRecord, SortedMap<ASTPath, E>>();
+    }
+
+    private SortedMap<ASTPath, E> getMap(ASTRecord rec) {
+      ASTRecord key = rec.replacePath(ASTPath.empty());
+      SortedMap<ASTPath, E> map = back.get(key);
+      if (map == null) {
+        map = new TreeMap<ASTPath, E>();
+        back.put(key, map);
+      }
+      return map;
+    }
+
+    @Override
+    public int size() {
+      int n = 0;
+      for (SortedMap<ASTPath, E> map : back.values()) {
+        n += map.size();
+      }
+      return n;
+    }
+
+    @Override
+    public boolean isEmpty() {
+      return size() == 0;
+    }
+
+    @Override
+    public boolean containsKey(Object key) {
+      ASTRecord rec = (ASTRecord) key;
+      SortedMap<ASTPath, E> m = getMap(rec);
+      return m != null && m.containsKey(rec.astPath);
+    }
+
+    @Override
+    public boolean containsValue(Object value) {
+      @SuppressWarnings("unchecked")
+      E e = (E) value;
+      for (SortedMap<ASTPath, E> map : back.values()) {
+        if (map.containsValue(e)) { return true; }
+      }
+      return false;
+    }
+
+    @Override
+    public E get(Object key) {
+      ASTRecord rec = (ASTRecord) key;
+      SortedMap<ASTPath, E> map = getMap(rec);
+      return map == null ? null : map.get(rec.astPath);
+    }
+
+    @Override
+    public E put(ASTRecord key, E value) {
+      ASTRecord rec = key;
+      SortedMap<ASTPath, E> map = getMap(rec);
+      return map == null ? null : map.put(rec.astPath, value);
+    }
+
+    @Override
+    public E remove(Object key) {
+      ASTRecord rec = (ASTRecord) key;
+      SortedMap<ASTPath, E> map = getMap(rec);
+      return map == null ? null : map.remove(rec.astPath);
+    }
+
+    @Override
+    public void putAll(Map<? extends ASTRecord, ? extends E> m) {
+      for (Map.Entry<? extends ASTRecord, ? extends E> entry : m.entrySet()) {
+        put(entry.getKey(), entry.getValue());
+      }
+    }
+
+    @Override
+    public void clear() {
+      back.clear();
+    }
+
+    @Override
+    public Set<ASTRecord> keySet() {
+      return back.keySet();
+    }
+
+    @Override
+    public Collection<E> values() {
+      Set<E> ret = new LinkedHashSet<E>();
+      for (SortedMap<ASTPath, E> m : back.values()) {
+        ret.addAll(m.values());
+      }
+      return ret;
+    }
+
+    @Override
+    public Set<Map.Entry<ASTRecord, E>> entrySet() {
+      final int size = size();
+      return new AbstractSet<Map.Entry<ASTRecord, E>>() {
+        @Override
+        public Iterator<Map.Entry<ASTRecord, E>> iterator() {
+          return new Iterator<Map.Entry<ASTRecord, E>>() {
+            Iterator<Map.Entry<ASTRecord, SortedMap<ASTPath, E>>> iter0 =
+                back.entrySet().iterator();
+            Iterator<Map.Entry<ASTPath, E>> iter1 =
+                Collections.<Map.Entry<ASTPath, E>>emptyIterator();
+            ASTRecord rec = null;
+
+            @Override
+            public boolean hasNext() {
+              if (iter1.hasNext()) { return true; }
+              while (iter0.hasNext()) {
+                Map.Entry<ASTRecord, SortedMap<ASTPath, E>> entry =
+                    iter0.next();
+                rec = entry.getKey();
+                iter1 = entry.getValue().entrySet().iterator();
+                if (iter1.hasNext()) { return true; }
+              }
+              iter1 = Collections.<Map.Entry<ASTPath, E>>emptyIterator();
+              return false;
+            }
+
+            @Override
+            public Map.Entry<ASTRecord, E> next() {
+              if (!hasNext()) { throw new NoSuchElementException(); }
+              final Map.Entry<ASTPath, E> e0 = iter1.next();
+              return new Map.Entry<ASTRecord, E>() {
+                final ASTRecord key = rec.replacePath(e0.getKey());
+                final E val = e0.getValue();
+                @Override public ASTRecord getKey() { return key; }
+                @Override public E getValue() { return val; }
+                @Override public E setValue(E value) {
+                  throw new UnsupportedOperationException();
+                }
+              };
+            }
+
+            @Override
+            public void remove() {
+              throw new UnsupportedOperationException();
+            }
+          };
+        }
+
+        @Override
+        public int size() { return size; }
+      };
+    }
+  }
+
+  // Simple AST implementation used only in determining type paths.
+  static abstract class TypeTree implements ExpressionTree {
+    private static Map<String, TypeTag> primTags =
+        new HashMap<String, TypeTag>();
+    {
+      primTags.put("byte", TypeTag.BYTE);
+      primTags.put("char", TypeTag.CHAR);
+      primTags.put("short", TypeTag.SHORT);
+      primTags.put("long", TypeTag.LONG);
+      primTags.put("float", TypeTag.FLOAT);
+      primTags.put("int", TypeTag.INT);
+      primTags.put("double", TypeTag.DOUBLE);
+      primTags.put("boolean", TypeTag.BOOLEAN);
+    }
+
+    static TypeTree fromJCTree(JCTree jt) {
+      if (jt != null) {
+        Kind kind = jt.getKind();
+        switch (kind) {
+        case ANNOTATED_TYPE:
+          return fromJCTree(
+              ((JCTree.JCAnnotatedType) jt).getUnderlyingType());
+        case IDENTIFIER:
+          return new IdenT(
+              ((JCTree.JCIdent) jt).sym.getSimpleName().toString());
+        case ARRAY_TYPE:
+          return new ArrT(
+              fromJCTree(((JCTree.JCArrayTypeTree) jt).getType()));
+        case MEMBER_SELECT:
+          return new LocT(
+              fromJCTree(((JCTree.JCFieldAccess) jt).getExpression()),
+              ((JCTree.JCFieldAccess) jt).getIdentifier());
+        case EXTENDS_WILDCARD:
+        case SUPER_WILDCARD:
+          return new WildT(kind,
+              fromJCTree(((JCTree.JCWildcard) jt).getBound()));
+        case UNBOUNDED_WILDCARD:
+          return new WildT();
+        case PARAMETERIZED_TYPE:
+          com.sun.tools.javac.util.List<JCExpression> typeArgs =
+            ((JCTree.JCTypeApply) jt).getTypeArguments();
+          List<Tree> args = new ArrayList<Tree>(typeArgs.size());
+          for (JCTree.JCExpression typeArg : typeArgs) {
+            args.add(fromJCTree(typeArg));
+          }
+          return new ParT(
+              fromJCTree(((JCTree.JCTypeApply) jt).getType()),
+              args);
+        default:
+          break;
+        }
+      }
+      return null;
+    }
+
+    static TypeTree fromType(final Type type) {
+      switch (type.getKind()) {
+      case ARRAY:
+        final ArrayType atype = (ArrayType) type;
+        final TypeTree componentType = fromType(atype.getComponentType());
+        return new ArrT(componentType);
+      case BOUNDED:
+        final BoundedType btype = (BoundedType) type;
+        final BoundedType.BoundKind bk = btype.getBoundKind();
+        final String bname = btype.getName().getName();
+        final TypeTree bound = fromType(btype.getBound());
+        return new Param(bname, bk, bound);
+      case DECLARED:
+        final DeclaredType dtype = (DeclaredType) type;
+        if (dtype.isWildcard()) {
+          return new WildT();
+        } else {
+          final String dname = dtype.getName();
+          TypeTag typeTag = primTags.get(dname);
+          if (typeTag == null) {
+            final TypeTree base = new IdenT(dname);
+            TypeTree ret = base;
+            List<Type> params = dtype.getTypeParameters();
+            DeclaredType inner = dtype.getInnerType();
+            if (!params.isEmpty()) {
+              final List<Tree> typeArgs = new ArrayList<Tree>(params.size());
+              for (Type t : params) { typeArgs.add(fromType(t)); }
+              ret = new ParT(base, typeArgs);
+            }
+            return inner == null ? ret : meld(fromType(inner), ret);
+          } else {
+            final TypeKind typeKind = typeTag.getPrimitiveTypeKind();
+            return new PrimT(typeKind);
+          }
+        }
+      default:
+        throw new RuntimeException("unknown type kind " + type.getKind());
+      }
+    }
+
+    static TypeTree fromType(final com.sun.tools.javac.code.Type type) {
+      return fromType(conv(type));
+    }
+
+    /**
+     * @param jtype
+     * @return
+     */
+    static Type conv(final com.sun.tools.javac.code.Type jtype) {
+      Type type = null;
+      DeclaredType d;
+      com.sun.tools.javac.code.Type t;
+      switch (jtype.getKind()) {
+      case ARRAY:
+        t = ((com.sun.tools.javac.code.Type.ArrayType) jtype).elemtype;
+        type = new ArrayType(conv(t));
+        break;
+      case DECLARED:
+        t = jtype;
+        d = null;
+        do {
+          DeclaredType d0 = d;
+          com.sun.tools.javac.code.Type.ClassType ct =
+              (com.sun.tools.javac.code.Type.ClassType) t;
+          d = new DeclaredType(ct.tsym.name.toString());
+          d.setInnerType(d0);
+          d0 = d;
+          for (com.sun.tools.javac.code.Type a : ct.getTypeArguments()) {
+            d.addTypeParameter(conv(a));
+          }
+          t = ct.getEnclosingType();
+        } while (t.getKind() == TypeKind.DECLARED);
+        type = d;
+        break;
+      case WILDCARD:
+        BoundedType.BoundKind k;
+        t = ((com.sun.tools.javac.code.Type.WildcardType) jtype).bound;
+        switch (((com.sun.tools.javac.code.Type.WildcardType) jtype).kind) {
+        case EXTENDS:
+          k = BoundedType.BoundKind.EXTENDS;
+          break;
+        case SUPER:
+          k = BoundedType.BoundKind.SUPER;
+          break;
+        case UNBOUND:
+          k = null;
+          type = new DeclaredType("?");
+          break;
+        default:
+          throw new RuntimeException();
+        }
+        if (k != null) {
+          d = new DeclaredType(jtype.tsym.name.toString());
+          type = new BoundedType(d, k, (DeclaredType) conv(t));
+        }
+        break;
+      case TYPEVAR:
+        t = ((com.sun.tools.javac.code.Type.TypeVar) jtype).getUpperBound();
+        type = conv(t);
+        if (type.getKind() == Type.Kind.DECLARED) {
+          type = new BoundedType(new DeclaredType(jtype.tsym.name.toString()),
+              BoundedType.BoundKind.EXTENDS, (DeclaredType) type);
+        }  // otherwise previous conv should have been here already
+        break;
+      case INTERSECTION:
+        t = jtype.tsym.erasure_field;  // ???
+        type = new DeclaredType(t.tsym.name.toString());
+        break;
+      case UNION:
+        // TODO
+        break;
+      case BOOLEAN:
+      case BYTE:
+      case CHAR:
+      case DOUBLE:
+      case LONG:
+      case SHORT:
+      case FLOAT:
+      case INT:
+        type = new DeclaredType(jtype.tsym.name.toString());
+        break;
+      // case ERROR:
+      // case EXECUTABLE:
+      // case NONE:
+      // case NULL:
+      // case OTHER:
+      // case PACKAGE:
+      // case VOID:
+      default:
+        break;
+      }
+      return type;
+    }
+
+    private static TypeTree meld(final TypeTree t0, final TypeTree t1) {
+      switch (t0.getKind()) {
+      case IDENTIFIER:
+        IdenT it = (IdenT) t0;
+        return new LocT(t1, it.getName());
+      case MEMBER_SELECT:
+        LocT lt = (LocT) t0;
+        return new LocT(meld(lt.getExpression(), t1), lt.getIdentifier());
+      case PARAMETERIZED_TYPE:
+        ParT pt = (ParT) t0;
+        return new ParT(meld(pt.getType(), t1), pt.getTypeArguments());
+      default:
+        throw new IllegalArgumentException("unexpected type " + t0);
+      }
+    }
+
+    static final class ArrT extends TypeTree implements ArrayTypeTree {
+      private final TypeTree componentType;
+
+      ArrT(TypeTree componentType) {
+        this.componentType = componentType;
+      }
+
+      @Override
+      public Kind getKind() { return Kind.ARRAY_TYPE; }
+
+      @Override
+      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
+        return visitor.visitArrayType(this, data);
+      }
+
+      @Override
+      public TypeTree getType() { return componentType; }
+
+      @Override
+      public String toString() { return componentType + "[]"; }
+    }
+
+    static final class LocT extends TypeTree implements MemberSelectTree {
+      private final TypeTree expr;
+      private final Name name;
+
+      LocT(TypeTree expr, Name name) {
+        this.expr = expr;
+        this.name = name;
+      }
+
+      @Override
+      public Kind getKind() { return Kind.MEMBER_SELECT; }
+
+      @Override
+      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
+        return visitor.visitMemberSelect(this, data);
+      }
+
+      @Override
+      public TypeTree getExpression() { return expr; }
+
+      @Override
+      public Name getIdentifier() { return name; }
+
+      @Override
+      public String toString() { return expr + "." + name; }
+    }
+
+    static final class ParT extends TypeTree implements ParameterizedTypeTree {
+      private final TypeTree base;
+      private final List<? extends Tree> typeArgs;
+
+      ParT(TypeTree base, List<? extends Tree> typeArgs) {
+        this.base = base;
+        this.typeArgs = typeArgs;
+      }
+
+      @Override
+      public Kind getKind() { return Kind.PARAMETERIZED_TYPE; }
+
+      @Override
+      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
+        return visitor.visitParameterizedType(this, data);
+      }
+
+      @Override
+      public TypeTree getType() { return base; }
+
+      @Override
+      public List<? extends Tree> getTypeArguments() {
+        return typeArgs;
+      }
+
+      @Override
+      public String toString() {
+        StringBuilder sb = new StringBuilder(base.toString());
+        String s = "<";
+        for (Tree t : typeArgs) {
+          sb.append(s);
+          sb.append(t.toString());
+          s = ", ";
+        }
+        sb.append('>');
+        return sb.toString();
+      }
+    }
+
+    static final class PrimT extends TypeTree implements PrimitiveTypeTree {
+      private final TypeKind typeKind;
+
+      PrimT(TypeKind typeKind) {
+        this.typeKind = typeKind;
+      }
+
+      @Override
+      public Kind getKind() { return Kind.PRIMITIVE_TYPE; }
+
+      @Override
+      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
+        return visitor.visitPrimitiveType(this, data);
+      }
+
+      @Override
+      public TypeKind getPrimitiveTypeKind() { return typeKind; }
+
+      @Override
+      public String toString() {
+        switch (typeKind) {
+        case BOOLEAN: return "boolean";
+        case BYTE: return "byte";
+        case CHAR: return "char";
+        case DOUBLE: return "double";
+        case FLOAT: return "float";
+        case INT: return "int";
+        case LONG: return "long";
+        case SHORT: return "short";
+        // case VOID: return "void";
+        // case WILDCARD: return "?";
+        default:
+          throw new IllegalArgumentException("unexpected type kind "
+              + typeKind);
+        }
+      }
+    }
+
+    static final class IdenT extends TypeTree implements IdentifierTree {
+      private final String name;
+
+      IdenT(String dname) {
+        this.name = dname;
+      }
+
+      @Override
+      public Kind getKind() { return Kind.IDENTIFIER; }
+
+      @Override
+      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
+        return visitor.visitIdentifier(this, data);
+      }
+
+      @Override
+      public Name getName() { return new TypeName(name); }
+
+      @Override
+      public String toString() { return name; }
+    }
+
+    static final class WildT extends TypeTree implements WildcardTree {
+      private final TypeTree bound;
+      private final Kind kind;
+
+      WildT() {
+        this(Kind.UNBOUNDED_WILDCARD, null);
+      }
+
+      WildT(TypeTree bound, BoundedType.BoundKind bk) {
+        this(bk == BoundedType.BoundKind.SUPER
+                ? Kind.SUPER_WILDCARD
+                : Kind.EXTENDS_WILDCARD,
+            bound);
+      }
+
+      WildT(Kind kind, TypeTree bound) {
+        this.kind = kind;
+        this.bound = bound;
+      }
+
+      @Override
+      public Kind getKind() { return kind; }
+
+      @Override
+      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
+        return visitor.visitWildcard(this, data);
+      }
+
+      @Override
+      public Tree getBound() { return bound; }
+
+      @Override
+      public String toString() { return "?"; }
+    }
+
+    static final class Param extends TypeTree implements TypeParameterTree {
+      private final String bname;
+      private final BoundedType.BoundKind bk;
+      private final Tree bound;
+
+      Param(String bname, BoundedType.BoundKind bk, TypeTree bound) {
+        this.bname = bname;
+        this.bk = bk;
+        this.bound = bound;
+      }
+
+      @Override
+      public Kind getKind() { return Kind.TYPE_PARAMETER; }
+
+      @Override
+      public <R, D> R accept(TreeVisitor<R, D> visitor, D data) {
+        return visitor.visitTypeParameter(this, data);
+      }
+
+      @Override
+      public Name getName() { return new TypeName(bname); }
+
+      @Override
+      public List<? extends Tree> getBounds() {
+        return Collections.singletonList(bound);
+      }
+
+      @Override
+      public List<? extends AnnotationTree> getAnnotations() {
+        return Collections.emptyList();
+      }
+
+      @Override
+      public String toString() {
+        return bname + " " + bk.toString() + " " + bound.toString();
+      }
+    }
+
+    static final class TypeName implements Name {
+      private final String str;
+
+      TypeName(String str) {
+        this.str = str;
+      }
+
+      @Override
+      public int length() { return str.length(); }
+
+      @Override
+      public char charAt(int index) { return str.charAt(index); }
+
+      @Override
+      public CharSequence subSequence(int start, int end) {
+        return str.subSequence(start, end);
+      }
+
+      @Override
+      public boolean contentEquals(CharSequence cs) {
+        if (cs != null) {
+          int n = length();
+          if (cs.length() == n) {
+            for (int i = 0; i < n; i++) {
+              if (charAt(i) != cs.charAt(i)) { return false; }
+            }
+            return true;
+          }
+        }
+        return false;
+      }
+
+      @Override
+      public String toString() { return str; }
+    }
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/InstanceOfCriterion.java b/annotation-file-utilities/src/annotator/find/InstanceOfCriterion.java
new file mode 100644
index 0000000..2ed3ab9
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/InstanceOfCriterion.java
@@ -0,0 +1,92 @@
+package annotator.find;
+
+import annotations.el.RelativeLocation;
+import annotator.scanner.InstanceOfScanner;
+
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class InstanceOfCriterion implements Criterion {
+
+  private final String methodName;
+  private final RelativeLocation loc;
+
+  public InstanceOfCriterion(String methodName, RelativeLocation loc) {
+    this.methodName = methodName.substring(0, methodName.lastIndexOf(")") + 1);
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      Criteria.dbug.debug("return null");
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    Criteria.dbug.debug("%n%s%n", this.toString());
+    Criteria.dbug.debug("InstanceOfCriterion.isSatisfiedBy: %s%n", leaf);
+    Criteria.dbug.debug("leaf: %s%n", leaf);
+    Criteria.dbug.debug("kind: %s%n", leaf.getKind());
+    Criteria.dbug.debug("class: %s%n", leaf.getClass());
+
+    TreePath parentPath = path.getParentPath();
+    if (parentPath == null) {
+      Criteria.dbug.debug("return: parent path null%n");
+      return false;
+    }
+
+    Tree parent = parentPath.getLeaf();
+    if (parent == null) {
+      Criteria.dbug.debug("return: parent null%n");
+      return false;
+    }
+
+    if (parent.getKind() == Tree.Kind.INSTANCE_OF) {
+      InstanceOfTree instanceOfTree = (InstanceOfTree) parent;
+      if (leaf != instanceOfTree.getType()) {
+        Criteria.dbug.debug("return: not type part of instanceof%n");
+        return false;
+      }
+
+      int indexInSource = InstanceOfScanner.indexOfInstanceOfTree(path, parent);
+      Criteria.dbug.debug("return source: %d%n", indexInSource);
+      boolean b;
+      if (loc.isBytecodeOffset()) {
+        int indexInClass = InstanceOfScanner.getMethodInstanceOfIndex(methodName, loc.offset);
+        Criteria.dbug.debug("return class: %d%n", indexInClass);
+        b = (indexInSource == indexInClass);
+      } else {
+        b = (indexInSource == loc.index);
+        Criteria.dbug.debug("return loc.index: %d%n", loc.index);
+      }
+      Criteria.dbug.debug("return new: %b", b);
+      return b;
+    } else {
+      boolean b = this.isSatisfiedBy(path.getParentPath());
+      Criteria.dbug.debug("return parent: %b%n", b);
+      return b;
+    }
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.INSTANCE_OF;
+  }
+
+  @Override
+  public String toString() {
+    return "InstanceOfCriterion: in method: " + methodName + " location: " + loc;
+  }
+
+}
diff --git a/annotation-file-utilities/src/annotator/find/IntersectionTypeLocationCriterion.java b/annotation-file-utilities/src/annotator/find/IntersectionTypeLocationCriterion.java
new file mode 100644
index 0000000..313a328
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/IntersectionTypeLocationCriterion.java
@@ -0,0 +1,61 @@
+package annotator.find;
+
+import java.util.List;
+
+import annotations.el.RelativeLocation;
+import annotations.io.ASTPath;
+
+import com.sun.source.tree.IntersectionTypeTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * @author dan
+ */
+public class IntersectionTypeLocationCriterion implements Criterion {
+  private final int typeIndex;
+
+  public IntersectionTypeLocationCriterion(RelativeLocation loc) {
+    typeIndex = loc.type_index;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    TreePath parentPath = path.getParentPath();
+    if (parentPath != null) {
+      Tree parent = parentPath.getLeaf();
+      if (parentPath != null
+          && parent.getKind() == Tree.Kind.INTERSECTION_TYPE) {
+        IntersectionTypeTree itt = (IntersectionTypeTree) parent;
+        List<? extends Tree> bounds = itt.getBounds();
+        Tree leaf = path.getLeaf();
+        if (typeIndex < bounds.size()
+            && leaf == bounds.get(typeIndex)) {
+          return true;
+        }
+      }
+    }
+    Tree.Kind kind = path.getLeaf().getKind();
+    if (ASTPath.isTypeKind(kind) || kind == Tree.Kind.MEMBER_SELECT) {
+      return isSatisfiedBy(path.getParentPath());
+    }
+    return false;
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.INTERSECT_LOCATION;
+  }
+
+  @Override
+  public String toString() {
+    return "IntersectionTypeLocation: at type index: " + typeIndex;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/IsCriterion.java b/annotation-file-utilities/src/annotator/find/IsCriterion.java
new file mode 100644
index 0000000..bcd48cb
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/IsCriterion.java
@@ -0,0 +1,72 @@
+package annotator.find;
+
+import annotator.scanner.CommonScanner;
+
+import com.sun.source.tree.*;
+import com.sun.source.util.TreePath;
+
+/**
+ * Represents the criterion that a program element has a particular type and
+ * name.
+ */
+final class IsCriterion implements Criterion {
+
+  private final Tree.Kind kind;
+  private final String name;
+
+  IsCriterion(Tree.Kind kind, String name) {
+    this.kind = kind;
+    this.name = name;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Kind getKind() {
+    return Kind.HAS_KIND;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+    Tree tree = path.getLeaf();
+    if (CommonScanner.hasClassKind(tree)) {
+      return InClassCriterion.isSatisfiedBy(path, name, /*exactMatch=*/ true);
+    }
+    if (tree.getKind() != kind) {
+      return false;
+    }
+    switch (tree.getKind()) {
+    case VARIABLE:
+      String varName = ((VariableTree)tree).getName().toString();
+      return varName.equals(name);
+    case METHOD:
+      String methodName = ((MethodTree)tree).getName().toString();
+      return methodName.equals(name);
+    // case CLASS:
+    //  return InClassCriterion.isSatisfiedBy(path, name, /*exactMatch=*/ true);
+    default:
+      throw new Error("unknown tree kind " + kind);
+    }
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return "is " + kind.toString().toLowerCase() + " '" + name + "'";
+  }
+
+}
diff --git a/annotation-file-utilities/src/annotator/find/IsSigMethodCriterion.java b/annotation-file-utilities/src/annotator/find/IsSigMethodCriterion.java
new file mode 100644
index 0000000..62bf373
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/IsSigMethodCriterion.java
@@ -0,0 +1,544 @@
+package annotator.find;
+
+import annotator.Main;
+
+import java.util.ArrayList;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import plume.UtilMDE;
+
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.ImportTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+
+public class IsSigMethodCriterion implements Criterion {
+
+  // The context is used for determining the fully qualified name of methods.
+  private static class Context {
+    public final String packageName;
+    public final List<String> imports;
+    public Context(String packageName, List<String> imports) {
+      this.packageName = packageName;
+      this.imports = imports;
+    }
+  }
+
+  private static final Map<CompilationUnitTree, Context> contextCache = new HashMap<CompilationUnitTree, Context>();
+
+  private final String fullMethodName; // really the full JVML signature, sans return type
+  private final String simpleMethodName;
+  // list of parameters in Java, not JVML format
+  private final List<String> fullyQualifiedParams;
+  // in Java, not JVML, format.  may be "void"
+  private final String returnType;
+
+  public IsSigMethodCriterion(String methodName) {
+    this.fullMethodName = methodName.substring(0, methodName.indexOf(")") + 1);
+    this.simpleMethodName = methodName.substring(0, methodName.indexOf("("));
+//    this.fullyQualifiedParams = new ArrayList<String>();
+//    for (String s : methodName.substring(
+//        methodName.indexOf("(") + 1, methodName.indexOf(")")).split(",")) {
+//      if (s.length() > 0) {
+//        fullyQualifiedParams.add(s);
+//      }
+//    }
+    this.fullyQualifiedParams = new ArrayList<String>();
+    try {
+      parseParams(
+        methodName.substring(methodName.indexOf("(") + 1,
+            methodName.indexOf(")")));
+    } catch (Exception e) {
+      throw new RuntimeException("Caught exception while parsing method: " +
+          methodName, e);
+    }
+    String returnTypeJvml = methodName.substring(methodName.indexOf(")") + 1);
+    this.returnType = (returnTypeJvml.equals("V")
+                       ? "void"
+                       : UtilMDE.fieldDescriptorToBinaryName(returnTypeJvml));
+  }
+
+  // params is in JVML format
+  private void parseParams(String params) {
+    while (params.length() != 0) {
+      // nextParam is in JVML format
+      String nextParam = readNext(params);
+      params = params.substring(nextParam.length());
+      fullyQualifiedParams.add(UtilMDE.fieldDescriptorToBinaryName(nextParam));
+    }
+  }
+
+  // strip a JVML type off a string containing multiple concatenated JVML types
+  private String readNext(String restOfParams) {
+    String firstChar = restOfParams.substring(0, 1);
+    if (isPrimitiveLetter(firstChar)) {
+      return firstChar;
+    } else if (firstChar.equals("[")) {
+      return "[" + readNext(restOfParams.substring(1));
+    } else if (firstChar.equals("L")) {
+      return "L" + restOfParams.substring(1, restOfParams.indexOf(";") + 1);
+    } else {
+      throw new RuntimeException("Unknown method params: " + fullMethodName + " with remainder: " + restOfParams);
+    }
+  }
+
+  // called by isSatisfiedBy(TreePath), will get compilation unit on its own
+  private static Context initImports(TreePath path) {
+    CompilationUnitTree topLevel = path.getCompilationUnit();
+    Context result = contextCache.get(topLevel);
+    if (result != null) {
+      return result;
+    }
+
+    ExpressionTree packageTree = topLevel.getPackageName();
+    String packageName;
+    if (packageTree == null) {
+      packageName = ""; // the default package
+    } else {
+      packageName = packageTree.toString();
+    }
+
+    List<String> imports = new ArrayList<String>();
+    for (ImportTree i : topLevel.getImports()) {
+      String imported = i.getQualifiedIdentifier().toString();
+      imports.add(imported);
+    }
+
+    result = new Context(packageName, imports);
+    contextCache.put(topLevel, result);
+    return result;
+  }
+
+  // Abstracts out the inner loop of matchTypeParams.
+  // goalType is fully-qualified.
+  private boolean matchTypeParam(String goalType, Tree type,
+                                 Map<String, String> typeToClassMap,
+                                 Context context) {
+    String simpleType = type.toString();
+
+    boolean haveMatch = matchSimpleType(goalType, simpleType, context);
+    if (!haveMatch) {
+      if (!typeToClassMap.isEmpty()) {
+        for (Map.Entry<String, String> p : typeToClassMap.entrySet()) {
+          simpleType = simpleType.replaceAll("\\b" + p.getKey() + "\\b",
+              p.getValue());
+          haveMatch = matchSimpleType(goalType, simpleType, context);
+          if (!haveMatch) {
+            Criteria.dbug.debug("matchTypeParams() => false:%n");
+            Criteria.dbug.debug("  type = %s%n", type);
+            Criteria.dbug.debug("  simpleType = %s%n", simpleType);
+            Criteria.dbug.debug("  goalType = %s%n", goalType);
+          }
+        }
+      }
+    }
+    return haveMatch;
+  }
+
+
+  private boolean matchTypeParams(List<? extends VariableTree> sourceParams,
+                                  Map<String, String> typeToClassMap,
+                                  Context context) {
+    assert sourceParams.size() == fullyQualifiedParams.size();
+    for (int i = 0; i < sourceParams.size(); i++) {
+      String fullType = fullyQualifiedParams.get(i);
+      VariableTree vt = sourceParams.get(i);
+      Tree vtType = vt.getType();
+      if (! matchTypeParam(fullType, vtType, typeToClassMap, context)) {
+        Criteria.dbug.debug(
+            "matchTypeParam() => false:%n  i=%d vt = %s%n  fullType = %s%n",
+            i, vt, fullType);
+        return false;
+      }
+    }
+    return true;
+  }
+
+
+  // simpleType is the name as it appeared in the source code.
+  // fullType is fully-qualified.
+  // Both are in Java, not JVML, format.
+  private boolean matchSimpleType(String fullType, String simpleType, Context context) {
+    Criteria.dbug.debug("matchSimpleType(%s, %s, %s)%n",
+        fullType, simpleType, context);
+
+    // must strip off generics, is all of this necessary, though?
+    // do you ever have generics anywhere but at the end?
+    while (simpleType.contains("<")) {
+      int bracketIndex = simpleType.lastIndexOf("<");
+      String beforeBracket = simpleType.substring(0, bracketIndex);
+      String afterBracket = simpleType.substring(simpleType.indexOf(">", bracketIndex) + 1);
+      simpleType = beforeBracket + afterBracket;
+    }
+
+
+    // TODO: arrays?
+
+    // first try qualifying simpleType with this package name,
+    // then with java.lang
+    // then with default package
+    // then with all of the imports
+
+    boolean matchable = false;
+
+    if (!matchable) {
+      // match with this package name
+      String packagePrefix = context.packageName;
+      if (packagePrefix.length() > 0) {
+        packagePrefix = packagePrefix + ".";
+      }
+      if (matchWithPrefix(fullType, simpleType, packagePrefix)) {
+        matchable = true;
+      }
+    }
+
+    if (!matchable) {
+      // match with java.lang
+      if (matchWithPrefix(fullType, simpleType, "java.lang.")) {
+        matchable = true;
+      }
+    }
+
+    if (!matchable) {
+      // match with default package
+      if (matchWithPrefix(fullType, simpleType, "")) {
+        matchable = true;
+      }
+    }
+
+    /*
+     * From Java 7 language definition 6.5.5.2 (Qualified Types):
+     * If a type name is of the form Q.Id, then Q must be either a type
+     * name or a package name.  If Id names exactly one accessible type
+     * that is a member of the type or package denoted by Q, then the
+     * qualified type name denotes that type.
+     */
+    if (!matchable) {
+      // match with any of the imports
+      for (String someImport : context.imports) {
+        String importPrefix = null;
+        if (someImport.contains("*")) {
+          // don't include the * in the prefix, should end in .
+          // TODO: this is a real bug due to nonnull, though I discovered it manually
+          // importPrefix = someImport.substring(0, importPrefix.indexOf("*"));
+          importPrefix = someImport.substring(0, someImport.indexOf("*"));
+        } else {
+          // if you imported a specific class, you can only use that import
+          // if the last part matches the simple type
+          String importSimpleType =
+            someImport.substring(someImport.lastIndexOf(".") + 1);
+
+          // Remove array brackets from simpleType if it has them
+          int arrayBracket = simpleType.indexOf('[');
+          String simpleBaseType = simpleType;
+          if (arrayBracket > -1) {
+            simpleBaseType = simpleType.substring(0, arrayBracket);
+          }
+          if (!(simpleBaseType.equals(importSimpleType)
+              || simpleBaseType.startsWith(importSimpleType + "."))) {
+            continue;
+          }
+
+          importPrefix = someImport.substring(0, someImport.lastIndexOf(".") + 1);
+        }
+
+        if (matchWithPrefix(fullType, simpleType, importPrefix)) {
+          matchable = true;
+          break; // out of for loop
+        }
+      }
+    }
+
+    return matchable;
+  }
+
+  private boolean matchWithPrefix(String fullType, String simpleType, String prefix) {
+    return matchWithPrefixOneWay(fullType, simpleType, prefix)
+        || matchWithPrefixOneWay(simpleType, fullType, prefix);
+  }
+
+  // simpleType can be in JVML format ??  Is that really possible?
+  private boolean matchWithPrefixOneWay(String fullType, String simpleType,
+      String prefix) {
+
+    // maybe simpleType is in JVML format
+    String simpleType2 = simpleType.replace("/", ".");
+
+    String fullType2 = fullType.replace("$", ".");
+
+    /* unused String prefix2 = (prefix.endsWith(".")
+                      ? prefix.substring(0, prefix.length() - 1)
+                      : prefix); */
+    boolean b = (fullType2.equals(prefix + simpleType2)
+                 // Hacky way to handle the possibility that fulltype is an
+                 // inner type but simple type is unqualified.
+                 || (fullType.startsWith(prefix)
+                     && (fullType.endsWith("$" + simpleType2)
+                         || fullType2.endsWith("." + simpleType2))));
+    Criteria.dbug.debug("matchWithPrefix(%s, %s, %s) => %b)%n",
+        fullType2, simpleType, prefix, b);
+    return b;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Context context = initImports(path);
+
+    Tree leaf = path.getLeaf();
+
+    if (leaf.getKind() != Tree.Kind.METHOD) {
+      Criteria.dbug.debug(
+          "IsSigMethodCriterion.isSatisfiedBy(%s) => false: not a METHOD tree%n",
+          Main.pathToString(path));
+      return false;
+    }
+    // else if ((((JCMethodDecl) leaf).mods.flags & Flags.GENERATEDCONSTR) != 0) {
+    //  Criteria.dbug.debug(
+    //      "IsSigMethodCriterion.isSatisfiedBy(%s) => false: generated constructor%n",
+    //      Main.pathToString(path));
+    //  return false;
+    // }
+
+    MethodTree mt = (MethodTree) leaf;
+
+    if (! simpleMethodName.equals(mt.getName().toString())) {
+      Criteria.dbug.debug("IsSigMethodCriterion.isSatisfiedBy => false: Names don't match%n");
+      return false;
+    }
+
+    List<? extends VariableTree> sourceParams = mt.getParameters();
+    if (fullyQualifiedParams.size() != sourceParams.size()) {
+      Criteria.dbug.debug("IsSigMethodCriterion.isSatisfiedBy => false: Number of parameters don't match%n");
+      return false;
+    }
+
+    // now go through all type parameters declared by method
+    // and for each one, create a mapping from the type to the
+    // first declared extended class, defaulting to Object
+    // for example,
+    // <T extends Date> void foo(T t)
+    //  creates mapping: T -> Date
+    // <T extends Date & List> void foo(Object o)
+    //  creates mapping: T -> Date
+    // <T extends Date, U extends List> foo(Object o)
+    //  creates mappings: T -> Date, U -> List
+    // <T> void foo(T t)
+    //  creates mapping: T -> Object
+
+    Map<String, String> typeToClassMap = new HashMap<String, String>();
+    for (TypeParameterTree param : mt.getTypeParameters()) {
+      String paramName = param.getName().toString();
+      String paramClass = "Object";
+      List<? extends Tree> paramBounds = param.getBounds();
+      if (paramBounds != null && paramBounds.size() >= 1) {
+        Tree boundZero = paramBounds.get(0);
+        if (boundZero.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+          boundZero = ((AnnotatedTypeTree) boundZero).getUnderlyingType();
+        }
+        paramClass = boundZero.toString();
+      }
+      typeToClassMap.put(paramName, paramClass);
+    }
+
+    // Do the same for the enclosing class.
+    // The type variable might not be from the directly enclosing
+    // class, but from a further up class.
+    // Go through all enclosing classes and add the type parameters.
+    {
+      TreePath classpath = path;
+      ClassTree ct = enclosingClass(classpath);
+      while (ct!=null) {
+        for (TypeParameterTree param : ct.getTypeParameters()) {
+          String paramName = param.getName().toString();
+          String paramClass = "Object";
+          List<? extends Tree> paramBounds = param.getBounds();
+          if (paramBounds != null && paramBounds.size() >= 1) {
+            Tree pb = paramBounds.get(0);
+            if (pb.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+                pb = ((AnnotatedTypeTree)pb).getUnderlyingType();
+            }
+            paramClass = pb.toString();
+          }
+          typeToClassMap.put(paramName, paramClass);
+        }
+        classpath = classpath.getParentPath();
+        ct = enclosingClass(classpath);
+      }
+    }
+
+    if (! matchTypeParams(sourceParams, typeToClassMap, context)) {
+      Criteria.dbug.debug("IsSigMethodCriterion => false: Parameter types don't match%n");
+      return false;
+    }
+
+    if ((mt.getReturnType() != null) // must be a constructor
+        && (! matchTypeParam(returnType, mt.getReturnType(), typeToClassMap, context))) {
+      Criteria.dbug.debug("IsSigMethodCriterion => false: Return types don't match%n");
+      return false;
+    }
+
+    Criteria.dbug.debug("IsSigMethodCriterion.isSatisfiedBy => true%n");
+    return true;
+  }
+
+  /* This is a copy of the method from the Checker Framework
+   * TreeUtils.enclosingClass.
+   * We cannot have a dependency on the Checker Framework.
+   * TODO: as is the case there, anonymous classes are not handled correctly.
+   */
+  private static ClassTree enclosingClass(final TreePath path) {
+    final Set<Tree.Kind> kinds = EnumSet.of(
+            Tree.Kind.CLASS,
+            Tree.Kind.ENUM,
+            Tree.Kind.INTERFACE,
+            Tree.Kind.ANNOTATION_TYPE
+    );
+    TreePath p = path;
+
+    while (p != null) {
+      Tree leaf = p.getLeaf();
+      assert leaf != null; /*nninvariant*/
+      if (kinds.contains(leaf.getKind())) {
+        return (ClassTree) leaf;
+      }
+      p = p.getParentPath();
+    }
+
+    return null;
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.SIG_METHOD;
+  }
+
+//  public static String getSignature(MethodTree mt) {
+//    String sig = mt.getName().toString().trim(); // method name, no parameters
+//    sig += "(";
+//    boolean first = true;
+//    for (VariableTree vt : mt.getParameters()) {
+//      if (!first) {
+//        sig += ",";
+//      }
+//      sig += getType(vt.getType());
+//      first = false;
+//    }
+//    sig += ")";
+//
+//    return sig;
+//  }
+//
+//  private static String getType(Tree t) {
+//    if (t.getKind() == Tree.Kind.PRIMITIVE_TYPE) {
+//      return getPrimitiveType((PrimitiveTypeTree) t);
+//    } else if (t.getKind() == Tree.Kind.IDENTIFIER) {
+//      return "L" + ((IdentifierTree) t).getName().toString();
+//    } else if (t.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+//      // don't care about generics due to erasure
+//      return getType(((ParameterizedTypeTree) t).getType());
+//    }
+//    throw new RuntimeException("unable to get type of: " + t);
+//  }
+//
+//  private static String getPrimitiveType(PrimitiveTypeTree pt) {
+//    TypeKind tk = pt.getPrimitiveTypeKind();
+//    if (tk == TypeKind.ARRAY) {
+//      return "[";
+//    } else if (tk == TypeKind.BOOLEAN) {
+//      return "Z";
+//    } else if (tk == TypeKind.BYTE) {
+//      return "B";
+//    } else if (tk == TypeKind.CHAR) {
+//      return "C";
+//    } else if (tk == TypeKind.DOUBLE) {
+//      return "D";
+//    } else if (tk == TypeKind.FLOAT) {
+//      return "F";
+//    } else if (tk == TypeKind.INT) {
+//      return "I";
+//    } else if (tk == TypeKind.LONG) {
+//      return "J";
+//    } else if (tk == TypeKind.SHORT) {
+//      return "S";
+//    }
+//
+//    throw new RuntimeException("Invalid TypeKind: " + tk);
+//  }
+
+  /*
+  private boolean isPrimitive(String s) {
+    return
+      s.equals("boolean") ||
+      s.equals("byte") ||
+      s.equals("char") ||
+      s.equals("double") ||
+      s.equals("float") ||
+      s.equals("int") ||
+      s.equals("long") ||
+      s.equals("short");
+  }
+  */
+
+  private boolean isPrimitiveLetter(String s) {
+    return
+      s.equals("Z") ||
+      s.equals("B") ||
+      s.equals("C") ||
+      s.equals("D") ||
+      s.equals("F") ||
+      s.equals("I") ||
+      s.equals("J") ||
+      s.equals("S");
+  }
+
+  /*
+  private String primitiveLetter(String s) {
+    if (s.equals("boolean")) {
+      return "Z";
+    } else if (s.equals("byte")) {
+      return "B";
+    } else if (s.equals("char")) {
+      return "C";
+    } else if (s.equals("double")) {
+      return "D";
+    } else if (s.equals("float")) {
+      return "F";
+    } else if (s.equals("int")) {
+      return "I";
+    } else if (s.equals("long")) {
+      return "J";
+    } else if (s.equals("short")) {
+      return "S";
+    } else {
+      throw new RuntimeException("IsSigMethodCriterion: unknown primitive: " + s);
+    }
+  }
+  */
+
+  @Override
+  public String toString() {
+    return "IsSigMethodCriterion: " + fullMethodName;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/LambdaCriterion.java b/annotation-file-utilities/src/annotator/find/LambdaCriterion.java
new file mode 100644
index 0000000..a81ab70
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/LambdaCriterion.java
@@ -0,0 +1,86 @@
+package annotator.find;
+
+import annotations.el.RelativeLocation;
+import annotator.scanner.LambdaScanner;
+
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class LambdaCriterion implements Criterion {
+  private final String methodName;
+  private final RelativeLocation loc;
+
+  public LambdaCriterion(String methodName, RelativeLocation loc) {
+    this.methodName = methodName;
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      Criteria.dbug.debug("return null");
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    Criteria.dbug.debug("%n%s%n", this.toString());
+    Criteria.dbug.debug("LambdaCriterion.isSatisfiedBy: %s%n", leaf);
+    Criteria.dbug.debug("leaf: %s%n", leaf);
+    Criteria.dbug.debug("kind: %s%n", leaf.getKind());
+    Criteria.dbug.debug("class: %s%n", leaf.getClass());
+
+    TreePath parentPath = path.getParentPath();
+    if (parentPath == null) {
+      Criteria.dbug.debug("return: parent path null%n");
+      return false;
+    }
+
+    Tree parent = parentPath.getLeaf();
+    if (parent == null) {
+      Criteria.dbug.debug("return: parent null%n");
+      return false;
+    }
+
+    if (parent.getKind() == Tree.Kind.LAMBDA_EXPRESSION) {
+      // LambdaExpressionTree lambdaTree = (LambdaExpressionTree) parent;
+
+      int indexInSource = LambdaScanner.indexOfLambdaExpressionTree(path, parent);
+      Criteria.dbug.debug("return source: %d%n", indexInSource);
+      boolean b;
+      if (loc.isBytecodeOffset()) {
+        int indexInClass = LambdaScanner.getMethodLambdaExpressionIndex(methodName, loc.offset);
+        Criteria.dbug.debug("return class: %d%n", indexInClass);
+        b = (indexInSource == indexInClass);
+      } else {
+        b = (indexInSource == loc.index);
+        Criteria.dbug.debug("return loc.index: %d%n", loc.index);
+      }
+      Criteria.dbug.debug("return new: %b%n", b);
+      return b;
+    } else {
+      boolean b = this.isSatisfiedBy(path.getParentPath());
+      Criteria.dbug.debug("return parent: %b%n", b);
+      return b;
+    }
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.LAMBDA_EXPRESSION;
+  }
+
+  @Override
+  public String toString() {
+    return "LambdaCriterion: at location: " + loc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/LocalVariableCriterion.java b/annotation-file-utilities/src/annotator/find/LocalVariableCriterion.java
new file mode 100644
index 0000000..cfb6a9c
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/LocalVariableCriterion.java
@@ -0,0 +1,128 @@
+package annotator.find;
+
+import java.util.List;
+
+import annotations.el.LocalLocation;
+import annotator.scanner.LocalVariableScanner;
+
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.util.Pair;
+
+/**
+ * Criterion for being a specific local variable.
+ */
+public class LocalVariableCriterion implements Criterion {
+
+  private final String fullMethodName;
+  private final LocalLocation loc;
+
+  public LocalVariableCriterion(String methodName, LocalLocation loc) {
+    this.fullMethodName = methodName.substring(0, methodName.indexOf(")") + 1);
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    TreePath parentPath = path.getParentPath();
+    if (parentPath != null) {
+      Tree parent = parentPath.getLeaf();
+      if (parent != null) {
+        if ((parent instanceof VariableTree)
+            // Avoid matching formal parameters
+            && (! (parentPath.getParentPath().getLeaf() instanceof MethodTree))) {
+          VariableTree vtt = (VariableTree) parent;
+          String varName = vtt.getName().toString();
+
+          if (loc.varName!=null && loc.varName.equals(varName)) {
+            int varIndex = LocalVariableScanner.indexOfVarTree(path, vtt, varName);
+
+            if (loc.varIndex==varIndex) {
+              // the location specifies a variable name and index and it matches the current variable
+              // -> hurray
+              return true;
+            }
+            return false;
+          }
+
+          Pair<String, Pair<Integer, Integer>> key =
+                  Pair.of(fullMethodName, Pair.of(loc.index, loc.scopeStart));
+          String potentialVarName =
+                  LocalVariableScanner.getFromMethodNameIndexMap(key);
+          if (potentialVarName != null) {
+            if (varName.equals(potentialVarName)) {
+              // now use methodNameCounter to ensure that if this is the
+              // i'th variable of this name, its offset is the i'th offset
+              // of all variables with this name
+              List<Integer> allOffsetsWithThisName =
+                      LocalVariableScanner.getFromMethodNameCounter(fullMethodName, potentialVarName);
+//                methodNameCounter.get(fullMethodName).get(potentialVarName);
+              Integer thisVariablesOffset =
+                      allOffsetsWithThisName.indexOf(loc.scopeStart);
+
+              // now you need to make sure that this is the
+              // thisVariablesOffset'th variable tree in the entire source
+              int i = LocalVariableScanner.indexOfVarTree(path, parent, potentialVarName);
+
+              if (i == thisVariablesOffset) {
+                return true;
+              }
+            }
+          }
+        } else {
+          // If present leaf does not yet satisfy the local variable
+          // criterion, note that it actually is the correct local variable
+          // if any of its parents satisfy this local variable criterion
+          // (and going all the way up past the top-level tree is taken
+          // care of by the check for null above.
+          //
+          // For example, if you have the tree for "Integer"
+          // for the local variable "List<Integer> foo;"
+          // the parent of the current leaf will satisfy the local variable
+          // criterion directly.  The fact that you will never return true
+          // for something that is not the correct local variable comes
+          // from the fact that you can't contain one local variable
+          // within another.  For example, you can't have
+          // List<Integer bar> foo;
+          // Thus, no local variable tree can contain another local
+          // variable tree.
+          // Another general example:
+          // List<Integer> foo = ...;
+          // If the tree for ... contains one local variable, there is no fear
+          // of a conflict with "List<Integer>", because "List<Integer> foo"
+          // is a subtree of "List<Integer> foo = ...;", so the two
+          // (possibly) conflicting local variable trees are both subtrees
+          // of the same tree, and neither is an ancestor of the other.
+          return this.isSatisfiedBy(parentPath);
+          // To do: should stop this once it gets to method? or some other top level?
+        }
+      }
+    }
+    return false;
+  }
+
+
+  @Override
+  public Kind getKind() {
+    return Kind.LOCAL_VARIABLE;
+  }
+
+  @Override
+  public String toString() {
+    return "LocalVariableCriterion: in: " + fullMethodName + " loc: " + loc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/MemberReferenceCriterion.java b/annotation-file-utilities/src/annotator/find/MemberReferenceCriterion.java
new file mode 100644
index 0000000..77f8ef3
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/MemberReferenceCriterion.java
@@ -0,0 +1,62 @@
+package annotator.find;
+
+import annotations.el.RelativeLocation;
+import annotator.scanner.MemberReferenceScanner;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class MemberReferenceCriterion implements Criterion {
+  private final String methodName;
+  private final RelativeLocation loc;
+
+  public MemberReferenceCriterion(String methodName, RelativeLocation loc) {
+    this.methodName = methodName;
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    if (leaf.getKind() == Tree.Kind.MEMBER_REFERENCE) {
+      int indexInSource =
+          MemberReferenceScanner.indexOfMemberReferenceTree(path, leaf);
+      boolean b;
+      if (loc.isBytecodeOffset()) {
+        int indexInClass =
+            MemberReferenceScanner.getMemberReferenceIndex(methodName,
+                loc.offset);
+        b = (indexInSource == indexInClass);
+      } else {
+        b = (indexInSource == loc.index);
+      }
+      return b;
+    } else {
+      boolean b = this.isSatisfiedBy(path.getParentPath());
+      return b;
+    }
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.METHOD_REFERENCE;
+  }
+
+  @Override
+  public String toString() {
+    return "MemberReferenceCriterion: in method: " + methodName + " location: " + loc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/MethodBoundCriterion.java b/annotation-file-utilities/src/annotator/find/MethodBoundCriterion.java
new file mode 100644
index 0000000..9be904b
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/MethodBoundCriterion.java
@@ -0,0 +1,45 @@
+package annotator.find;
+
+import annotations.el.BoundLocation;
+
+import com.sun.source.util.TreePath;
+import com.sun.source.tree.Tree;
+
+public class MethodBoundCriterion implements Criterion {
+
+  private final String methodName;
+  public final BoundLocation boundLoc;
+  private final Criterion sigMethodCriterion;
+  private final Criterion boundLocationCriterion;
+
+  public MethodBoundCriterion(String methodName, BoundLocation boundLoc) {
+    this.methodName = methodName;
+    this.boundLoc = boundLoc;
+    this.sigMethodCriterion = Criteria.inMethod(methodName);
+    this.boundLocationCriterion = Criteria.atBoundLocation(boundLoc);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    return sigMethodCriterion.isSatisfiedBy(path) &&
+      boundLocationCriterion.isSatisfiedBy(path);
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.METHOD_BOUND;
+  }
+
+  @Override
+  public String toString() {
+    return "MethodBoundCriterion: method: " + methodName + " bound boundLoc: " + boundLoc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/NewCriterion.java b/annotation-file-utilities/src/annotator/find/NewCriterion.java
new file mode 100644
index 0000000..6752f91
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/NewCriterion.java
@@ -0,0 +1,83 @@
+package annotator.find;
+
+import annotations.el.RelativeLocation;
+import annotator.scanner.NewScanner;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * Criterion for being a specific object creation expression.
+ */
+public class NewCriterion implements Criterion {
+
+  private final String methodName;
+  private final Criterion inMethodCriterion;
+
+  private final RelativeLocation loc;
+
+  public NewCriterion(String methodName, RelativeLocation loc) {
+    this.methodName = methodName.substring(0, methodName.lastIndexOf(")") + 1);
+
+    if (!(methodName.startsWith("init for field") ||
+            methodName.startsWith("static init number")
+            || methodName.startsWith("instance init number"))) {
+      // keep strings consistent with text used in IndexFileSpecification
+      this.inMethodCriterion = Criteria.inMethod(methodName);
+    } else {
+      this.inMethodCriterion = null;
+    }
+
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) {
+      return false;
+    }
+
+    Tree leaf = path.getLeaf();
+
+    if (inMethodCriterion!=null && !inMethodCriterion.isSatisfiedBy(path)) {
+      // If we're not in the method now, the parent path may still be in the method.
+      // For example, the current leaf could be inside a method inside of an
+      // anonymous inner class defined in another method.
+      return this.isSatisfiedBy(path.getParentPath());
+    }
+    if (leaf.getKind() == Tree.Kind.NEW_CLASS
+            || leaf.getKind() == Tree.Kind.NEW_ARRAY) {
+      int indexInSource = NewScanner.indexOfNewTree(path, leaf);
+      // System.out.printf("indexInSource=%d%n", indexInSource);
+      boolean b;
+      if (loc.isBytecodeOffset()) {
+        int indexInClass = NewScanner.getMethodNewIndex(methodName, loc.offset);
+        b = (indexInSource == indexInClass);
+      } else {
+        b = (indexInSource == loc.index);
+      }
+      return b;
+    } else {
+      return this.isSatisfiedBy(path.getParentPath());
+    }
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.NEW;
+  }
+
+  @Override
+  public String toString() {
+    return "NewCriterion in method: " + methodName + " at location " + loc;
+  }
+
+}
diff --git a/annotation-file-utilities/src/annotator/find/NewInsertion.java b/annotation-file-utilities/src/annotator/find/NewInsertion.java
new file mode 100644
index 0000000..a7e4678
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/NewInsertion.java
@@ -0,0 +1,119 @@
+package annotator.find;
+
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import type.DeclaredType;
+import type.Type;
+
+/**
+ * @author dbro
+ *
+ */
+public class NewInsertion extends TypedInsertion {
+  private final static Pattern qualifiers = Pattern.compile("(?:\\w++\\.)*+");
+
+  /**
+   * If true, the type will be qualified with the name of the superclass.
+   */
+  protected boolean qualifyType;
+
+  /**
+   * Construct a NewInsertion.
+   * <p>
+   * If "new" already exists in the initializer, then pass a
+   * {@link DeclaredType} thats name is the empty String. This will only
+   * insert an annotation on the existing type.
+   * <p>
+   * To insert the annotation along with "new" and the type (for example,
+   * {@code @Anno new Type[] \{...\}}), set the name to the type to insert.
+   * This can be done either before calling this constructor, or by modifying
+   * the return value of {@link #getType()}.
+   *
+   * @param type the type to use when inserting the receiver
+   * @param criteria where to insert the text
+   * @param innerTypeInsertions the inner types to go on this receiver
+   */
+  public NewInsertion(Type type, Criteria criteria,
+      List<Insertion> innerTypeInsertions) {
+    super(type, criteria, innerTypeInsertions);
+    annotationsOnly = false;
+    qualifyType = false;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected String getText(boolean comments, boolean abbreviate) {
+    if (annotationsOnly || type.getKind() != Type.Kind.ARRAY) {
+      StringBuilder b = new StringBuilder();
+      List<String> annotations = type.getAnnotations();
+      if (annotations.isEmpty()) { return ""; }
+      for (String a : annotations) {
+        b.append(' ').append(a);  // initial space removed below
+      }
+      return new AnnotationInsertion(b.substring(1), getCriteria(),
+          getSeparateLine()).getText(comments, abbreviate);
+    } else {
+      DeclaredType baseType = getBaseType();
+      boolean commentAnnotation = (comments && baseType.getName().isEmpty());
+      String result = typeToString(type, commentAnnotation, abbreviate);
+      if (!baseType.getName().isEmpty()) {
+        // First, temporarily strip off any qualifiers.
+        Matcher matcher = qualifiers.matcher(result);
+        String prefix = "";
+        if (matcher.find() && matcher.start() == 0) {
+          prefix = result.substring(0, matcher.end());
+          result = result.substring(matcher.end());
+        }
+        // If the variable name preceded the array brackets in the
+        // source, extract it from the result.
+        if (qualifyType) {
+          for (DeclaredType t = baseType; t != null; t = t.getInnerType()) {
+            result += t.getName() + ".";
+          }
+        }
+        // Finally, prepend extracted qualifiers.
+        result = prefix + result;
+      }
+      result = "new " + result;
+      if (comments) {
+        result = "/*>>> " + result + " */";
+      }
+      return result;
+    }
+  }
+
+  /**
+   * If {@code true}, qualify {@code type} with the name of the superclass.
+   * This will only happen if a "new" is inserted.
+   */
+  public void setQualifyType(boolean qualifyType) {
+    this.qualifyType = qualifyType;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected boolean addLeadingSpace(boolean gotSeparateLine, int pos,
+      char precedingChar) {
+    if ((precedingChar == '.' || precedingChar == '(')
+        && getBaseType().getName().isEmpty()) {
+      // If only the annotation is being inserted then don't insert a
+      // space if it's immediately after a '.' or '('
+      return false;
+    }
+    return super.addLeadingSpace(gotSeparateLine, pos, precedingChar);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  protected boolean addTrailingSpace(boolean gotSeparateLine) {
+    return true;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Kind getKind() {
+    return Kind.NEW;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/NotInMethodCriterion.java b/annotation-file-utilities/src/annotator/find/NotInMethodCriterion.java
new file mode 100644
index 0000000..a0a0f38
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/NotInMethodCriterion.java
@@ -0,0 +1,54 @@
+package annotator.find;
+
+import annotations.io.ASTPath;
+
+import com.sun.source.tree.*;
+import com.sun.source.util.TreePath;
+
+/**
+ * Represents the criterion that a program element is not enclosed by any
+ * method (i.e. it's a field, class type parameter, etc.).
+ */
+final class NotInMethodCriterion implements Criterion {
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public Kind getKind() {
+    return Kind.NOT_IN_METHOD;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    do {
+      Tree.Kind kind = path.getLeaf().getKind();
+      if (kind == Tree.Kind.METHOD) {
+        return false;
+      }
+      if (ASTPath.isClassEquiv(kind)) {
+        return true;
+      }
+      path = path.getParentPath();
+    } while (path != null && path.getLeaf() != null);
+
+    return true;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return "not in method";
+  }
+
+}
diff --git a/annotation-file-utilities/src/annotator/find/PackageCriterion.java b/annotation-file-utilities/src/annotator/find/PackageCriterion.java
new file mode 100644
index 0000000..dd4f3be
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/PackageCriterion.java
@@ -0,0 +1,62 @@
+package annotator.find;
+
+import annotator.Main;
+
+import com.sun.source.tree.*;
+import com.sun.source.util.TreePath;
+
+/**
+ * Represents the criterion that a program element is in a method with a
+ * certain name.
+ */
+final class PackageCriterion implements Criterion {
+
+  private final String name;
+
+  PackageCriterion(String name) {
+    this.name = name;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public Kind getKind() {
+    return Kind.PACKAGE;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree tree) {
+    assert path == null || path.getLeaf() == tree;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    Tree tree = path.getLeaf();
+    Criteria.dbug.debug("PackageCriterion.isSatisfiedBy(%s, %s); this=%s%n",
+        Main.pathToString(path), tree, this.toString());
+
+    if (tree.getKind() == Tree.Kind.COMPILATION_UNIT) {
+      CompilationUnitTree cu = (CompilationUnitTree)tree;
+      if (cu.getSourceFile().getName().endsWith("package-info.java")) {
+        ExpressionTree pn = cu.getPackageName();
+        assert ((pn instanceof IdentifierTree)
+                || (pn instanceof MemberSelectTree));
+        if (this.name.equals(pn.toString())) {
+          return true;
+        }
+      }
+    }
+    Criteria.dbug.debug("PackageCriterion.isSatisfiedBy => false%n");
+    return false;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public String toString() {
+    return "package '" + name + "'";
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/ParamCriterion.java b/annotation-file-utilities/src/annotator/find/ParamCriterion.java
new file mode 100644
index 0000000..fea9022
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ParamCriterion.java
@@ -0,0 +1,71 @@
+package annotator.find;
+
+import java.util.List;
+
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+
+public class ParamCriterion implements Criterion {
+
+  private final String methodName;
+  private final Integer paramPos;
+
+  public ParamCriterion(String methodName, Integer pos) {
+    this.methodName = methodName.substring(0, methodName.indexOf(")") + 1);
+    this.paramPos = pos;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+
+    if (path == null) {
+      return false;
+    }
+
+    // no inner type location, want to annotate outermost type
+    // i.e.   @Nullable List list;
+    //        @Nullable List<String> list;
+    Tree leaf = path.getLeaf();
+    if (leaf instanceof VariableTree) {
+      Tree parent = path.getParentPath().getLeaf();
+      List<? extends VariableTree> params;
+      switch (parent.getKind()) {
+      case METHOD:
+        params = ((MethodTree) parent).getParameters();
+        break;
+      case LAMBDA_EXPRESSION:
+        params = ((LambdaExpressionTree) parent).getParameters();
+        break;
+      default:
+        params = null;
+        break;
+      }
+      return params != null && params.size() > paramPos
+          && params.get(paramPos).equals(leaf);
+    }
+
+    return this.isSatisfiedBy(path.getParentPath());
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.PARAM;
+  }
+
+  @Override
+  public String toString() {
+    return "ParamCriterion for method: " + methodName + " at position: " +
+            paramPos;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/ReceiverCriterion.java b/annotation-file-utilities/src/annotator/find/ReceiverCriterion.java
new file mode 100644
index 0000000..a97db08
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ReceiverCriterion.java
@@ -0,0 +1,80 @@
+package annotator.find;
+
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class ReceiverCriterion implements Criterion {
+
+  private final String methodName; // no return type
+  private final Criterion isSigMethodCriterion;
+
+  public ReceiverCriterion(String methodName) {
+    this.methodName = methodName;
+    isSigMethodCriterion = Criteria.isSigMethod(methodName);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    // want to annotate BlockTree returned by MethodTree.getBody();
+    if (path == null) {
+      return false;
+    }
+
+    if (path.getLeaf().getKind() == Tree.Kind.METHOD) {
+      if (isSigMethodCriterion.isSatisfiedBy(path)) {
+          MethodTree leaf = (MethodTree) path.getLeaf();
+          // If the method already has a receiver, then insert directly on the
+          // receiver, not on the method.
+          return leaf.getReceiverParameter() == null;
+      }
+      return false;
+    } else {
+      // We may be attempting to insert an annotation on a type parameter of an
+      // existing receiver, so make sure this is the right receiver parameter:
+      // work up the tree to find the method declaration. Store the parameter we
+      // pass through up to the method declaration so we can make sure we came up
+      // through the receiver. Then check to make sure this is the correct method
+      // declaration.
+      Tree param = null;
+      TreePath parent = path;
+      while (parent != null && parent.getLeaf().getKind() != Tree.Kind.METHOD) {
+        if (parent.getLeaf().getKind() == Tree.Kind.VARIABLE) {
+          if (param == null) {
+            param = parent.getLeaf();
+          } else {
+            // The only variable we should pass through is the receiver parameter.
+            // If we pass through more than one then this isn't the right place.
+            return false;
+          }
+        }
+        parent = parent.getParentPath();
+      }
+      if (parent != null && param != null) {
+        MethodTree method = (MethodTree) parent.getLeaf();
+        if (param == method.getReceiverParameter()) {
+          return isSigMethodCriterion.isSatisfiedBy(parent);
+        }
+      }
+      return false;
+    }
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.RECEIVER;
+  }
+
+  @Override
+  public String toString() {
+    return "ReceiverCriterion for method: " + methodName;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/ReceiverInsertion.java b/annotation-file-utilities/src/annotator/find/ReceiverInsertion.java
new file mode 100644
index 0000000..5c31488
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ReceiverInsertion.java
@@ -0,0 +1,138 @@
+package annotator.find;
+
+import java.util.List;
+
+import type.DeclaredType;
+
+/**
+ * An insertion for a method receiver. This supports inserting an
+ * annotation on an existing receiver and creating a new (annotated)
+ * receiver if none are present.
+ */
+public class ReceiverInsertion extends TypedInsertion {
+    /**
+     * If true a comma will be added at the end of the insertion (only if also
+     * inserting the receiver).
+     */
+    private boolean addComma;
+
+    /**
+     * If true, {@code this} will be qualified with the name of the
+     * superclass.
+     */
+    private boolean qualifyThis;
+
+    /**
+     * Construct a ReceiverInsertion.
+     * <p>
+     * If the receiver parameter already exists in the method declaration, then
+     * pass a DeclaredType thats name is the empty String. This will only insert
+     * an annotation on the existing receiver.
+     * <p>
+     * To insert the annotation and the receiver (for example,
+     * {@code @Anno Type this}) the name should be set to the type to insert.
+     * This can either be done before calling this constructor, or by modifying
+     * the return value of {@link #getType()}.
+     * <p>
+     * A comma will not be added to the end of the receiver. In the case that
+     * there is a parameter following the inserted receiver pass {@code true} to
+     * {@link #setAddComma(boolean)} to add a comma to the end of the receiver.
+     *
+     * @param type the type to use when inserting the receiver
+     * @param criteria where to insert the text
+     * @param innerTypeInsertions the inner types to go on this receiver
+     */
+    public ReceiverInsertion(DeclaredType type, Criteria criteria, List<Insertion> innerTypeInsertions) {
+        super(type, criteria, innerTypeInsertions);
+        addComma = false;
+        qualifyThis = false;
+    }
+
+    /**
+     * If {@code true} a comma will be added at the end of the receiver.
+     * This will only happen if a receiver is inserted (see
+     * {@link #ReceiverInsertion(DeclaredType, Criteria, List)} for a description of
+     * when a receiver is inserted). This is useful if the method already has
+     * one or more parameters.
+     */
+    public void setAddComma(boolean addComma) {
+        this.addComma = addComma;
+    }
+
+    /**
+     * If {@code true}, qualify {@code this} with the name of the superclass.
+     * This will only happen if a receiver is inserted (see
+     * {@link #ReceiverInsertion(DeclaredType, Criteria, List)}
+     * for a description of when a receiver is inserted). This is useful
+     * for inner class constructors.
+     */
+    public void setQualifyType(boolean qualifyThis) {
+        this.qualifyThis = qualifyThis;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected String getText(boolean comments, boolean abbreviate) {
+      if (annotationsOnly) {
+        StringBuilder b = new StringBuilder();
+        List<String> annotations = type.getAnnotations();
+        if (annotations.isEmpty()) { return ""; }
+        for (String a : annotations) {
+            b.append(a);
+            b.append(' ');
+        }
+        return new AnnotationInsertion(b.toString(), getCriteria(),
+                getSeparateLine()).getText(comments, abbreviate);
+      } else {
+        DeclaredType baseType = getBaseType();
+        boolean commentAnnotation = (comments && baseType.getName().isEmpty());
+        String result = typeToString(type, commentAnnotation, abbreviate);
+        if (!baseType.getName().isEmpty()) {
+            result += " ";
+            if (qualifyThis) {
+                for (DeclaredType t = baseType; t != null;
+                        t = t.getInnerType()) {
+                    result += t.getName() + ".";
+                }
+            }
+            result += "this";
+            if (addComma) {
+                result += ",";
+            }
+            if (comments) {
+                result = "/*>>> " + result + " */";
+            }
+        }
+        return result;
+      }
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean addLeadingSpace(boolean gotSeparateLine, int pos,
+            char precedingChar) {
+        if (precedingChar == '.' && getBaseType().getName().isEmpty()) {
+            // If only the annotation is being inserted then don't insert a
+            // space if it's immediately after a '.'
+            return false;
+        }
+        return super.addLeadingSpace(gotSeparateLine, pos, precedingChar);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected boolean addTrailingSpace(boolean gotSeparateLine) {
+        // If the type is not already in the source and the receiver is the only
+        // parameter, don't add a trailing space.
+        if (!getBaseType().getName().isEmpty() && !addComma) {
+            return false;
+        }
+        return super.addTrailingSpace(gotSeparateLine);
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Kind getKind() {
+        return Kind.RECEIVER;
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/find/ReturnTypeCriterion.java b/annotation-file-utilities/src/annotator/find/ReturnTypeCriterion.java
new file mode 100644
index 0000000..e559927
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/ReturnTypeCriterion.java
@@ -0,0 +1,70 @@
+package annotator.find;
+
+import annotator.Main;
+import annotator.scanner.CommonScanner;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class ReturnTypeCriterion implements Criterion {
+
+  private final String methodName;
+  private final Criterion inClassCriterion;
+  private final Criterion sigMethodCriterion;
+
+  public ReturnTypeCriterion(String className, String methodName) {
+    this.methodName = methodName;
+    this.inClassCriterion = Criteria.inClass(className, false);
+    this.sigMethodCriterion = methodName.isEmpty() ? null
+        : Criteria.isSigMethod(methodName);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null) { return false; }
+
+    Criteria.dbug.debug("ReturnTypeCriterion.isSatisfiedBy(%s); this=%n",
+        Main.pathToString(path), this.toString());
+
+    do {
+      if (path.getLeaf().getKind() == Tree.Kind.METHOD) {
+        if (sigMethodCriterion == null
+            || sigMethodCriterion.isSatisfiedBy(path)) {
+          // Method and return type verified; now check class.
+          path = path.getParentPath();
+          while (path != null && path.getLeaf() != null) {
+            if (CommonScanner.hasClassKind(path.getLeaf())) {
+              if (!inClassCriterion.isSatisfiedBy(path)) { break; }
+              Criteria.dbug.debug("ReturnTypeCriterion.isSatisfiedBy => true%n");
+              return true;
+            }
+            path = path.getParentPath();
+          }
+        }
+        break;
+      }
+      path = path.getParentPath();
+    } while (path != null && path.getLeaf() != null);
+
+    Criteria.dbug.debug("ReturnTypeCriterion.isSatisfiedBy => false%n");
+    return false;
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.RETURN_TYPE;
+  }
+
+  @Override
+  public String toString() {
+    return "ReturnTypeCriterion for method: " + methodName;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/TreeFinder.java b/annotation-file-utilities/src/annotator/find/TreeFinder.java
new file mode 100644
index 0000000..47443e8
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/TreeFinder.java
@@ -0,0 +1,1847 @@
+package annotator.find;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.type.NullType;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimaps;
+import com.google.common.collect.SetMultimap;
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.IdentifierTree;
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ModifiersTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.PrimitiveTypeTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.WildcardTree;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreeScanner;
+
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind;
+import com.sun.tools.javac.code.Types;
+import com.sun.tools.javac.tree.JCTree;
+import com.sun.tools.javac.tree.JCTree.JCAnnotatedType;
+import com.sun.tools.javac.tree.JCTree.JCAnnotation;
+import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+import com.sun.tools.javac.tree.JCTree.JCClassDecl;
+import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCFieldAccess;
+import com.sun.tools.javac.tree.JCTree.JCIdent;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCModifiers;
+import com.sun.tools.javac.tree.JCTree.JCNewArray;
+import com.sun.tools.javac.tree.JCTree.JCNewClass;
+import com.sun.tools.javac.tree.JCTree.JCTypeApply;
+import com.sun.tools.javac.tree.JCTree.JCTypeParameter;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.tree.JCTree.JCWildcard;
+import com.sun.tools.javac.util.Position;
+
+import annotations.io.ASTIndex;
+import annotations.io.ASTPath;
+import annotations.io.ASTRecord;
+import annotations.io.DebugWriter;
+import annotator.Main;
+import annotator.scanner.CommonScanner;
+import annotator.specification.IndexFileSpecification;
+
+import plume.Pair;
+
+import type.DeclaredType;
+import type.Type;
+
+/**
+ * A {@link TreeScanner} that is able to locate program elements in an
+ * AST based on {@code Criteria}. {@link #getInsertionsByPosition(JCTree.JCCompilationUnit,List)}
+ * scans a tree and returns a
+ * mapping of source positions (as character offsets) to insertion text.
+ */
+public class TreeFinder extends TreeScanner<Void, List<Insertion>> {
+  public static final DebugWriter dbug = new DebugWriter();
+  public static final DebugWriter stak = new DebugWriter();
+  public static final DebugWriter warn = new DebugWriter();
+
+  /**
+   * String representation of regular expression matching a comment in
+   * Java code.  The part before {@code |} matches a single-line
+   * comment, and the part after matches a multi-line comment, which
+   * breaks down as follows (adapted from
+   * <a href="http://perldoc.perl.org/perlfaq6.html#How-do-I-use-a-regular-expression-to-strip-C-style-comments-from-a-file%3f">Perl FAQ</a>):
+   * <pre>
+   *          /\*         ##  Start of comment
+   *          [^*]*\*+    ##  Non-* followed by 1-or-more *s
+   *          (
+   *              [^/*][^*]*\*+
+   *          )*          ##  0 or more things which don't start with /
+   *                      ##    but do end with '*'
+   *          /           ##  End of comment
+   * </pre>
+   * Note: Care must be taken to avoid false comment matches starting
+   * inside a string literal.  Ensuring that the code segment being
+   * matched starts at an AST node boundary is sufficient to prevent
+   * this complication.
+   */
+  private final static String comment =
+      "//.*$|/\\*[^*]*+\\*++(?:[^*/][^*]*+\\*++)*+/";
+
+  /**
+   * Regular expression matching a character or string literal.
+   */
+  private final static String literal =
+      "'(?:(?:\\\\(?:'|[^']*+))|[^\\\\'])'|\"(?:\\\\.|[^\\\\\"])*\"";
+
+  /**
+   * Regular expression matching a non-commented instance of {@code /}
+   * that is not part of a comment-starting delimiter.
+   */
+  private final static String nonDelimSlash = "/(?=[^*/])";
+
+  /**
+   * Returns regular expression matching "anything but" {@code c}: a
+   * single comment, character or string literal, or non-{@code c}
+   * character.
+   */
+  private final static String otherThan(char c) {
+    String cEscaped;
+
+    // escape if necessary for use in character class
+    switch (c) {
+    case '/':
+    case '"':
+    case '\'':
+      cEscaped = ""; break;  // already present in class defn
+    case '\\':
+    case '[':
+    case ']':
+      cEscaped = "\\" + c; break;  // escape!
+    default:
+      cEscaped = "" + c;
+    }
+
+    return "[^/'" + cEscaped + "\"]|" + "|" + literal + "|" + comment
+        + (c == '/' ? "" : nonDelimSlash);
+  }
+
+  // If this code location is not an array type, return null.  Otherwise,
+  // starting at an array type, walk up the AST as long as still an array,
+  // and stop at the largest containing array (with nothing but arrays in
+  // between).
+  public static TreePath largestContainingArray(TreePath p) {
+    if (p.getLeaf().getKind() != Tree.Kind.ARRAY_TYPE) {
+      return null;
+    }
+    while (p.getParentPath().getLeaf().getKind() == Tree.Kind.ARRAY_TYPE) {
+      p = p.getParentPath();
+    }
+    assert p.getLeaf().getKind() == Tree.Kind.ARRAY_TYPE;
+    return p;
+  }
+
+  /**
+   * Returns the position of the first (non-commented) instance of a
+   * character at or after the given position.  (Assumes position is not
+   * inside a comment.)
+   *
+   * @see #getNthInstanceBetween(char, int, int, int, CompilationUnitTree)
+   */
+  private int getFirstInstanceAfter(char c, int i) {
+    return getNthInstanceInRange(c, i, Integer.MAX_VALUE, 1);
+  }
+
+  /**
+   * Returns the position of the {@code n}th (non-commented, non-quoted)
+   * instance of a character between the given positions, or the last
+   * instance if {@code n==0}.  (Assumes position is not inside a
+   * comment.)
+   *
+   * @param c the character being sought
+   * @param start position at which the search starts (inclusive)
+   * @param end position at which the search ends (exclusive)
+   * @param n number of repetitions, or 0 for last occurrence
+   * @return position of match in {@code tree}, or
+   *          {@link Position.NOPOS} if match not found
+   */
+  private int getNthInstanceInRange(char c, int start, int end, int n) {
+    if (end < 0) {
+      throw new IllegalArgumentException("negative end position");
+    }
+    if (n < 0) {
+      throw new IllegalArgumentException("negative count");
+    }
+
+    try {
+      CharSequence s = tree.getSourceFile().getCharContent(true);
+      int count = n;
+      int pos = Position.NOPOS;
+      int stop = Math.min(end, s.length());
+      String cQuoted = c == '/' ? nonDelimSlash : Pattern.quote("" + c);
+      String regex = "(?:" + otherThan(c) + ")*+" + cQuoted;
+      Pattern p = Pattern.compile(regex, Pattern.MULTILINE);
+      Matcher m = p.matcher(s).region(start, stop);
+
+      // using n==0 for "last" ensures that {@code (--n == 0)} is always
+      // false, (reasonably) assuming no underflow
+      while (m.find()) {
+        pos = m.end() - 1;
+        if (--count == 0) { break; }
+      }
+      // positive count means search halted before nth instance was found
+      return count > 0 ? Position.NOPOS : pos;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  // Find a node's parent in the current source tree.
+  private Tree parent(Tree node) {
+    return getPath(node).getParentPath().getLeaf();
+  }
+
+  /**
+   * An alternative to TreePath.getPath(CompilationUnitTree,Tree) that
+   * caches its results.
+   */
+  public TreePath getPath(Tree target) {
+    if (treePathCache.containsKey(target)) {
+      return treePathCache.get(target);
+    }
+    TreePath result = TreePath.getPath(tree, target);
+    treePathCache.put(target, result);
+    return result;
+  }
+
+  Map<Tree, TreePath> treePathCache = new HashMap<Tree, TreePath>();
+
+  private ASTRecord astRecord(Tree node) {
+    Map<Tree, ASTRecord> index = ASTIndex.indexOf(tree);
+    return index.get(node);
+  }
+
+  /**
+   * Determines the insertion position for type annotations on various
+   * elements.  For instance, type annotations for a declaration should be
+   * placed before the type rather than the variable name.
+   */
+  private class TypePositionFinder
+  extends TreeScanner<Pair<ASTRecord, Integer>, Insertion> {
+    private Pair<ASTRecord, Integer> pathAndPos(JCTree t) {
+      return Pair.of(astRecord(t), t.pos);
+    }
+
+    private Pair<ASTRecord, Integer> pathAndPos(JCTree t, int i) {
+      return Pair.of(astRecord(t), i);
+    }
+
+    /** @param t an expression for a type */
+    private Pair<ASTRecord, Integer> getBaseTypePosition(JCTree t) {
+      while (true) {
+        switch (t.getKind()) {
+        case IDENTIFIER:
+        case PRIMITIVE_TYPE:
+          return pathAndPos(t);
+        case MEMBER_SELECT:
+          JCTree exp = t;
+          do {  // locate pkg name, if any
+            JCFieldAccess jfa = (JCFieldAccess) exp;
+            exp = jfa.getExpression();
+            if (jfa.sym.isStatic()) {
+              return pathAndPos(exp,
+                  getFirstInstanceAfter('.',
+                      exp.getEndPosition(tree.endPositions)) + 1);
+            }
+          } while (exp instanceof JCFieldAccess
+              && ((JCFieldAccess) exp).sym.getKind() != ElementKind.PACKAGE);
+          if (exp != null) {
+            if (exp.getKind() == Tree.Kind.IDENTIFIER) {
+              Symbol sym = ((JCIdent) exp).sym;
+              if (!(sym.isStatic() || sym.getKind() == ElementKind.PACKAGE)) {
+                return pathAndPos(t, t.getStartPosition());
+              }
+            }
+            t = exp;
+          }
+          return pathAndPos(t,
+              getFirstInstanceAfter('.',
+                  t.getEndPosition(tree.endPositions)) + 1);
+        case ARRAY_TYPE:
+          t = ((JCArrayTypeTree) t).elemtype;
+          break;
+        case PARAMETERIZED_TYPE:
+          return pathAndPos(t, t.getStartPosition());
+        case EXTENDS_WILDCARD:
+        case SUPER_WILDCARD:
+          t = ((JCWildcard) t).inner;
+          break;
+        case UNBOUNDED_WILDCARD:
+          // This is "?" as in "List<?>".  ((JCWildcard) t).inner is null.
+          // There is nowhere to attach the annotation, so for now return
+          // the "?" tree itself.
+          return pathAndPos(t);
+        case ANNOTATED_TYPE:
+          // If this type already has annotations on it, get the underlying
+          // type, without annotations.
+          t = ((JCAnnotatedType) t).underlyingType;
+          break;
+        default:
+          throw new RuntimeException(String.format("Unrecognized type (kind=%s, class=%s): %s", t.getKind(), t.getClass(), t));
+        }
+      }
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitVariable(VariableTree node, Insertion ins) {
+      Name name = node.getName();
+      JCVariableDecl jn = (JCVariableDecl) node;
+      JCTree jt = jn.getType();
+      Criteria criteria = ins.getCriteria();
+      dbug.debug("visitVariable: %s %s%n", jt, jt.getClass());
+      if (name != null && criteria.isOnFieldDeclaration()) {
+        return Pair.of(astRecord(node), jn.getStartPosition());
+      }
+      if (jt instanceof JCTypeApply) {
+        JCExpression type = ((JCTypeApply) jt).clazz;
+        return pathAndPos(type);
+      }
+      return Pair.of(astRecord(node), jn.pos);
+    }
+
+    // When a method is visited, it is visited for the receiver, not the
+    // return value and not the declaration itself.
+    @Override
+    public Pair<ASTRecord, Integer> visitMethod(MethodTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitMethod%n");
+      super.visitMethod(node, ins);
+
+      JCMethodDecl jcnode = (JCMethodDecl) node;
+      JCVariableDecl jcvar = (JCVariableDecl) node.getReceiverParameter();
+      if (jcvar != null) { return pathAndPos(jcvar); }
+
+      int pos = Position.NOPOS;
+      ASTRecord astPath = astRecord(jcnode)
+          .extend(Tree.Kind.METHOD, ASTPath.PARAMETER, -1);
+
+      if (node.getParameters().isEmpty()) {
+        // no parameters; find first (uncommented) '(' after method name
+        pos = findMethodName(jcnode);
+        if (pos >= 0) { pos = getFirstInstanceAfter('(', pos); }
+        if (++pos <= 0) {
+          throw new RuntimeException("Couldn't find param opening paren for: "
+              + jcnode);
+        }
+      } else {
+        pos = ((JCTree) node.getParameters().get(0)).getStartPosition();
+      }
+      return Pair.of(astPath, pos);
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitIdentifier(IdentifierTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitIdentifier(%s)%n", node);
+      // for arrays, need to indent inside array, not right before type
+      ASTRecord rec = ASTIndex.indexOf(tree).get(node);
+      ASTPath astPath = ins.getCriteria().getASTPath();
+      Tree parent = parent(node);
+      Integer i = null;
+      JCIdent jcnode = (JCIdent) node;
+
+      // ASTPathEntry.type _n_ is a special case because it does not
+      // correspond to a node in the AST.
+      if (parent.getKind() == Tree.Kind.NEW_ARRAY) { // NewArrayTree)
+        ASTPath.ASTEntry entry;
+        dbug.debug("TypePositionFinder.visitIdentifier: recognized array%n");
+        if (astPath == null) {
+          entry = new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, 0);
+          astPath = astRecord(parent).extend(entry).astPath;
+        } else {
+          entry = astPath.get(astPath.size() - 1);  // kind is NewArray
+        }
+        if (entry.childSelectorIs(ASTPath.TYPE)) {
+          int n = entry.getArgument();
+          i = jcnode.getStartPosition();
+          if (n < getDimsSize((JCExpression) parent)) {  // else n == #dims
+            i = getNthInstanceInRange('[', i,
+                ((JCNewArray) parent).getEndPosition(tree.endPositions), n+1);
+          }
+        }
+        if (i == null) {
+          i = jcnode.getEndPosition(tree.endPositions);
+        }
+      } else if (parent.getKind() == Tree.Kind.NEW_CLASS) { // NewClassTree)
+        dbug.debug("TypePositionFinder.visitIdentifier: recognized class%n");
+        JCNewClass nc = (JCNewClass) parent;
+        dbug.debug(
+            "TypePositionFinder.visitIdentifier: clazz %s (%d) constructor %s%n",
+            nc.clazz, nc.clazz.getPreferredPosition(), nc.constructor);
+        i = nc.clazz.getPreferredPosition();
+        if (astPath == null) {
+          astPath = astRecord(node).astPath;
+        }
+      } else {
+        ASTRecord astRecord = astRecord(node);
+        astPath = astRecord.astPath;
+        i = ((JCIdent) node).pos;
+      }
+
+      dbug.debug("visitIdentifier(%s) => %d where parent (%s) = %s%n",
+          node, i, parent.getClass(), parent);
+      return Pair.of(rec.replacePath(astPath), i);
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitMemberSelect(MemberSelectTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitMemberSelect(%s)%n", node);
+      JCFieldAccess raw = (JCFieldAccess) node;
+      return Pair.of(astRecord(node),
+          raw.getEndPosition(tree.endPositions) - raw.name.length());
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitTypeParameter(TypeParameterTree node, Insertion ins) {
+      JCTypeParameter tp = (JCTypeParameter) node;
+      return Pair.of(astRecord(node), tp.getStartPosition());
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitWildcard(WildcardTree node, Insertion ins) {
+      JCWildcard wc = (JCWildcard) node;
+      return Pair.of(astRecord(node), wc.getStartPosition());
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitPrimitiveType(PrimitiveTypeTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitPrimitiveType(%s)%n", node);
+      return pathAndPos((JCTree) node);
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitParameterizedType(ParameterizedTypeTree node, Insertion ins) {
+      Tree parent = parent(node);
+      dbug.debug("TypePositionFinder.visitParameterizedType %s parent=%s%n",
+          node, parent);
+      Integer pos = getBaseTypePosition(((JCTypeApply) node).getType()).b;
+      return Pair.of(astRecord(node), pos);
+    }
+
+    /**
+     * Returns the number of array levels that are in the given array type tree,
+     * or 0 if the given node is not an array type tree.
+     */
+    private int arrayLevels(com.sun.tools.javac.code.Type t) {
+      return t.accept(new Types.SimpleVisitor<Integer, Integer>() {
+        @Override
+        public Integer visitArrayType(com.sun.tools.javac.code.Type.ArrayType t,
+            Integer i) {
+          return t.elemtype.accept(this, i+1);
+        }
+        @Override
+        public Integer visitType(com.sun.tools.javac.code.Type t, Integer i) {
+          return i;
+        }
+      }, 0);
+    }
+
+    private int arrayLevels(Tree node) {
+      int result = 0;
+      while (node.getKind() == Tree.Kind.ARRAY_TYPE) {
+        result++;
+        node = ((ArrayTypeTree) node).getType();
+      }
+      return result;
+    }
+
+    private JCTree arrayContentType(JCArrayTypeTree att) {
+      JCTree node = att;
+      do {
+        node = ((JCArrayTypeTree) node).getType();
+      } while (node.getKind() == Tree.Kind.ARRAY_TYPE);
+      return node;
+    }
+
+    private ArrayTypeTree largestContainingArray(Tree node) {
+      TreePath p = getPath(node);
+      Tree result = TreeFinder.largestContainingArray(p).getLeaf();
+      assert result.getKind() == Tree.Kind.ARRAY_TYPE;
+      return (ArrayTypeTree) result;
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitArrayType(ArrayTypeTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitArrayType(%s)%n", node);
+      JCArrayTypeTree att = (JCArrayTypeTree) node;
+      dbug.debug("TypePositionFinder.visitArrayType(%s) preferred = %s%n",
+          node, att.getPreferredPosition());
+      // If the code has a type like "String[][][]", then this gets called
+      // three times:  for String[][][], String[][], and String[]
+      // respectively.  For each of the three, call String[][][] "largest".
+      ArrayTypeTree largest = largestContainingArray(node);
+      int largestLevels = arrayLevels(largest);
+      int levels = arrayLevels(node);
+      int start = arrayContentType(att).getPreferredPosition() + 1;
+      int end = att.getEndPosition(tree.endPositions);
+      int pos = arrayInsertPos(start, end);
+
+      dbug.debug("  levels=%d largestLevels=%d%n", levels, largestLevels);
+      for (int i=levels; i<largestLevels; i++) {
+        pos = getFirstInstanceAfter('[', pos+1);
+        dbug.debug("  pos %d at i=%d%n", pos, i);
+      }
+      return Pair.of(astRecord(node), pos);
+    }
+
+    /**
+     * Find position in source code where annotation is to be inserted.
+     *
+     * @param start beginning of range to be matched
+     * @param end end of range to be matched
+     *
+     * @return position for annotation insertion
+     */
+    private int arrayInsertPos(int start, int end) {
+      try {
+        CharSequence s = tree.getSourceFile().getCharContent(true);
+        int pos = getNthInstanceInRange('[', start, end, 1);
+
+        if (pos < 0) {
+          // no "[", so check for "..."
+          String nonDot = otherThan('.');
+          String regex = "(?:(?:\\.\\.?)?" + nonDot + ")*(\\.\\.\\.)";
+          Pattern p = Pattern.compile(regex, Pattern.MULTILINE);
+          Matcher m = p.matcher(s).region(start, end);
+
+          if (m.find()) {
+            pos = m.start(1);
+          }
+          if (pos < 0) {  // should never happen
+            throw new RuntimeException("no \"[\" or \"...\" in array type");
+          }
+        }
+        return pos;
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitCompilationUnit(CompilationUnitTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitCompilationUnit%n");
+      JCCompilationUnit cu = (JCCompilationUnit) node;
+      return Pair.of(astRecord(node), cu.getStartPosition());
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitClass(ClassTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitClass%n");
+      JCClassDecl cd = (JCClassDecl) node;
+      JCTree t = cd.mods == null ? cd : cd.mods;
+      return Pair.of(astRecord(cd), t.getPreferredPosition());
+    }
+
+    // There are three types of array initializers:
+    //   /*style 1*/ String[] names1 = new String[12];
+    //   /*style 2*/ String[] names2 = { "Alice", "Bob" };
+    //   /*style 3*/ String[] names3 = new String[] { "Alice", "Bob" };
+    // (Can the styles be combined?)
+    //
+    // For style 1, we can just find the location of the
+    // dimensionality expression, and then locate the bracket before it.
+    // For style 2, annotations are impossible.
+    // For style 3, we need to count the brackets and get to the right one.
+    //
+    // The AST depth of the initializer is correct unless all arrays are
+    // empty, in which case it is arbitary.  This is legal:
+    // String[][][][][] names4 = new String[][][][][] { { { } } };
+    //
+    // Array initializers can also be multi-dimensional, but this is not
+    // relevant to us:
+    //   int[][] pascalsTriangle = { { 1 }, { 1,1 }, { 1,2,1 } };
+    //   int[][] pascalsTriangle = new int[][] { { 1 }, { 1,1 }, { 1,2,1 } };
+
+    // structure stolen from javac's Pretty.java
+    private int getDimsSize(JCExpression tree) {
+      if (tree instanceof JCNewArray) {
+        JCNewArray na = (JCNewArray) tree;
+        if (na.dims.size() != 0) {
+          // when not all dims are given, na.dims.size() gives wrong answer
+          return arrayLevels(na.type);
+        }
+        if (na.elemtype != null) {
+          return getDimsSize(na.elemtype) + 1;
+        }
+        assert na.elems != null;
+        int maxDimsSize = 0;
+        for (JCExpression elem : na.elems) {
+          if (elem instanceof JCNewArray) {
+            int elemDimsSize = getDimsSize((JCNewArray)elem);
+            maxDimsSize = Math.max(maxDimsSize, elemDimsSize);
+          } else if (elem instanceof JCArrayTypeTree) {
+            // Does this ever happen?  javac's Pretty.java handles it.
+            System.out.printf("JCArrayTypeTree: %s%n", elem);
+          }
+        }
+        return maxDimsSize + 1;
+      } else if (tree instanceof JCAnnotatedType) {
+        return getDimsSize(((JCAnnotatedType) tree).underlyingType);
+      } else if (tree instanceof JCArrayTypeTree) {
+        return 1 + getDimsSize(((JCArrayTypeTree) tree).elemtype);
+      } else {
+        return 0;
+      }
+    }
+
+
+    // Visit an expression of one of these forms:
+    //   new int[5][10]
+    //   new int[][] {...}
+    //   { ... }            -- as in: String[] names2 = { "Alice", "Bob" };
+    @Override
+    public Pair<ASTRecord, Integer> visitNewArray(NewArrayTree node, Insertion ins) {
+      dbug.debug("TypePositionFinder.visitNewArray%n");
+      JCNewArray na = (JCNewArray) node;
+      GenericArrayLocationCriterion galc =
+          ins.getCriteria().getGenericArrayLocation();
+      ASTRecord rec = ASTIndex.indexOf(tree).get(node);
+      ASTPath astPath = ins.getCriteria().getASTPath();
+      String childSelector = null;
+      // Invariant:  na.dims.size() == 0  or  na.elems == null  (but not both)
+      // If na.dims.size() != 0, na.elemtype is non-null.
+      // If na.dims.size() == 0, na.elemtype may be null or non-null.
+      int dimsSize = getDimsSize(na);
+      int dim = galc == null ? 0 : galc.getLocation().size();
+
+      if (astPath == null) {
+        astPath = astRecord(node).astPath.extendNewArray(dim);
+        childSelector = ASTPath.TYPE;
+      } else {
+        ASTPath.ASTEntry lastEntry = null;
+        int n = astPath.size();
+        int i = n;
+        // find matching node = last path entry w/kind NEW_ARRAY
+        while (--i >= 0) {
+          lastEntry = astPath.get(i);
+          if (lastEntry.getTreeKind() == Tree.Kind.NEW_ARRAY) { break; }
+        }
+        assert i >= 0 : "no matching path entry (kind=NEW_ARRAY)";
+        if (n > i+1) {
+          // find correct node further down and visit if present
+          assert dim + 1 == dimsSize;
+          Tree typeTree = na.elemtype;
+          int j = i + dim + 1;
+          while (--dim >= 0) {
+            typeTree = ((ArrayTypeTree) typeTree).getType();
+          }
+loop:
+          while (j < n) {
+            ASTPath.ASTEntry entry = astPath.get(j);
+            switch (entry.getTreeKind()) {
+            case ANNOTATED_TYPE:
+              typeTree = ((AnnotatedTypeTree) typeTree).getUnderlyingType();
+              continue;  // no increment
+            case ARRAY_TYPE:
+              typeTree = ((ArrayTypeTree) typeTree).getType();
+              break;
+            case MEMBER_SELECT:
+              if (typeTree instanceof JCTree.JCFieldAccess) {
+                JCTree.JCFieldAccess jfa = (JCTree.JCFieldAccess) typeTree;
+                typeTree = jfa.getExpression();
+                // if just a qualifier, don't increment loop counter
+                if (jfa.sym.getKind() == ElementKind.PACKAGE) { continue; }
+                break;
+              }
+              break loop;
+            case PARAMETERIZED_TYPE:
+              if (entry.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
+                int arg = entry.getArgument();
+                List<? extends Tree> typeArgs =
+                    ((ParameterizedTypeTree) typeTree).getTypeArguments();
+                typeTree = typeArgs.get(arg);
+              } else {  // ASTPath.TYPE
+                typeTree = ((ParameterizedTypeTree) typeTree).getType();
+              }
+              break;
+            default:
+              break loop;
+            }
+            ++j;
+          }
+          if (j < n) {
+            // sought node is absent, so return default; insertion can
+            // be applied only as an inner of some TypedInsertion anyway
+            return getBaseTypePosition(na);
+          }
+          return typeTree.accept(this, ins);
+        }
+
+        childSelector = lastEntry.getChildSelector();
+        if (dim > 0 && ASTPath.TYPE.equals(childSelector)) {
+          // rebuild path with current value of dim
+          ASTPath newPath = ASTPath.empty();
+          int j = 0;
+          dim += lastEntry.getArgument();
+          while (j < i) {  // [0,i)
+            newPath = newPath.extend(astPath.get(j));
+            j++;
+          }
+          lastEntry = new ASTPath.ASTEntry(Tree.Kind.NEW_ARRAY,
+              ASTPath.TYPE, dim);  // i
+          newPath = newPath.extend(lastEntry);
+          while (j < n) {  // [i,n)
+            newPath = newPath.extend(astPath.get(j));
+            j++;
+          }
+          astPath = newPath;
+        } else {
+          dim = lastEntry.getArgument();
+        }
+      }
+
+      if (ASTPath.TYPE.equals(childSelector)) {
+      if (na.toString().startsWith("{")) {
+        if (ins.getKind() == Insertion.Kind.ANNOTATION) {
+          TreePath parentPath = TreePath.getPath(tree, na).getParentPath();
+          if (parentPath != null) {
+            Tree parent = parentPath.getLeaf();
+            if (parent.getKind() == Tree.Kind.VARIABLE) {
+              AnnotationInsertion ai = (AnnotationInsertion) ins;
+              JCTree typeTree = ((JCVariableDecl) parent).getType();
+              ai.setType(typeTree.toString());
+              return Pair.of(rec.replacePath(astPath), na.getStartPosition());
+            }
+          }
+          System.err.println("WARNING: array initializer " + node +
+              " has no explicit type; skipping insertion " + ins);
+          return null;
+        } else {
+          return Pair.of(rec.replacePath(astPath), na.getStartPosition());
+        }
+      }
+      if (dim == dimsSize) {
+        if (na.elemtype == null) {
+          System.err.println("WARNING: array initializer " + node +
+              " has no explicit type; skipping insertion " + ins);
+          return null;
+        }
+        return getBaseTypePosition(na.elemtype);
+      }
+      if (na.dims.size() != 0) {
+        int startPos = na.getStartPosition();
+        int endPos = na.getEndPosition(tree.endPositions);
+        int pos = getNthInstanceInRange('[', startPos, endPos, dim + 1);
+        return Pair.of(rec.replacePath(astPath), pos);
+      }
+      // In a situation like
+      //   node=new String[][][][][]{{{}}}
+      // Also see Pretty.printBrackets.
+      if (dim == 0) {
+        if (na.elemtype == null) {
+          return Pair.of(rec.replacePath(astPath), na.getStartPosition());
+        }
+        // na.elemtype.getPreferredPosition(); seems to be at the end,
+        //  after the brackets.
+        // na.elemtype.getStartPosition(); is before the type name itself.
+        int startPos = na.elemtype.getStartPosition();
+        return Pair.of(rec.replacePath(astPath),
+            getFirstInstanceAfter('[', startPos+1));
+      } else if (dim == dimsSize) {
+        return Pair.of(rec.replacePath(astPath),
+            na.getType().pos().getStartPosition());
+      } else {
+        JCArrayTypeTree jcatt = (JCArrayTypeTree) na.elemtype;
+        for (int i=1; i<dim; i++) {
+          JCTree elem = jcatt.elemtype;
+          if (elem.hasTag(JCTree.Tag.ANNOTATED_TYPE)) {
+            elem = ((JCAnnotatedType) elem).underlyingType;
+          }
+          assert elem.hasTag(JCTree.Tag.TYPEARRAY);
+          jcatt = (JCArrayTypeTree) elem;
+        }
+        return Pair.of(rec.replacePath(astPath),
+            jcatt.pos().getPreferredPosition());
+      }
+      } else if (ASTPath.DIMENSION.equals(childSelector)) {
+        List<JCExpression> inits = na.getInitializers();
+        if (dim < inits.size()) {
+          JCExpression expr = inits.get(dim);
+          return Pair.of(astRecord(expr), expr.getStartPosition());
+        }
+        return null;
+      } else if (ASTPath.INITIALIZER.equals(childSelector)) {
+        JCExpression expr = na.getDimensions().get(dim);
+        return Pair.of(astRecord(expr), expr.getStartPosition());
+      } else {
+        assert false : "Unexpected child selector in AST path: "
+            + (childSelector == null ? "null" : "\"" + childSelector + "\"");
+        return null;
+      }
+    }
+
+    @Override
+    public Pair<ASTRecord, Integer> visitNewClass(NewClassTree node, Insertion ins) {
+      JCNewClass na = (JCNewClass) node;
+      JCExpression className = na.clazz;
+      // System.out.printf("classname %s (%s)%n", className, className.getClass());
+      while (! (className.getKind() == Tree.Kind.IDENTIFIER)) { // IdentifierTree
+        if (className instanceof JCAnnotatedType) {
+          className = ((JCAnnotatedType) className).underlyingType;
+        } else if (className instanceof JCTypeApply) {
+          className = ((JCTypeApply) className).clazz;
+        } else if (className instanceof JCFieldAccess) {
+          // This occurs for fully qualified names, e.g. "new java.lang.Object()".
+          // I'm not quite sure why the field "selected" is taken, but "name" would
+          // be a type mismatch. It seems to work, see NewPackage test case.
+          className = ((JCFieldAccess) className).selected;
+        } else {
+          throw new Error(String.format("unrecognized JCNewClass.clazz (%s): %s%n" +
+                  "   surrounding new class tree: %s%n", className.getClass(), className, node));
+        }
+        // System.out.printf("classname %s (%s)%n", className, className.getClass());
+      }
+
+      return visitIdentifier((IdentifierTree) className, ins);
+    }
+  }
+
+  /**
+   * Determine the insertion position for declaration annotations on
+   * various elements.  For instance, method declaration annotations should
+   * be placed before all the other modifiers and annotations.
+   */
+  private class DeclarationPositionFinder extends TreeScanner<Integer, Void> {
+
+    // When a method is visited, it is visited for the declaration itself.
+    @Override
+    public Integer visitMethod(MethodTree node, Void p) {
+      super.visitMethod(node, p);
+
+      // System.out.printf("DeclarationPositionFinder.visitMethod()%n");
+
+      ModifiersTree mt = node.getModifiers();
+
+      // actually List<JCAnnotation>.
+      List<? extends AnnotationTree> annos = mt.getAnnotations();
+      // Set<Modifier> flags = mt.getFlags();
+
+      JCTree before;
+      if (annos.size() > 1) {
+        before = (JCAnnotation) annos.get(0);
+      } else if (node.getReturnType() != null) {
+        before = (JCTree) node.getReturnType();
+      } else {
+        // if we're a constructor, we have null return type, so we use the constructor's position
+        // rather than the return type's position
+        before = (JCTree) node;
+      }
+      int declPos = before.getStartPosition();
+
+      // There is no source code location information for Modifiers, so
+      // cannot iterate through the modifiers.  But we don't have to.
+      int modsPos = ((JCModifiers)mt).pos().getStartPosition();
+      if (modsPos != Position.NOPOS) {
+        declPos = Math.min(declPos, modsPos);
+      }
+
+      return declPos;
+    }
+
+    @Override
+    public Integer visitCompilationUnit(CompilationUnitTree node, Void p) {
+      JCCompilationUnit cu = (JCCompilationUnit) node;
+      return cu.getStartPosition();
+    }
+
+    @Override
+    public Integer visitClass(ClassTree node, Void p) {
+      JCClassDecl cd = (JCClassDecl) node;
+      int result = -1;
+      if (cd.mods != null
+          && (cd.mods.flags != 0 || cd.mods.annotations.size() > 0)) {
+        result = cd.mods.getPreferredPosition();
+      }
+      if (result < 0) {
+        result = cd.getPreferredPosition();
+      }
+      assert result >= 0 || cd.name.isEmpty()
+        : String.format("%d %d %d%n", cd.getStartPosition(),
+                        cd.getPreferredPosition(), cd.pos);
+      return result < 0 ? null : result;
+    }
+
+  }
+
+  private final TypePositionFinder tpf;
+  private final DeclarationPositionFinder dpf;
+  private final JCCompilationUnit tree;
+  private final SetMultimap<Pair<Integer, ASTPath>, Insertion> insertions;
+  private final SetMultimap<ASTRecord, Insertion> astInsertions;
+
+  /**
+   * Creates a {@code TreeFinder} from a source tree.
+   *
+   * @param tree the source tree to search
+   */
+  public TreeFinder(JCCompilationUnit tree) {
+    this.tree = tree;
+    this.insertions = LinkedHashMultimap.create();
+    this.astInsertions = LinkedHashMultimap.create();
+    this.tpf = new TypePositionFinder();
+    this.dpf = new DeclarationPositionFinder();
+  }
+
+  // which nodes are possible insertion sites
+  boolean handled(Tree node) {
+    switch (node.getKind()) {
+    case ANNOTATION:
+    case ARRAY_TYPE:
+    case CLASS:
+    case COMPILATION_UNIT:
+    case ENUM:
+    case EXPRESSION_STATEMENT:
+    case EXTENDS_WILDCARD:
+    case IDENTIFIER:
+    case INTERFACE:
+    case METHOD:
+    case NEW_ARRAY:
+    case NEW_CLASS:
+    case PARAMETERIZED_TYPE:
+    case PRIMITIVE_TYPE:
+    case SUPER_WILDCARD:
+    case TYPE_PARAMETER:
+    case UNBOUNDED_WILDCARD:
+    case VARIABLE:
+      return true;
+    default:
+      return node instanceof ExpressionTree;
+    }
+  }
+
+  /**
+   * Determines if the last {@link TypePathEntry} in the given list is a
+   * {@link TypePathEntryKind#WILDCARD}.
+   *
+   * @param location the list to check
+   * @return {@code true} if the last {@link TypePathEntry} is a
+   *         {@link TypePathEntryKind#WILDCARD}, {@code false} otherwise.
+   */
+  private boolean wildcardLast(List<TypePathEntry> location) {
+    return location.get(location.size() - 1).tag == TypePathEntryKind.WILDCARD;
+  }
+
+  /**
+   * Scans this tree, using the list of insertions to generate the source
+   * position to insertion text mapping.  Insertions are removed from the
+   * list when positions are found for them.
+   *
+   * @param node AST node being considered for annotation insertions
+   * @param p list of insertions not yet placed
+   * <p>
+   * When a match is found, this routine removes the insertion from p and
+   * adds it to the insertions map as a value, with a key that is a pair.
+   * On return, p contains only the insertions for which no match was found.
+   */
+  @Override
+  public Void scan(Tree node, List<Insertion> p) {
+    if (node == null) {
+      return null;
+    }
+
+    dbug.debug("SCANNING: %s %s%n", node.getKind(), node);
+    if (! handled(node)) {
+      dbug.debug("Not handled, skipping (%s): %s%n", node.getClass(), node);
+      // nothing to do
+      return super.scan(node, p);
+    }
+
+    TreePath path = getPath(node);
+    assert path == null || path.getLeaf() == node :
+      String.format("Mismatch: '%s' '%s' '%s'%n",
+          path, path.getLeaf(), node);
+
+    // To avoid annotating existing annotations right before
+    // the element you wish to annotate, skip anything inside of
+    // an annotation.
+    if (path != null) {
+      for (Tree t : path) {
+        if (t.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+          // We started with something within a parameterized type and
+          // should not look for any further annotations.
+          // TODO: does this work on multiple nested levels?
+          break;
+        }
+        if (t.getKind() == Tree.Kind.ANNOTATION) {
+          return super.scan(node, p);
+        }
+      }
+    }
+
+    for (Iterator<Insertion> it = p.iterator(); it.hasNext(); ) {
+      Insertion i = it.next();
+      if (i.getInserted()) {
+        // Skip this insertion if it has already been inserted. See
+        // the ReceiverInsertion class for details.
+        it.remove();
+        continue;
+      }
+      dbug.debug("Considering insertion at tree:%n");
+      dbug.debug("  Insertion: %s%n", i);
+      dbug.debug("  First line of node: %s%n", Main.firstLine(node.toString()));
+      dbug.debug("  Type of node: %s%n", node.getClass());
+      if (!i.getCriteria().isSatisfiedBy(path, node)) {
+        dbug.debug("  ... not satisfied%n");
+        continue;
+      } else {
+        dbug.debug("  ... satisfied!%n");
+        dbug.debug("    First line of node: %s%n", Main.firstLine(node.toString()));
+        dbug.debug("    Type of node: %s%n", node.getClass());
+
+        ASTPath astPath = i.getCriteria().getASTPath();
+        Integer pos = astPath == null ? findPosition(path, i)
+            : Main.convert_jaifs ? null  // already in correct form
+            : findPositionByASTPath(astPath, path, i);
+        if (pos != null) {
+          dbug.debug("  ... satisfied! at %d for node of type %s: %s%n",
+              pos, node.getClass(), Main.treeToString(node));
+          insertions.put(Pair.of(pos, astPath), i);
+        }
+      }
+      it.remove();
+    }
+    return super.scan(node, p);
+  }
+
+  // Find insertion position for Insertion whose criteria matched the
+  // given TreePath.
+  // If no position is found, report an error and return null.
+  Integer findPosition(TreePath path, Insertion i) {
+    Tree node = path.getLeaf();
+    try {
+      // As per the JSR308 specification, receiver parameters are not allowed
+      // on method declarations of anonymous inner classes.
+      if (i.getCriteria().isOnReceiver()
+          && path.getParentPath().getParentPath().getLeaf().getKind() == Tree.Kind.NEW_CLASS) {
+        warn.debug("WARNING: Cannot insert a receiver parameter "
+            + "on a method declaration of an anonymous inner class.  "
+            + "This insertion will be skipped.%n    Insertion: %s%n", i);
+        return null;
+      }
+
+      // TODO: Find a more fine-grained replacement for the 2nd conjunct below.
+      // The real issue is whether the insertion will add non-annotation code,
+      // which is only sometimes the case for a TypedInsertion.
+      if (alreadyPresent(path, i) && !(i instanceof TypedInsertion)) {
+        // Don't insert a duplicate if this particular annotation is already
+        // present at this location.
+        return null;
+      }
+
+      if (i.getKind() == Insertion.Kind.CONSTRUCTOR) {
+        ConstructorInsertion cons = (ConstructorInsertion) i;
+        if (node.getKind() == Tree.Kind.METHOD) {
+          JCMethodDecl method = (JCMethodDecl) node;
+          // TODO: account for the following situation in matching phase instead
+          if (method.sym.owner.isAnonymous()) { return null; }
+          if ((method.mods.flags & Flags.GENERATEDCONSTR) != 0) {
+            addConstructor(path, cons, method);
+          } else {
+            cons.setAnnotationsOnly(true);
+            cons.setInserted(true);
+            i = cons.getReceiverInsertion();
+            if (i == null) { return null; }
+          }
+        } else {
+          cons.setAnnotationsOnly(true);
+        }
+      }
+
+      if (i.getKind() == Insertion.Kind.RECEIVER && node.getKind() == Tree.Kind.METHOD) {
+        ReceiverInsertion receiver = (ReceiverInsertion) i;
+        MethodTree method = (MethodTree) node;
+        VariableTree rcv = method.getReceiverParameter();
+
+        if (rcv == null) {
+          addReceiverType(path, receiver, method);
+        }
+      }
+
+      if (i.getKind() == Insertion.Kind.NEW && node.getKind() == Tree.Kind.NEW_ARRAY) {
+        NewInsertion neu = (NewInsertion) i;
+        NewArrayTree newArray = (NewArrayTree) node;
+
+        if (newArray.toString().startsWith("{")) {
+          addNewType(path, neu, newArray);
+        }
+      }
+
+      // If this is a method, then it might have been selected because of
+      // the receiver, or because of the return value.  Distinguish those.
+      // One way would be to set a global variable here.  Another would be
+      // to look for a particular different node.  I will do the latter.
+      Integer pos = Position.NOPOS;
+
+      // The insertion location is at or below the matched location
+      // in the source tree.  For example, a receiver annotation
+      // matches on the method and inserts on the (possibly newly
+      // created) receiver.
+      Map<Tree, ASTRecord> astIndex = ASTIndex.indexOf(tree);
+      ASTRecord insertRecord = astIndex.get(node);
+      dbug.debug("TreeFinder.scan: node=%s%n  critera=%s%n",
+          node, i.getCriteria());
+
+      if (CommonScanner.hasClassKind(node)
+          && i.getCriteria().isOnTypeDeclarationExtendsClause()
+          && ((ClassTree) node).getExtendsClause() == null) {
+        return implicitClassBoundPosition((JCClassDecl) node, i);
+      }
+      if (node.getKind() == Tree.Kind.METHOD
+          && i.getCriteria().isOnReturnType()) {
+        JCMethodDecl jcnode = (JCMethodDecl) node;
+        Tree returnType = jcnode.getReturnType();
+        insertRecord = insertRecord.extend(Tree.Kind.METHOD, ASTPath.TYPE);
+        if (returnType == null) {
+          // find constructor name instead
+          pos = findMethodName(jcnode);
+          if (pos < 0) {  // skip -- inserted w/generated constructor
+            return null;
+          }
+          dbug.debug("pos = %d at constructor name: %s%n",
+              pos, jcnode.sym.toString());
+        } else {
+          Pair<ASTRecord, Integer> pair = tpf.scan(returnType, i);
+          insertRecord = pair.a;
+          pos = pair.b;
+          assert handled(node);
+          dbug.debug("pos = %d at return type node: %s%n",
+              pos, returnType.getClass());
+        }
+      } else if (node.getKind() == Tree.Kind.TYPE_PARAMETER
+          && i.getCriteria().onBoundZero()
+          && (((TypeParameterTree) node).getBounds().isEmpty()
+              || (((JCExpression) ((TypeParameterTree) node)
+                      .getBounds().get(0))).type.tsym.isInterface())
+          || (node instanceof WildcardTree
+              && ((WildcardTree) node).getBound() == null
+              && wildcardLast(i.getCriteria()
+                      .getGenericArrayLocation().getLocation()))) {
+        Pair<ASTRecord, Integer> pair = tpf.scan(node, i);
+        insertRecord = pair.a;
+        pos = pair.b;
+
+        if (i.getKind() == Insertion.Kind.ANNOTATION) {
+          if (node.getKind() == Tree.Kind.TYPE_PARAMETER
+              && !((TypeParameterTree) node).getBounds().isEmpty()) {
+            Tree bound = ((TypeParameterTree) node).getBounds().get(0);
+            pos = ((JCExpression) bound).getStartPosition();
+            ((AnnotationInsertion) i).setGenerateBound(true);
+          } else {
+            int limit = ((JCTree) parent(node)).getEndPosition(tree.endPositions);
+            Integer nextpos1 = getNthInstanceInRange(',', pos+1, limit, 1);
+            Integer nextpos2 = getNthInstanceInRange('>', pos+1, limit, 1);
+            pos = (nextpos1 != Position.NOPOS && nextpos1 < nextpos2) ? nextpos1 : nextpos2;
+            ((AnnotationInsertion) i).setGenerateExtends(true);
+          }
+        }
+      } else if (i.getKind() == Insertion.Kind.CAST) {
+        Type t = ((CastInsertion) i).getType();
+        JCTree jcTree = (JCTree) node;
+        pos = jcTree.getStartPosition();
+        if (t.getKind() == Type.Kind.DECLARED) {
+          DeclaredType dt = (DeclaredType) t;
+          if (dt.getName().isEmpty()) {
+            dt.setName(jcTree.type instanceof NullType ? "Object"
+                : jcTree.type.toString());
+          }
+        }
+      } else if (i.getKind() == Insertion.Kind.CLOSE_PARENTHESIS) {
+        JCTree jcTree = (JCTree) node;
+        pos = jcTree.getEndPosition(tree.endPositions);
+      } else {
+        boolean typeScan = true;
+        if (node.getKind() == Tree.Kind.METHOD) { // MethodTree
+          // looking for the receiver or the declaration
+          typeScan = i.getCriteria().isOnReceiver();
+        } else if (CommonScanner.hasClassKind(node)) { // ClassTree
+          typeScan = ! i.getSeparateLine(); // hacky check
+        }
+        if (typeScan) {
+          // looking for the type
+          dbug.debug("Calling tpf.scan(%s: %s)%n", node.getClass(), node);
+          Pair<ASTRecord, Integer> pair = tpf.scan(node, i);
+          insertRecord = pair.a;
+          pos = pair.b;
+          assert handled(node);
+          dbug.debug("pos = %d at type: %s (%s)%n", pos,
+              node.toString(), node.getClass());
+        } else if (node.getKind() == Tree.Kind.METHOD
+            && i.getKind() == Insertion.Kind.CONSTRUCTOR
+            && (((JCMethodDecl) node).mods.flags & Flags.GENERATEDCONSTR) != 0) {
+          Tree parent = path.getParentPath().getLeaf();
+          pos = ((JCClassDecl) parent).getEndPosition(tree.endPositions) - 1;
+          insertRecord = null;  // TODO
+        } else {
+          // looking for the declaration
+          pos = dpf.scan(node, null);
+          insertRecord = astRecord(node);
+          dbug.debug("pos = %s at declaration: %s%n", pos, node.getClass());
+        }
+      }
+
+      if (pos != null) {
+        assert pos >= 0 :
+          String.format("pos: %s%nnode: %s%ninsertion: %s%n", pos, node, i);
+        astInsertions.put(insertRecord, i);
+      }
+      return pos;
+    } catch (Throwable e) {
+      reportInsertionError(i, e);
+      return null;
+    }
+  }
+
+  // Find insertion position for Insertion whose criteria (including one
+  // for the ASTPath) matched the given TreePath.
+  // If no position is found, report an error and return null.
+  Integer findPositionByASTPath(ASTPath astPath, TreePath path, Insertion i) {
+    Tree node = path.getLeaf();
+    try {
+      ASTPath.ASTEntry entry = astPath.get(-1);
+      // As per the JSR308 specification, receiver parameters are not allowed
+      // on method declarations of anonymous inner classes.
+      if (entry.getTreeKind() == Tree.Kind.METHOD
+          && entry.childSelectorIs(ASTPath.PARAMETER)
+          && entry.getArgument() == -1
+          && path.getParentPath().getParentPath().getLeaf().getKind()
+          == Tree.Kind.NEW_CLASS) {
+        warn.debug("WARNING: Cannot insert a receiver parameter "
+            + "on a method declaration of an anonymous inner class.  "
+            + "This insertion will be skipped.%n    Insertion: %s%n", i);
+        return null;
+      }
+
+      if (alreadyPresent(path, i)) {
+        // Don't insert a duplicate if this particular annotation is already
+        // present at this location.
+        return null;
+      }
+
+      if (i.getKind() == Insertion.Kind.CONSTRUCTOR) {
+        ConstructorInsertion cons = (ConstructorInsertion) i;
+
+        if (node.getKind() == Tree.Kind.METHOD) {
+          JCMethodDecl method = (JCMethodDecl) node;
+          if ((method.mods.flags & Flags.GENERATEDCONSTR) != 0) {
+            addConstructor(path, cons, method);
+          } else {
+            cons.setAnnotationsOnly(true);
+            cons.setInserted(true);
+            i = cons.getReceiverInsertion();
+            if (i == null) { return null; }
+          }
+        } else {
+          cons.setAnnotationsOnly(true);
+        }
+      }
+
+      if (i.getKind() == Insertion.Kind.RECEIVER && node.getKind() == Tree.Kind.METHOD) {
+        ReceiverInsertion receiver = (ReceiverInsertion) i;
+        MethodTree method = (MethodTree) node;
+
+        if (method.getReceiverParameter() == null) {
+          addReceiverType(path, receiver, method);
+        }
+      }
+
+      if (i.getKind() == Insertion.Kind.NEW && node.getKind() == Tree.Kind.NEW_ARRAY) {
+        NewInsertion neu = (NewInsertion) i;
+        NewArrayTree newArray = (NewArrayTree) node;
+
+        if (newArray.toString().startsWith("{")) {
+          addNewType(path, neu, newArray);
+        }
+      }
+
+      // If this is a method, then it might have been selected because of
+      // the receiver, or because of the return value.  Distinguish those.
+      // One way would be to set a global variable here.  Another would be
+      // to look for a particular different node.  I will do the latter.
+      Integer pos = Position.NOPOS;
+
+      // The insertion location is at or below the matched location
+      // in the source tree.  For example, a receiver annotation
+      // matches on the method and inserts on the (possibly newly
+      // created) receiver.
+      Map<Tree, ASTRecord> astIndex = ASTIndex.indexOf(tree);
+      ASTRecord insertRecord = astIndex.get(node);
+      dbug.debug("TreeFinder.scan: node=%s%n  criteria=%s%n",
+          node, i.getCriteria());
+
+      if (CommonScanner.hasClassKind(node)
+          && entry.childSelectorIs(ASTPath.BOUND)
+          && entry.getArgument() < 0
+          && ((ClassTree) node).getExtendsClause() == null) {
+        return implicitClassBoundPosition((JCClassDecl) node, i);
+      }
+      if (node.getKind() == Tree.Kind.METHOD
+          && i.getCriteria().isOnMethod("<init>()V")
+          && entry.childSelectorIs(ASTPath.PARAMETER)
+          && entry.getArgument() < 0) {
+        if (i.getKind() != Insertion.Kind.CONSTRUCTOR) { return null; }
+        Tree parent = path.getParentPath().getLeaf();
+        insertRecord = insertRecord.extend(Tree.Kind.METHOD, ASTPath.PARAMETER, -1);
+        pos = ((JCTree) parent).getEndPosition(tree.endPositions) - 1;
+      } else if (node.getKind() == Tree.Kind.METHOD
+          && entry.childSelectorIs(ASTPath.TYPE)) {
+        JCMethodDecl jcnode = (JCMethodDecl) node;
+        Tree returnType = jcnode.getReturnType();
+        insertRecord = insertRecord.extend(Tree.Kind.METHOD, ASTPath.TYPE);
+        if (returnType == null) {
+          // find constructor name instead
+          pos = findMethodName(jcnode);
+          if (pos < 0) {  // skip -- inserted w/generated constructor
+            return null;
+          }
+          dbug.debug("pos = %d at constructor name: %s%n",
+              pos, jcnode.sym.toString());
+        } else {
+          Pair<ASTRecord, Integer> pair = tpf.scan(returnType, i);
+          insertRecord = pair.a;
+          pos = pair.b;
+          assert handled(node);
+          dbug.debug("pos = %d at return type node: %s%n",
+              pos, returnType.getClass());
+        }
+      } else if (node.getKind() == Tree.Kind.TYPE_PARAMETER
+          && entry.getTreeKind() == Tree.Kind.TYPE_PARAMETER  // TypeParameter.bound
+          && (((TypeParameterTree) node).getBounds().isEmpty()
+              || (((JCExpression) ((TypeParameterTree) node)
+                      .getBounds().get(0))).type.tsym.isInterface())
+          || ASTPath.isWildcard(node.getKind())
+          && (entry.getTreeKind() == Tree.Kind.TYPE_PARAMETER
+              || ASTPath.isWildcard(entry.getTreeKind()))
+          && entry.childSelectorIs(ASTPath.BOUND)
+          && (!entry.hasArgument() || entry.getArgument() == 0)) {
+        Pair<ASTRecord, Integer> pair = tpf.scan(node, i);
+        insertRecord = pair.a;
+        pos = pair.b;
+
+        if (i.getKind() == Insertion.Kind.ANNOTATION) {
+          if (node.getKind() == Tree.Kind.TYPE_PARAMETER
+              && !((TypeParameterTree) node).getBounds().isEmpty()) {
+            Tree bound = ((TypeParameterTree) node).getBounds().get(0);
+            pos = ((JCExpression) bound).getStartPosition();
+            ((AnnotationInsertion) i).setGenerateBound(true);
+          } else {
+            int limit = ((JCTree) parent(node)).getEndPosition(tree.endPositions);
+            Integer nextpos1 = getNthInstanceInRange(',', pos+1, limit, 1);
+            Integer nextpos2 = getNthInstanceInRange('>', pos+1, limit, 1);
+            pos = (nextpos1 != Position.NOPOS && nextpos1 < nextpos2) ? nextpos1 : nextpos2;
+            ((AnnotationInsertion) i).setGenerateExtends(true);
+          }
+        }
+      } else if (i.getKind() == Insertion.Kind.CAST) {
+        Type t = ((CastInsertion) i).getType();
+        JCTree jcTree = (JCTree) node;
+        if (jcTree.getKind() == Tree.Kind.VARIABLE && !astPath.isEmpty()
+            && astPath.get(-1).childSelectorIs(ASTPath.INITIALIZER)) {
+          node = ((JCVariableDecl) node).getInitializer();
+          if (node == null) { return null; }
+          jcTree = (JCTree) node;
+        }
+        pos = jcTree.getStartPosition();
+        if (t.getKind() == Type.Kind.DECLARED) {
+          DeclaredType dt = (DeclaredType) t;
+          if (dt.getName().isEmpty()) {
+              if (jcTree.type instanceof NullType) {
+                dt.setName("Object");
+              } else {
+                t = Insertions.TypeTree.conv(jcTree.type);
+                t.setAnnotations(dt.getAnnotations());
+                ((CastInsertion) i).setType(t);
+              }
+          }
+        }
+      } else if (i.getKind() == Insertion.Kind.CLOSE_PARENTHESIS) {
+        JCTree jcTree = (JCTree) node;
+        if (jcTree.getKind() == Tree.Kind.VARIABLE && !astPath.isEmpty()
+            && astPath.get(-1).childSelectorIs(ASTPath.INITIALIZER)) {
+          node = ((JCVariableDecl) node).getInitializer();
+          if (node == null) { return null; }
+          jcTree = (JCTree) node;
+        }
+        pos = jcTree.getEndPosition(tree.endPositions);
+      } else {
+        boolean typeScan = true;
+        if (node.getKind() == Tree.Kind.METHOD) { // MethodTree
+          // looking for the receiver or the declaration
+          typeScan = IndexFileSpecification.isOnReceiver(i.getCriteria());
+        } else if (node.getKind() == Tree.Kind.CLASS) { // ClassTree
+          typeScan = ! i.getSeparateLine(); // hacky check
+        }
+        if (typeScan) {
+          // looking for the type
+          dbug.debug("Calling tpf.scan(%s: %s)%n", node.getClass(), node);
+          Pair<ASTRecord, Integer> pair = tpf.scan(node, i);
+          insertRecord = pair.a;
+          pos = pair.b;
+          assert handled(node);
+          dbug.debug("pos = %d at type: %s (%s)%n",
+              pos, node.toString(), node.getClass());
+        } else if (node.getKind() == Tree.Kind.METHOD
+            && i.getKind() == Insertion.Kind.CONSTRUCTOR
+            && (((JCMethodDecl) node).mods.flags & Flags.GENERATEDCONSTR) != 0) {
+          Tree parent = path.getParentPath().getLeaf();
+          pos = ((JCClassDecl) parent).getEndPosition(tree.endPositions) - 1;
+          insertRecord = null;  // TODO
+        } else {
+          // looking for the declaration
+          pos = dpf.scan(node, null);
+          insertRecord = astRecord(node);
+          assert pos != null;
+          dbug.debug("pos = %d at declaration: %s%n", pos, node.getClass());
+        }
+      }
+
+      if (pos != null) {
+        assert pos >= 0 :
+          String.format("pos: %s%nnode: %s%ninsertion: %s%n", pos, node, i);
+        astInsertions.put(insertRecord, i);
+      }
+      return pos;
+    } catch (Throwable e) {
+      reportInsertionError(i, e);
+      return null;
+    }
+  }
+
+  private Integer implicitClassBoundPosition(JCClassDecl cd, Insertion i) {
+    Integer pos;
+    if (cd.sym == null || cd.sym.isAnonymous()
+        || i.getKind() != Insertion.Kind.ANNOTATION) {
+      return null;
+    }
+    JCModifiers mods = cd.getModifiers();
+    String name = cd.getSimpleName().toString();
+    if (cd.typarams == null || cd.typarams.isEmpty()) {
+      int start = cd.getStartPosition();
+      int offset = Math.max(start,
+          mods.getEndPosition(tree.endPositions) + 1);
+      String s = cd.toString().substring(offset - start);
+      Pattern p = Pattern.compile("(?:\\s|" + comment
+          + ")*+class(?:\\s|" + comment
+          + ")++" + Pattern.quote(name) + "\\b");
+      Matcher m = p.matcher(s);
+      if (!m.find() || m.start() != 0) { return null; }
+      pos = offset + m.end() - 1;
+    } else {  // generic class
+      JCTypeParameter param = cd.typarams.get(cd.typarams.length()-1);
+      int start = param.getEndPosition(tree.endPositions);
+      pos = getFirstInstanceAfter('>', start) + 1;
+    }
+    ((AnnotationInsertion) i).setGenerateExtends(true);
+    return pos;
+  }
+
+  /**
+   * Returns the start position of the method's name.  In particular,
+   * works properly for constructors, for which the name field in the
+   * AST is always "<init>" instead of the name from the source.
+   *
+   * @param node AST node of method declaration
+   * @return position of method name (from {@link JCMethodDecl#sym}) in source
+   */
+  private int findMethodName(JCMethodDecl node) {
+    String sym = node.sym.toString();
+    String name = sym.substring(0, sym.indexOf('('));
+    JCModifiers mods = node.getModifiers();
+    JCBlock body = node.body;
+    if ((mods.flags & Flags.GENERATEDCONSTR) != 0) { return Position.NOPOS; }
+    int nodeStart = node.getStartPosition();
+    int nodeEnd = node.getEndPosition(tree.endPositions);
+    int nodeLength = nodeEnd - nodeStart;
+    int modsLength = mods.getEndPosition(tree.endPositions)
+        - mods.getStartPosition();  // can't trust string length!
+    int bodyLength = body == null ? 1
+        : body.getEndPosition(tree.endPositions) - body.getStartPosition();
+    int start = nodeStart + modsLength;
+    int end = nodeStart + nodeLength - bodyLength;
+    int angle = name.lastIndexOf('>');  // check for type params
+    if (angle >= 0) { name = name.substring(angle + 1); }
+
+    try {
+      CharSequence s = tree.getSourceFile().getCharContent(true);
+      String regex = "\\b" + Pattern.quote(name) + "\\b";  // sufficient?
+      Pattern pat = Pattern.compile(regex, Pattern.MULTILINE);
+      Matcher mat = pat.matcher(s).region(start, end);
+      return mat.find() ? mat.start() : Position.NOPOS;
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  /**
+   * Determines if the annotation in the given insertion is already present
+   * at the given location in the AST.
+   *
+   * @param path the location in the AST to check for the annotation
+   * @param ins the annotation to check for
+   * @return {@code true} if the given annotation is already at the given
+   *         location in the AST, {@code false} otherwise.
+   */
+  private boolean alreadyPresent(TreePath path, Insertion ins) {
+    List<? extends AnnotationTree> alreadyPresent = null;
+    if (path != null) {
+      for (Tree n : path) {
+        if (n.getKind() == Tree.Kind.CLASS) {
+          alreadyPresent = ((ClassTree) n).getModifiers().getAnnotations();
+          break;
+        } else if (n.getKind() == Tree.Kind.METHOD) {
+          alreadyPresent = ((MethodTree) n).getModifiers().getAnnotations();
+          break;
+        } else if (n.getKind() == Tree.Kind.VARIABLE) {
+          alreadyPresent = ((VariableTree) n).getModifiers().getAnnotations();
+          break;
+        } else if (n.getKind() == Tree.Kind.TYPE_CAST) {
+          Tree type = ((TypeCastTree) n).getType();
+          if (type.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+            alreadyPresent = ((AnnotatedTypeTree) type).getAnnotations();
+          }
+          break;
+        } else if (n.getKind() == Tree.Kind.INSTANCE_OF) {
+          Tree type = ((InstanceOfTree) n).getType();
+          if (type.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+            alreadyPresent = ((AnnotatedTypeTree) type).getAnnotations();
+          }
+          break;
+        } else if (n.getKind() == Tree.Kind.NEW_CLASS) {
+          JCNewClass nc = (JCNewClass) n;
+          if (nc.clazz.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+            alreadyPresent = ((AnnotatedTypeTree) nc.clazz).getAnnotations();
+          }
+          break;
+        } else if (n.getKind() == Tree.Kind.PARAMETERIZED_TYPE) {
+          // If we pass through a parameterized type, stop, otherwise we
+          // mix up annotations on the outer type.
+          break;
+        } else if (n.getKind() == Tree.Kind.ARRAY_TYPE) {
+          Tree type = ((ArrayTypeTree) n).getType();
+          if (type.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+            alreadyPresent = ((AnnotatedTypeTree) type).getAnnotations();
+          }
+          break;
+        } else if (n.getKind() == Tree.Kind.ANNOTATED_TYPE) {
+          alreadyPresent = ((AnnotatedTypeTree) n).getAnnotations();
+          break;
+        }
+        // TODO: don't add cast insertion if it's already present.
+      }
+    }
+
+    if (alreadyPresent != null) {
+      for (AnnotationTree at : alreadyPresent) {
+        // Compare the to-be-inserted annotation to the existing
+        // annotation, ignoring its arguments (duplicate annotations are
+        // never allowed even if they differ in arguments).  If we did
+        // have to compare our arguments, we'd have to deal with enum
+        // arguments potentially being fully qualified or not:
+        // @Retention(java.lang.annotation.RetentionPolicy.CLASS) vs
+        // @Retention(RetentionPolicy.CLASS)
+        String ann = at.getAnnotationType().toString();
+        // strip off leading @ along w/any leading or trailing whitespace
+        String text = ins.getText();
+        String iann = Main.removeArgs(text).a.trim()
+            .substring(text.startsWith("@") ? 1 : 0);
+        String iannNoPackage = Insertion.removePackage(iann).b;
+        // System.out.printf("Comparing: %s %s %s%n", ann, iann, iannNoPackage);
+        if (ann.equals(iann) || ann.equals(iannNoPackage)) {
+          dbug.debug("Already present, not reinserting: %s%n", ann);
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Reports an error inserting an insertion to {@code System.err}.
+   * @param i the insertion that caused the error
+   * @param e the error. If there's a message it will be printed.
+   */
+  public static void reportInsertionError(Insertion i, Throwable e) {
+    System.err.println("Error processing insertion:");
+    System.err.println("\t" + i);
+    if (e.getMessage() != null) {
+      // If the message has multiple lines, indent them so it's easier to read.
+      System.err.println("\tError: " + e.getMessage().replace("\n", "\n\t\t"));
+    }
+    if (dbug.or(stak).isEnabled()) {
+      e.printStackTrace();
+    } else {
+      System.err.println("\tRun with --print_error_stack to see the stack trace.");
+    }
+    System.err.println("\tThis insertion will be skipped.");
+  }
+
+  /**
+   * Modifies the given receiver insertion so that it contains the type
+   * information necessary to insert a full method declaration receiver
+   * parameter. This is for receiver insertions where a receiver does not
+   * already exist in the source code. This will also add the annotations to be
+   * inserted to the correct part of the receiver type.
+   *
+   * @param path the location in the AST to insert the receiver
+   * @param receiver details of the receiver to insert
+   * @param method the method the receiver is being inserted into
+   */
+  private void addReceiverType(TreePath path, ReceiverInsertion receiver,
+      MethodTree method) {
+    // Find the name of the class
+    // with type parameters to create the receiver. Walk up the tree and
+    // pick up class names to add to the receiver type. Since we're
+    // starting from the innermost class, the classes we get to at earlier
+    // iterations of the loop are inside of the classes we get to at later
+    // iterations.
+    TreePath parent = path;
+    Tree leaf = parent.getLeaf();
+    Tree.Kind kind = leaf.getKind();
+    // This is the outermost type, currently containing only the
+    // annotation to add to the receiver.
+    Type outerType = receiver.getType();
+    DeclaredType baseType = receiver.getBaseType();
+    // This holds the inner types as they're being read in.
+    DeclaredType innerTypes = null;
+    DeclaredType staticType = null;
+    // For an inner class constructor, the receiver comes from the
+    // superclass, so skip past the first type definition.
+    boolean isCon = ((MethodTree) parent.getLeaf()).getReturnType() == null;
+    boolean skip = isCon;
+
+    while (kind != Tree.Kind.COMPILATION_UNIT
+        && kind != Tree.Kind.NEW_CLASS) {
+      if (kind == Tree.Kind.CLASS
+          || kind == Tree.Kind.INTERFACE
+          || kind == Tree.Kind.ENUM
+          || kind == Tree.Kind.ANNOTATION_TYPE) {
+        ClassTree clazz = (ClassTree) leaf;
+        String className = clazz.getSimpleName().toString();
+        boolean isStatic = kind == Tree.Kind.INTERFACE
+            || kind == Tree.Kind.ENUM
+            || clazz.getModifiers().getFlags().contains(Modifier.STATIC);
+        skip &= !isStatic;
+        if (skip) {
+          skip = false;
+          receiver.setQualifyType(true);
+        } else if (!className.isEmpty()) {
+          // className will be empty for the CLASS node directly inside an
+          // anonymous inner class NEW_CLASS node.
+          DeclaredType inner = new DeclaredType(className);
+          if (staticType == null) {
+            // Only include type parameters on the classes to the right of and
+            // including the rightmost static class.
+            for (TypeParameterTree tree : clazz.getTypeParameters()) {
+              inner.addTypeParameter(new DeclaredType(tree.getName().toString()));
+            }
+          }
+          if (staticType == null && isStatic) {
+            // If this is the first static class then move the annotations here.
+            inner.setAnnotations(outerType.getAnnotations());
+            outerType.clearAnnotations();
+            staticType = inner;
+          }
+          if (innerTypes == null) {
+            // This is the first type we've read in, so set it as the
+            // innermost type.
+            innerTypes = inner;
+          } else {
+            // inner (the type just read in this iteration) is outside of
+            // innerTypes (the types already read in previous iterations).
+            inner.setInnerType(innerTypes);
+            innerTypes = inner;
+          }
+        }
+      }
+      parent = parent.getParentPath();
+      leaf = parent.getLeaf();
+      kind = leaf.getKind();
+    }
+    if (isCon && innerTypes == null) {
+      throw new IllegalArgumentException(
+          "can't annotate (non-existent) receiver of non-inner constructor");
+    }
+
+    // Merge innerTypes into outerType: outerType only has the annotations
+    // on the receiver, while innerTypes has everything else. innerTypes can
+    // have the annotations if it is a static class.
+    baseType.setName(innerTypes.getName());
+    baseType.setTypeParameters(innerTypes.getTypeParameters());
+    baseType.setInnerType(innerTypes.getInnerType());
+    if (staticType != null && !innerTypes.getAnnotations().isEmpty()) {
+      outerType.setAnnotations(innerTypes.getAnnotations());
+    }
+
+    Type type = (staticType == null) ? baseType : staticType;
+    Insertion.decorateType(receiver.getInnerTypeInsertions(), type,
+        receiver.getCriteria().getASTPath());
+
+    // If the method doesn't have parameters, don't add a comma.
+    receiver.setAddComma(method.getParameters().size() > 0);
+  }
+
+  private void addNewType(TreePath path, NewInsertion neu,
+      NewArrayTree newArray) {
+    DeclaredType baseType = neu.getBaseType();
+    if (baseType.getName().isEmpty()) {
+      List<String> annotations = neu.getType().getAnnotations();
+      Type newType = Insertions.TypeTree.conv(
+          ((JCTree.JCNewArray) newArray).type);
+      for (String ann : annotations) {
+        newType.addAnnotation(ann);
+      }
+      neu.setType(newType);
+    }
+    Insertion.decorateType(neu.getInnerTypeInsertions(), neu.getType(),
+        neu.getCriteria().getASTPath());
+  }
+
+  private void addConstructor(TreePath path, ConstructorInsertion cons,
+      MethodTree method) {
+    ReceiverInsertion recv = cons.getReceiverInsertion();
+    MethodTree leaf = (MethodTree) path.getLeaf();
+    ClassTree parent = (ClassTree) path.getParentPath().getLeaf();
+    DeclaredType baseType = cons.getBaseType();
+    if (baseType.getName().isEmpty()) {
+      List<String> annotations = baseType.getAnnotations();
+      String className = parent.getSimpleName().toString();
+      Type newType = new DeclaredType(className);
+      cons.setType(newType);
+      for (String ann : annotations) {
+        newType.addAnnotation(ann);
+      }
+    }
+    if (recv != null) {
+      Iterator<Insertion> iter = cons.getInnerTypeInsertions().iterator();
+      List<Insertion> recvInner = new ArrayList<Insertion>();
+      addReceiverType(path, recv, leaf);
+      while (iter.hasNext()) {
+        Insertion i = iter.next();
+        if (i.getCriteria().isOnReceiver()) {
+          recvInner.add(i);
+          iter.remove();
+        }
+      }
+      Insertion.decorateType(recvInner, recv.getType(),
+          cons.getCriteria().getASTPath());
+    }
+    Insertion.decorateType(cons.getInnerTypeInsertions(), cons.getType(),
+        cons.getCriteria().getASTPath());
+  }
+
+  public SetMultimap<ASTRecord, Insertion> getPaths() {
+    return Multimaps.unmodifiableSetMultimap(astInsertions);
+  }
+
+  /**
+   * Scans the given tree with the given insertion list and returns the
+   * mapping from source position to insertion text.  The positions are sorted
+   * in decreasing order of index, so that inserting one doesn't throw
+   * off the index for a subsequent one.
+   *
+   * <p>
+   * <i>N.B.:</i> This method calls {@code scan()} internally.
+   * </p>
+   *
+   * @param node the tree to scan
+   * @param p the list of insertion criteria
+   * @return the source position to insertion text mapping
+   */
+  public SetMultimap<Pair<Integer, ASTPath>, Insertion>
+  getInsertionsByPosition(JCCompilationUnit node, List<Insertion> p) {
+    List<Insertion> uninserted = new ArrayList<Insertion>(p);
+    this.scan(node, uninserted);
+    // There may be many extra annotations in a .jaif file.  For instance,
+    // the .jaif file may be for an entire library, but its compilation
+    // units are processed one by one.
+    // However, we should warn about any insertions that were within the
+    // given compilation unit but still didn't get inserted.
+    List<? extends Tree> typeDecls = node.getTypeDecls();
+    for (Insertion i : uninserted) {
+      InClassCriterion c = i.getCriteria().getInClass();
+      if (c == null) {
+        continue;
+      }
+      for (Tree t : typeDecls) {
+        if (c.isSatisfiedBy(TreePath.getPath(node, t))) {
+          // Avoid warnings about synthetic generated methods.
+          // This test is too coarse, but is good enough for now.
+          // There are also synthetic local variables; maybe suppress
+          // warnings about them, too.
+          if (! (i.getCriteria().isOnMethod("<init>()V")
+                 || i.getCriteria().isOnLocalVariable())) {
+            // Should be made more user-friendly
+            System.err.printf("Found class %s, but unable to insert %s:%n  %s%n", c.className, i.getText(), i);
+          }
+        }
+      }
+    }
+    if (dbug.isEnabled()) {
+      // Output every insertion that was not given a position:
+      for (Insertion i : uninserted) {
+        System.err.println("Unable to insert: " + i);
+      }
+    }
+    dbug.debug("getPositions => %d positions%n", insertions.size());
+    return Multimaps.unmodifiableSetMultimap(insertions);
+  }
+
+  /**
+   * Scans the given tree with the given {@link Insertions} and returns
+   * the mapping from source position to insertion text.
+   *
+   * <p>
+   * <i>N.B.:</i> This method calls {@code scan()} internally.
+   * </p>
+   *
+   * @param node the tree to scan
+   * @param insertions the insertion criteria
+   * @return the source position to insertion text mapping
+   */
+  public SetMultimap<Pair<Integer, ASTPath>, Insertion>
+  getPositions(JCCompilationUnit node, Insertions insertions) {
+    List<Insertion> list = new ArrayList<Insertion>();
+    treePathCache.clear();
+    list.addAll(insertions.forOuterClass(node, ""));
+    for (JCTree decl : node.getTypeDecls()) {
+      if (decl.getTag() == JCTree.Tag.CLASSDEF) {
+        String name = ((JCClassDecl) decl).sym.className();
+        Collection<Insertion> forClass = insertions.forOuterClass(node, name);
+        list.addAll(forClass);
+      }
+    }
+    return getInsertionsByPosition(node, list);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/TypeArgumentCriterion.java b/annotation-file-utilities/src/annotator/find/TypeArgumentCriterion.java
new file mode 100644
index 0000000..ef5bb44
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/TypeArgumentCriterion.java
@@ -0,0 +1,62 @@
+package annotator.find;
+
+import java.util.List;
+
+import annotations.el.RelativeLocation;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.tree.JCTree;
+
+public class TypeArgumentCriterion implements Criterion {
+  private final String methodName;
+  private final RelativeLocation loc;
+
+  public TypeArgumentCriterion(String methodName, RelativeLocation loc) {
+    this.methodName = methodName;
+    this.loc = loc;
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path, Tree leaf) {
+    assert path == null || path.getLeaf() == leaf;
+    return isSatisfiedBy(path);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public boolean isSatisfiedBy(TreePath path) {
+    if (path == null || path.getParentPath() == null) { return false; }
+
+    TreePath parentPath = path.getParentPath();
+    Tree parent = parentPath.getLeaf();
+    List<? extends Tree> typeArgs;
+
+    switch (parent.getKind()) {
+    case MEMBER_REFERENCE:
+      typeArgs = ((JCTree.JCMemberReference) parent).getTypeArguments();
+      break;
+    case METHOD_INVOCATION:
+      typeArgs = ((JCTree.JCMethodInvocation) parent).getTypeArguments();
+      break;
+    default:
+      return isSatisfiedBy(parentPath);
+    }
+
+    return typeArgs != null
+        && loc.index >= 0 && loc.index < typeArgs.size()
+        && typeArgs.get(loc.index) == path.getLeaf();
+  }
+
+  @Override
+  public Kind getKind() {
+    return Kind.TYPE_ARGUMENT;
+  }
+
+  @Override
+  public String toString() {
+    return "TypeArgumentCriterion: in method: " + methodName
+        + " location: " + loc;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/TypeBoundExtendsInsertion.java b/annotation-file-utilities/src/annotator/find/TypeBoundExtendsInsertion.java
new file mode 100644
index 0000000..0615a25
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/TypeBoundExtendsInsertion.java
@@ -0,0 +1,41 @@
+package annotator.find;
+
+/**
+ * Specifies an insertion of an "extends @Annotation java.lang.Object" to a type
+ * bound.
+ */
+public class TypeBoundExtendsInsertion extends AnnotationInsertion {
+
+    /**
+     * Creates a new TypeBoundExtendsInsertion.
+     *
+     * @param text
+     *            the text to insert
+     * @param criteria
+     *            where to insert the text
+     * @param separateLine
+     *            whether to insert the text on its own
+     */
+    public TypeBoundExtendsInsertion(String text, Criteria criteria,
+            boolean separateLine) {
+        super(text, criteria, separateLine);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected String getText(boolean comments, boolean abbreviate) {
+        return "extends java.lang." + super.getText(comments, abbreviate)
+                + " Object";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected boolean addTrailingSpace(boolean gotSeparateLine) {
+        // Never add a trailing space for an extends insertion.
+        return false;
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/find/TypedInsertion.java b/annotation-file-utilities/src/annotator/find/TypedInsertion.java
new file mode 100644
index 0000000..2636e1b
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/TypedInsertion.java
@@ -0,0 +1,107 @@
+package annotator.find;
+
+import java.util.List;
+
+import type.ArrayType;
+import type.BoundedType;
+import type.DeclaredType;
+import type.Type;
+
+/**
+ * Superclass for {@link Insertion} classes for which insertion may
+ * result in code generation other than just annotations.
+ * {@code TypedInsertion}s keep track of insertions on inner types.  If
+ * there is no type given in the source, one may be generated (along
+ * with other code necessary in the context) to serve as an insertion
+ * site.
+ * <p>
+ * We don't know until the end of the whole insertion process whether
+ * the type already exists or not.  To remedy this, we store a reference
+ * to each insertion on an inner type of a receiver in two places: the
+ * global list of all insertions and the {@code TypedInsertion} that is
+ * the parent of the inner type insertion.  If the type is not already
+ * present, the inner type insertions are inserted into the new type and
+ * labeled as "inserted" (with {@link Insertion#setInserted(boolean)})
+ * so they are not inserted as the rest of the insertions list is
+ * processed.
+ *
+ * @author dbro
+ */
+public abstract class TypedInsertion extends Insertion {
+  /**
+   * The type for insertion.
+   */
+  protected Type type;
+
+  /**
+   * If true only the annotations from {@link type} will be inserted.
+   */
+  protected boolean annotationsOnly;
+
+  /**
+   * The inner types to go on this insertion. See {@link ReceiverInsertion}
+   * for more details.
+   */
+  protected List<Insertion> innerTypeInsertions;
+
+  public TypedInsertion(Type type, Criteria criteria,
+          List<Insertion> innerTypeInsertions) {
+      this(type, criteria, false, innerTypeInsertions);
+  }
+
+  public TypedInsertion(Type type, Criteria criteria, boolean b,
+          List<Insertion> innerTypeInsertions) {
+      super(criteria, b);
+      this.type = type;
+      this.innerTypeInsertions = innerTypeInsertions;
+      annotationsOnly = false;
+  }
+
+  /**
+   * If {@code true} only the annotations on {@code type} will be inserted.
+   * This is useful when the "new" has already been inserted.
+   */
+  public void setAnnotationsOnly(boolean annotationsOnly) {
+      this.annotationsOnly = annotationsOnly;
+  }
+
+  /**
+   * Sets the type.
+   */
+  public void setType(Type type) {
+      this.type = type;
+  }
+
+  /**
+   * Gets the type.  It is assumed that the returned value will be
+   * modified to update the type to be inserted.
+   */
+  public Type getType() {
+      return type;
+  }
+
+  /**
+   * Gets the inner type insertions associated with this insertion.
+   * @return a copy of the inner types
+   */
+  public List<Insertion> getInnerTypeInsertions() {
+      return innerTypeInsertions;
+  }
+
+  public DeclaredType getBaseType() {
+    return getBaseType(type);
+  }
+
+  public static DeclaredType getBaseType(Type type) {
+    switch (type.getKind()) {
+    case DECLARED:
+      return (DeclaredType) type;
+    case BOUNDED:
+      return getBaseType(((BoundedType) type).getName());
+    case ARRAY:
+      return getBaseType(((ArrayType) type).getComponentType());
+    default:  // should never be reached
+      return null;
+    }
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/find/package-info.java b/annotation-file-utilities/src/annotator/find/package-info.java
new file mode 100644
index 0000000..7e60dd8
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/find/package-info.java
@@ -0,0 +1,21 @@
+/**
+ * Provides interfaces and classes for finding where in the existing
+ * source to insert annotations etc.  {@link annotator.find.TreeFinder} manages the
+ * control flow and discovers the positions for insertion, relying on
+ * implementations of {@link annotator.find.Criterion} for determining whether an
+ * insertion should be made at a given location and on extensions of
+ * {@link annotator.find.Insertion} for the concrete text to be inserted.
+ * <p>
+ * The current flow, given a collection of insertions and an abstract
+ * syntax tree (AST) representing a Java source file, consists of a
+ * pre-order traversal of the AST to find insertion positions, followed
+ * by the insertion of text for each positioned {@link annotator.find.Insertion} into
+ * the source code, in reverse order by position.  At each annotatable
+ * node encountered during the traversal, the program checks the
+ * {@link annotator.find.Criteria} for each yet-unmatched {@link annotator.find.Insertion} against
+ * the current node; when there is a match, the program finds and
+ * records the appropriate source position.
+ *
+ * @see annotator.find.TreeFinder#getInsertionsByPosition(com.sun.tools.javac.tree.JCTree.JCCompilationUnit, java.util.List)
+ */
+package annotator.find;
diff --git a/annotation-file-utilities/src/annotator/scanner/AnonymousClassScanner.java b/annotation-file-utilities/src/annotator/scanner/AnonymousClassScanner.java
new file mode 100644
index 0000000..13d5afd
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/AnonymousClassScanner.java
@@ -0,0 +1,105 @@
+package annotator.scanner;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+
+/**
+ * AnonymousClassScanner determine the index of a tree for an anonymous
+ * class.  If the index is i, it is the ith anonymous class in the file.
+ * Thus, if i = 2, it will have a name of the form NamedClass$2.
+ */
+public class AnonymousClassScanner extends TreePathScanner<Void, Integer> {
+
+  /**
+   * Given an anonymous class, computes and returns its 1-based index in the given tree representing
+   * an anonymous class.
+   *
+   * @param path the source path ending in the anonymous class
+   * @param anonclass the anonymous class to search for
+   * @return the index of the anonymous class in the source code
+   */
+  public static int indexOfClassTree(TreePath path, Tree anonclass) {
+    // Move up to the CLASS tree enclosing this CLASS tree and start the tree
+    // traversal from there. This prevents us from counting anonymous classes
+    // that are in a different part of the tree and therefore aren't included
+    // in the index number.
+    int classesFound = 0;
+    boolean anonclassFound = false;
+    while (path.getParentPath() != null && classesFound < 1) {
+      if (path.getLeaf() == anonclass) {
+        anonclassFound = true;
+      }
+      path = path.getParentPath();
+      if (anonclassFound && CommonScanner.hasClassKind(path.getLeaf())) {
+        classesFound++;
+      }
+    }
+    AnonymousClassScanner lvts = new AnonymousClassScanner(anonclass);
+    lvts.scan(path, 0);
+    if (lvts.found) {
+      return lvts.index;
+    } else {
+      return -1;
+    }
+  }
+
+  private int index;
+  // top-level class doesn't count, so first index will be -1
+  private boolean found;
+  private Tree anonclass;
+
+  /**
+   * Creates a new AnonymousClassScanner that searches for the index of the given
+   * tree, representing an anonymous class.
+   *
+   * @param tree the anonymous class to search for
+   */
+  private AnonymousClassScanner(Tree anonclass) {
+    this.index = 1;             // start counting at 1
+    this.found = false;
+    this.anonclass = anonclass;
+  }
+
+  // Slightly tricky counting:  if the target item is a CLASS, only count
+  // CLASSes.  If it is a NEW_CLASS, only count NEW_CLASSes
+  // The level parameter keeps us from traversing too low in the tree and
+  // counting classes that aren't included in the index number.
+
+  @Override
+  public Void visitClass(ClassTree node, Integer level) {
+    if (level < 2) {
+      if (!found && CommonScanner.hasClassKind(anonclass)) {
+        if (anonclass == node) {
+          found = true;
+        } else if (node.getSimpleName().toString().trim().isEmpty()) {
+          // don't count classes with given names in source
+          index++;
+        }
+      }
+      super.visitClass(node, level + 1);
+    }
+    return null;
+  }
+
+  @Override
+  public Void visitNewClass(NewClassTree node, Integer level) {
+    // if (level < 2) {
+      if (!found && anonclass.getKind() == Tree.Kind.NEW_CLASS) {
+        if (anonclass == node) {
+          found = true;
+        } else if (node.getClassBody() != null) {
+          // Need to make sure you actually are creating anonymous inner class,
+          // not just object creation.
+          index++;
+        } else {
+          return null;
+        }
+      }
+      super.visitNewClass(node, level + 1);
+    // }
+    return null;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/CastScanner.java b/annotation-file-utilities/src/annotator/scanner/CastScanner.java
new file mode 100644
index 0000000..428e538
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/CastScanner.java
@@ -0,0 +1,115 @@
+package annotator.scanner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.util.TreePath;
+
+/** CastScanner stores information about the names and offsets of
+ * casts inside a method, and can also be used to scan the source
+ * tree and determine the index of a given cast, where the i^th
+ * index corresponds to the i^th cast, using 0-based indexing.
+ */
+public class CastScanner extends CommonScanner {
+
+  /**
+   * Computes the index of the given cast tree amongst all cast trees inside
+   * its method, using 0-based indexing.
+   *
+   * @param origpath the path ending in the given cast tree
+   * @param tree the cast tree to search for
+   * @return the index of the given cast tree
+   */
+  public static int indexOfCastTree(TreePath origpath, Tree tree) {
+    TreePath path = findCountingContext(origpath);
+    if (path == null) {
+      return -1;
+    }
+
+    CastScanner lvts = new CastScanner(tree);
+    lvts.scan(path, null);
+    return lvts.index;
+  }
+
+  private int index = -1;
+  private boolean done = false;
+  private final Tree tree;
+  private static String prevMethodName = null;
+  private static int prevOffset = -1;
+  private static int nestLevels = 0;
+
+  private CastScanner(Tree tree) {
+    this.index = -1;
+    this.done = false;
+    this.tree = tree;
+  }
+
+  @Override
+  public Void visitTypeCast(TypeCastTree node, Void p) {
+    if (!done) {
+      index++;
+    }
+    if (tree == node) {
+      done = true;
+      return p;
+    }
+    return super.visitTypeCast(node, p);
+  }
+
+  // Map from name of a method a list of bytecode offsets of all
+  // casts in that method.
+  private static Map<String,List<Integer>> methodNameToCastOffsets =
+    new HashMap<String, List<Integer>>();
+
+
+  /**
+   * Adds a cast bytecode offset to the current list of offsets for
+   * methodName.  This method must be called with monotonically increasing
+   * offsets for any one method.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset to add
+   */
+  public static void addCastToMethod(String methodName, Integer offset) {
+    List<  Integer> offsetList = methodNameToCastOffsets.get(methodName);
+    if (offsetList == null) {
+      offsetList = new ArrayList<  Integer>();
+      methodNameToCastOffsets.put(methodName, offsetList);
+    }
+    if (methodName.equals(prevMethodName) && offset-prevOffset == 3) {
+      // consecutive instructions -> nested casts -> reverse order!
+      // TODO: other cases for nested casts?
+      ++nestLevels;
+      offsetList.add(offsetList.size()-nestLevels, offset);
+    } else {
+      nestLevels = 0;
+      offsetList.add(offset);
+    }
+    prevMethodName = methodName;
+    prevOffset = offset;
+  }
+
+  /**
+   * Returns the index of the given offset within the list of offsets
+   * for the given method, using 0-based indexing,
+   * or returns a negative number if the offset is not one of the
+   * offsets in the method.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset of the instanceof check
+   * @return the index of the given offset, or a negative number if the
+   *  given offset does not exists inside the method
+   */
+  public static Integer getMethodCastIndex(String methodName, Integer offset) {
+    List<Integer> offsetList = methodNameToCastOffsets.get(methodName);
+    if (offsetList == null) {
+      return -1;
+    }
+
+    return offsetList.indexOf(offset);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/CommonScanner.java b/annotation-file-utilities/src/annotator/scanner/CommonScanner.java
new file mode 100644
index 0000000..0f65b8b
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/CommonScanner.java
@@ -0,0 +1,143 @@
+package annotator.scanner;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+
+/**
+ * The common base-class for all scanners that contains shared tree-traversal
+ * methods.
+ */
+public class CommonScanner extends TreePathScanner<Void, Void> {
+    public static boolean hasClassKind(Tree tree) {
+        Tree.Kind kind = tree.getKind();
+        // Tree.Kind.NEW_CLASS is excluded here because 1) there is no
+        // type name to be annotated on an anonymous inner class, and
+        // consequently 2) NEW_CLASS insertions are handled separately.
+        return kind == Tree.Kind.CLASS
+                || kind == Tree.Kind.INTERFACE
+                || kind == Tree.Kind.ENUM
+                || kind == Tree.Kind.ANNOTATION_TYPE;
+    }
+
+    /**
+     * The counting context for new, typecast, instanceof, and locals.
+     * This is a path to a method or a field/instance/static initializer.
+     */
+    public static TreePath findCountingContext(TreePath path) {
+        while (path != null) {
+            if (path.getLeaf().getKind() == Tree.Kind.METHOD ||
+                    isFieldInit(path) ||
+                    isInitBlock(path)) {
+                return path;
+            }
+            path = path.getParentPath();
+        }
+        return path;
+    }
+
+    // classes
+
+    public static TreePath findEnclosingClass(TreePath path) {
+        while (!hasClassKind(path.getLeaf())
+                || path.getParentPath().getLeaf().getKind() ==
+                    Tree.Kind.NEW_CLASS) {
+            path = path.getParentPath();
+            if (path == null) {
+                return null;
+            }
+        }
+        return path;
+    }
+
+    // methods
+
+    public static TreePath findEnclosingMethod(TreePath path) {
+        while (path.getLeaf().getKind() != Tree.Kind.METHOD) {
+            path = path.getParentPath();
+            if (path == null) {
+                return null;
+            }
+        }
+        return path;
+    }
+
+    // Field Initializers
+
+    public static boolean isFieldInit(TreePath path) {
+        return path.getLeaf().getKind() == Tree.Kind.VARIABLE
+                && path.getParentPath() != null
+                && hasClassKind(path.getParentPath().getLeaf());
+    }
+
+    public static TreePath findEnclosingFieldInit(TreePath path) {
+        while (!isFieldInit(path)) {
+            path = path.getParentPath();
+            if (path == null) {
+                return null;
+            }
+        }
+        return path;
+    }
+
+    // initializer blocks
+
+    public static boolean isInitBlock(TreePath path, boolean isStatic) {
+      return isInitBlock(path)
+              && ((BlockTree) path.getLeaf()).isStatic() == isStatic;
+    }
+
+    public static boolean isInitBlock(TreePath path) {
+        return path.getParentPath() != null
+                && hasClassKind(path.getParentPath().getLeaf())
+                && path.getLeaf().getKind() == Tree.Kind.BLOCK;
+    }
+
+    public static TreePath findEnclosingInitBlock(TreePath path,
+            boolean isStatic) {
+        while (!isInitBlock(path, isStatic)) {
+            path = path.getParentPath();
+            if (path == null) {
+                return null;
+            }
+        }
+        return path;
+    }
+
+    public static boolean isStaticInit(TreePath path) {
+        return isInitBlock(path, true);
+    }
+
+    public static TreePath findEnclosingStaticInit(TreePath path) {
+        while (!isStaticInit(path)) {
+            path = path.getParentPath();
+            if (path == null) {
+                return null;
+            }
+        }
+        return path;
+    }
+
+    public static boolean isInstanceInit(TreePath path) {
+        return isInitBlock(path, false);
+    }
+
+    public static TreePath findEnclosingInstanceInit(TreePath path) {
+        while (!isInstanceInit(path)) {
+            path = path.getParentPath();
+            if (path == null) {
+                return null;
+            }
+        }
+        return path;
+    }
+
+    // Don't scan into any classes so that occurrences in nested classes
+    // aren't counted.
+    @Override
+    public Void visitClass(ClassTree node, Void p) {
+        return p;
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/InitBlockScanner.java b/annotation-file-utilities/src/annotator/scanner/InitBlockScanner.java
new file mode 100644
index 0000000..f02a07e
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/InitBlockScanner.java
@@ -0,0 +1,56 @@
+package annotator.scanner;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+import com.sun.tools.javac.tree.JCTree.JCBlock;
+
+/**
+ * InitScanner scans the source tree and determines the index of a given
+ * initializer block, where index {@code i} corresponds to the (0-based)
+ * i^th initializer of the indicated kind (static or instance),
+ *
+ * @author dbro
+ */
+public class InitBlockScanner extends TreePathScanner<Void, Boolean> {
+    public static int indexOfInitTree(TreePath path, boolean isStatic) {
+        // we allow to start with any path/tree within an initializer.
+        // first go to the enclosing initializer
+        Tree tree =
+            CommonScanner.findEnclosingInitBlock(path, isStatic).getLeaf();
+        // find the enclosing class
+        path = CommonScanner.findEnclosingClass(path);
+        if (tree==null || path == null) {
+            return -1;
+        }
+        // find the index of the current initializer within the
+        // enclosing class
+        InitBlockScanner bts = new InitBlockScanner(tree);
+        bts.scan(path, isStatic);
+        return bts.index;
+    }
+
+    private int index = -1;
+    private boolean done = false;
+    private final Tree tree;
+
+    private InitBlockScanner(Tree tree) {
+        this.index = -1;
+        this.done = false;
+        this.tree = tree;
+    }
+
+    @Override
+    public Void visitBlock(BlockTree node, Boolean isStatic) {
+        // TODO: is isStatic only used for static initializer blocks?
+        if (!done && isStatic == node.isStatic()
+                && ((JCBlock) node).endpos >= 0) {
+            index++;
+        }
+        if (tree == node) {
+            done = true;
+        }
+        return super.visitBlock(node, isStatic);
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/InstanceOfScanner.java b/annotation-file-utilities/src/annotator/scanner/InstanceOfScanner.java
new file mode 100644
index 0000000..f6acd78
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/InstanceOfScanner.java
@@ -0,0 +1,105 @@
+package annotator.scanner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/** InstanceOfScanner stores information about the names and offsets of
+ * instanceof checks inside a method, and can also be used to scan the source
+ * tree and determine the index of a given instanceof check, where the i^th
+ * index corresponds to the i^th instanceof check, using 0-based indexing.
+ */
+public class InstanceOfScanner extends CommonScanner {
+
+  /**
+   * Computes the index of the given instanceof tree amongst all instanceof
+   * tree inside its method, using 0-based indexing.
+   *
+   * @param origpath the path ending in the given instanceof tree
+   * @param tree the instanceof tree to search for
+   * @return the index of the given instanceof tree
+   */
+  public static int indexOfInstanceOfTree(TreePath origpath, Tree tree) {
+    TreePath path = findCountingContext(origpath);
+    if (path == null) {
+      return -1;
+    }
+
+    InstanceOfScanner ios = new InstanceOfScanner(tree);
+    ios.scan(path, null);
+    return ios.index;
+  }
+
+  private int index = -1;
+  private boolean done = false;
+  private final Tree tree;
+
+  /**
+   * Creates an InstanceOfScanner that will scan the source tree for the
+   *  given node representing the instanceof check to find.
+   * @param tree the given instanceof check to search for
+   */
+  private InstanceOfScanner(Tree tree) {
+    this.index = -1;
+    this.done = false;
+    this.tree = tree;
+  }
+
+  @Override
+  public Void visitInstanceOf(InstanceOfTree node, Void p) {
+    if (!done) {
+      index++;
+    }
+    if (tree == node) {
+      done = true;
+    }
+    return super.visitInstanceOf(node, p);
+  }
+
+  // Map from name of a method to a list of bytecode offsets of all
+  // instanceof checks in that method.
+  private static Map<String, List<Integer>> methodNameToInstanceOfOffsets =
+    new HashMap<String, List<Integer>>();
+
+  /**
+   * Adds an instanceof bytecode offset to the current list of offsets for
+   * methodName.  This method must be called with monotonically increasing
+   * offsets for any one method.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset to add
+   */
+  public static void addInstanceOfToMethod(String methodName, Integer offset) {
+    List<  Integer> offsetList = methodNameToInstanceOfOffsets.get(methodName);
+    if (offsetList == null) {
+      offsetList = new ArrayList<  Integer>();
+      methodNameToInstanceOfOffsets.put(methodName, offsetList);
+    }
+    offsetList.add(offset);
+  }
+
+  /**
+   * Returns the index of the given offset within the list of offsets
+   * for the given method, using 0-based indexing,
+   * or returns a negative number if the offset is not one of the
+   * offsets in the method.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset of the instanceof check
+   * @return the index of the given offset, or a negative number
+   *  if the offset does not exist inside the method
+   */
+  public static Integer getMethodInstanceOfIndex(String methodName, Integer offset)  {
+    List<  Integer> offsetList = methodNameToInstanceOfOffsets.get(methodName);
+    if (offsetList == null) {
+      return -1;
+    }
+
+    return offsetList.indexOf(offset);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/LambdaScanner.java b/annotation-file-utilities/src/annotator/scanner/LambdaScanner.java
new file mode 100644
index 0000000..58f6e45
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/LambdaScanner.java
@@ -0,0 +1,109 @@
+package annotator.scanner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+/**
+ * LambdaScanner stores information about the names and offsets of
+ * lambda expressions inside a method, and can also be used to scan the
+ * source tree and determine the index of a given instanceof check,
+ * where the i^th index corresponds to the i^th instanceof check, using
+ * 0-based indexing.
+ */
+public class LambdaScanner extends CommonScanner {
+
+  /**
+   * Computes the index of the given lambda expression tree amongst all
+   * lambda expression trees inside its method, using 0-based indexing.
+   *
+   * @param origpath the path ending in the given lambda expression tree
+   * @param tree the lambda expression tree to search for
+   * @return the index of the given lambda expression tree
+   */
+  public static int indexOfLambdaExpressionTree(TreePath origpath, Tree tree) {
+    TreePath path = findCountingContext(origpath);
+    if (path == null) {
+      return -1;
+    }
+
+    LambdaScanner ls = new LambdaScanner(tree);
+    ls.scan(path, null);
+    return ls.index;
+  }
+
+  private int index;
+  private boolean done;
+  private final Tree tree;
+
+  /**
+   * Creates an InstanceOfScanner that will scan the source tree for the
+   *  given node representing the lambda expression to find.
+   * @param tree the given lambda expression to search for
+   */
+  private LambdaScanner(Tree tree) {
+    this.index = -1;
+    this.done = false;
+    this.tree = tree;
+  }
+
+  @Override
+  public Void visitLambdaExpression(LambdaExpressionTree node, Void p) {
+    if (!done) {
+      index++;
+    }
+    if (tree == node) {
+      done = true;
+    }
+    return super.visitLambdaExpression(node, p);
+  }
+
+  // Map from name of a method to a list of bytecode offsets of all
+  // lambda expressions in that method.
+  private static Map<String, List<Integer>> methodNameToLambdaExpressionOffsets =
+      new HashMap<String, List<Integer>>();
+
+  /**
+   * Adds a lambda expression bytecode offset to the current list of
+   * offsets for methodName.  This method must be called with
+   * monotonically increasing offsets for any one method.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset to add
+   */
+  public static void addLambdaExpressionToMethod(String methodName, Integer offset) {
+    List<Integer> offsetList =
+        methodNameToLambdaExpressionOffsets.get(methodName);
+    if (offsetList == null) {
+      offsetList = new ArrayList<Integer>();
+      methodNameToLambdaExpressionOffsets.put(methodName, offsetList);
+    }
+    offsetList.add(offset);
+  }
+
+  /**
+   * Returns the index of the given offset within the list of offsets
+   * for the given method, using 0-based indexing, or returns a negative
+   * number if the offset is not one of the offsets in the context.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset of the lambda expression
+   * @return the index of the given offset, or a negative number
+   *  if the offset does not exist inside the context
+   */
+  public static Integer
+  getMethodLambdaExpressionIndex(String methodName, Integer offset) {
+    List<Integer> offsetList =
+        methodNameToLambdaExpressionOffsets.get(methodName);
+    if (offsetList == null) {
+      return -1;
+    }
+
+    return offsetList.indexOf(offset);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/LocalClassScanner.java b/annotation-file-utilities/src/annotator/scanner/LocalClassScanner.java
new file mode 100644
index 0000000..d1ef277
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/LocalClassScanner.java
@@ -0,0 +1,89 @@
+package annotator.scanner;
+
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
+
+/**
+ * LocalClassScanner determines the index of a tree for a local
+ * class. If the index is i, it is the ith local class with the class name in
+ * the file. Thus, if i = 2, it will have a name of the form
+ * OuterClass$2InnerClass.
+ */
+public class LocalClassScanner extends TreePathScanner<Void, Integer> {
+
+  /**
+   * Given a local class, computes and returns its 1-based index in the given
+   * tree representing a local class.
+   *
+   * @param path the source path ending in the local class
+   * @param localClass the local class to search for
+   * @return the index of the local class in the source code
+   */
+  public static int indexOfClassTree(TreePath path, ClassTree localClass) {
+    // Move up to the CLASS tree enclosing this CLASS tree and start the tree
+    // traversal from there. This prevents us from counting local classes that
+    // are in a different part of the tree and therefore aren't included in the
+    // index number.
+    int classesFound = 0;
+    boolean localClassFound = false;
+    while (path.getParentPath() != null && classesFound < 1) {
+      if (path.getLeaf() == localClass) {
+        localClassFound = true;
+      }
+      path = path.getParentPath();
+      if (localClassFound && path.getLeaf().getKind() == Tree.Kind.CLASS) {
+        classesFound++;
+      }
+    }
+    LocalClassScanner lcs = new LocalClassScanner(localClass);
+    lcs.scan(path, 0);
+    if (lcs.found) {
+      return lcs.index;
+    } else {
+      return -1;
+    }
+  }
+
+  private int index;
+  private boolean found;
+  private ClassTree localClass;
+
+  /**
+   * Creates a new LocalClassScanner that searches for the index of the given
+   * tree, representing a local class.
+   *
+   * @param localClass the local class to search for
+   */
+  private LocalClassScanner(ClassTree localClass) {
+    this.index = 1;
+    this.found = false;
+    this.localClass = localClass;
+  }
+
+  // The level parameter keeps us from traversing too low in the tree and
+  // counting classes that aren't included in the index number.
+
+  @Override
+  public Void visitBlock(BlockTree node, Integer level) {
+    if (level < 1) {
+      // Visit blocks since a local class can only be in a block. Then visit each
+      // statement of the block to see if any are the correct local class.
+      for (StatementTree statement : node.getStatements()) {
+        if (!found && statement.getKind() == Tree.Kind.CLASS) {
+          ClassTree c = (ClassTree) statement;
+          if (localClass == statement) {
+            found = true;
+          } else if (c.getSimpleName().equals(localClass.getSimpleName())) {
+            index++;
+          }
+        }
+      }
+      super.visitBlock(node, level + 1);
+    }
+    return null;
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/LocalVariableScanner.java b/annotation-file-utilities/src/annotator/scanner/LocalVariableScanner.java
new file mode 100644
index 0000000..30b6c15
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/LocalVariableScanner.java
@@ -0,0 +1,167 @@
+package annotator.scanner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.util.Pair;
+
+/** LocalVariableScanner stores information about the names and offsets of
+ * local variables inside a method, and can also be used to scan the source
+ * tree and determine the index of a local variable with a given name, so that
+ * the i^th index corresponds to the i^th declaration of a local variable with
+ * that name, using 0-based indexing.
+ */
+public class LocalVariableScanner extends CommonScanner {
+  /**
+   * Computes the index i of the given tree along the given tree path
+   * such that it is the i^th declaration of the local variable with the given
+   * var name, using 0-based indexing.
+   *
+   * @param origpath the source path that ends in varTree
+   * @param varTree the variable tree that declares the local variable
+   * @param varName the name of the local variable
+   * @return the index of the variable tree with respect to the given
+   *  local variable name
+   */
+  public static int indexOfVarTree(TreePath origpath, Tree varTree, String varName) {
+    TreePath path = findCountingContext(origpath);
+    if (path == null) {
+      return -1;
+    }
+
+    LocalVariableScanner lvts = new LocalVariableScanner(varTree, varName);
+
+    try {
+      lvts.scan(path, null);
+    } catch (Throwable e) {
+      System.out.println("LocalVariableScanner: can't locate: " + varTree);
+      return -2; // Don't return -1, which is above return code
+    }
+    return lvts.index;
+  }
+
+  /*
+   * For efficiency, we might want to have methods like the following, specialized for the
+   * three different cases.
+   */
+  /*
+  public static int indexOfVarTreeInStaticInit(TreePath path, Tree tree, String varName) {
+    // only start searching from within this method
+      path = findEnclosingStaticInit(path);
+      if (path == null) {
+        return -1;
+      }
+
+      LocalVariableScanner lvts = new LocalVariableScanner(tree, varName);
+      lvts.scan(path, null);
+      return lvts.index;
+  }
+  */
+
+  private int index = -1;
+  private boolean done = false;
+  private final Tree varTree;
+  private final String varName;
+
+  private LocalVariableScanner(Tree varTree, String varName) {
+    this.index = -1;
+    this.done = false;
+    this.varTree = varTree;
+    this.varName = varName;
+  }
+
+  @Override
+  public Void visitVariable(VariableTree node, Void p) {
+    // increment index only if you have not already reached the right node, and
+    // if this node declares the same local variable you are searching for
+    if (varName.equals(node.getName().toString())) {
+      if (!done) {
+        index++;
+      }
+      if (varTree == node) {
+        done = true;
+      }
+    }
+    return p;
+  }
+
+  // TODO: refactor class keys to avoid so many uses of generics
+
+  // mapping from (method-name, variable-index, start-offset)
+  // to variable name
+  private static Map<Pair<String, Pair<Integer,Integer>>, String>
+    methodNameIndexMap = new HashMap<Pair<String, Pair<Integer,Integer>>, String>();
+
+  // map from method to map from variable name to
+  // a list of start offsets
+  private static Map<String, Map<String, List<Integer>>>
+    methodNameCounter = new HashMap<String, Map<String,List<Integer>>>();
+
+  /**
+   * Adds the given variable specified as a pair of method name and
+   *  (index, start-offset) under the given name to the list of all local
+   *  variables.
+   *
+   * @param varInfo a pair of the method and a pair describing the local
+   *  variable index and start offset of the local variable
+   * @param name the name of the local variable
+   */
+  public static void addToMethodNameIndexMap(Pair<String, Pair<Integer,Integer>> varInfo, String name) {
+    methodNameIndexMap.put(varInfo, name);
+  }
+
+  /**
+   * Gets the name of the local variable in the given method, and at the
+   *  the given index and offset.
+   *
+   * @param varInfo a pair of the method name and a pair of the local variable's
+   *  index and start offset
+   * @return the name of the local variable at the specified location
+   */
+  public static String getFromMethodNameIndexMap(Pair<String, Pair<Integer, Integer>> varInfo) {
+    return methodNameIndexMap.get(varInfo);
+  }
+
+  /**
+   * Adds to the given method the fact that the local variable with the given
+   *  name is declared at the given start offset.
+   *
+   * @param methodName the method containing the local variable
+   * @param varName the name of the local variable
+   * @param offset the start offset of the local variable
+   */
+  public static void addToMethodNameCounter(String methodName, String varName,
+        Integer offset) {
+    Map<String, List<Integer>> nameOffsetCounter = methodNameCounter.get(methodName);
+    if (nameOffsetCounter == null) {
+      nameOffsetCounter = new HashMap<String, List<Integer>>();
+      methodNameCounter.put(methodName, nameOffsetCounter);
+    }
+
+    List<  Integer> listOfOffsets = nameOffsetCounter.get(varName);
+    if (listOfOffsets == null) {
+      listOfOffsets = new ArrayList<  Integer>();
+      nameOffsetCounter.put(varName, listOfOffsets);
+    }
+
+    listOfOffsets.add(offset);
+  }
+
+  /**
+   * Returns a list of all start bytecode offsets of variable declarations with
+   * the given variable name in the given method.
+   *
+   * @param methodName the name of the method
+   * @param varName the name of the local variable
+   * @return a list of start offsets for live ranges of all local variables
+   * with the given name in the given method
+   */
+  public static List<Integer> getFromMethodNameCounter(String methodName, String varName) {
+    return methodNameCounter.get(methodName).get(varName);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/MemberReferenceScanner.java b/annotation-file-utilities/src/annotator/scanner/MemberReferenceScanner.java
new file mode 100644
index 0000000..c3cbf44
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/MemberReferenceScanner.java
@@ -0,0 +1,102 @@
+package annotator.scanner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.source.tree.MemberReferenceTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class MemberReferenceScanner extends CommonScanner {
+
+  /**
+   * Computes the index of the given method invocation amongst all
+   * method invocation trees inside its method, using 0-based indexing.
+   *
+   * @param origpath the path ending in the given method invocation tree
+   * @param tree the method invocation tree to search for
+   * @return the index of the given method invocation tree
+   */
+  public static int indexOfMemberReferenceTree(TreePath origpath, Tree tree) {
+    TreePath path = findCountingContext(origpath);
+    if (path == null) {
+      return -1;
+    }
+
+    MemberReferenceScanner mcs = new MemberReferenceScanner(tree);
+    mcs.scan(path, null);
+    return mcs.index;
+  }
+
+  private int index;
+  private boolean done;
+  private final Tree tree;
+
+  /**
+   * Creates an InstanceOfScanner that will scan the source tree for the
+   *  given node representing the method invocation to find.
+   * @param tree the given method invocation to search for
+   */
+  private MemberReferenceScanner(Tree tree) {
+    this.index = -1;
+    this.done = false;
+    this.tree = tree;
+  }
+
+  @Override
+  public Void visitMemberReference(MemberReferenceTree node, Void p) {
+    if (!done) {
+      index++;
+    }
+    if (tree == node) {
+      done = true;
+    }
+    return super.visitMemberReference(node, p);
+  }
+
+  // Map from name of a method to a list of bytecode offsets of all
+  // lambda expressions in that method.
+  private static Map<String, List<Integer>> methodNameToMemberReferenceOffsets =
+      new HashMap<String, List<Integer>>();
+
+  /**
+   * Adds a member reference bytecode offset to the current list of
+   * offsets for methodName.  This method must be called with
+   * monotonically increasing offsets for any one method.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset to add
+   */
+  public static void addMemberReferenceToMethod(String methodName, Integer offset) {
+    List<Integer> offsetList =
+        methodNameToMemberReferenceOffsets.get(methodName);
+    if (offsetList == null) {
+      offsetList = new ArrayList<Integer>();
+      methodNameToMemberReferenceOffsets.put(methodName, offsetList);
+    }
+    offsetList.add(offset);
+  }
+
+  /**
+   * Returns the index of the given offset within the list of offsets
+   * for the given method, using 0-based indexing, or returns a negative
+   * number if the offset is not one of the offsets in the context.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset of the lambda expression
+   * @return the index of the given offset, or a negative number
+   *  if the offset does not exist inside the context
+   */
+  public static Integer
+  getMemberReferenceIndex(String methodName, Integer offset) {
+    List<Integer> offsetList =
+        methodNameToMemberReferenceOffsets.get(methodName);
+    if (offsetList == null) {
+      return -1;
+    }
+
+    return offsetList.indexOf(offset);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/MethodCallScanner.java b/annotation-file-utilities/src/annotator/scanner/MethodCallScanner.java
new file mode 100644
index 0000000..a104519
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/MethodCallScanner.java
@@ -0,0 +1,102 @@
+package annotator.scanner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+public class MethodCallScanner extends CommonScanner {
+
+  /**
+   * Computes the index of the given method invocation amongst all
+   * method invocation trees inside its method, using 0-based indexing.
+   *
+   * @param origpath the path ending in the given method invocation tree
+   * @param tree the method invocation tree to search for
+   * @return the index of the given method invocation tree
+   */
+  public static int indexOfMethodCallTree(TreePath origpath, Tree tree) {
+    TreePath path = findCountingContext(origpath);
+    if (path == null) {
+      return -1;
+    }
+
+    MethodCallScanner mcs = new MethodCallScanner(tree);
+    mcs.scan(path, null);
+    return mcs.index;
+  }
+
+  private int index;
+  private boolean done;
+  private final Tree tree;
+
+  /**
+   * Creates an InstanceOfScanner that will scan the source tree for the
+   *  given node representing the method invocation to find.
+   * @param tree the given method invocation to search for
+   */
+  private MethodCallScanner(Tree tree) {
+    this.index = -1;
+    this.done = false;
+    this.tree = tree;
+  }
+
+  @Override
+  public Void visitMethodInvocation(MethodInvocationTree node, Void p) {
+    if (!done) {
+      index++;
+    }
+    if (tree == node) {
+      done = true;
+    }
+    return super.visitMethodInvocation(node, p);
+  }
+
+  // Map from name of a method to a list of bytecode offsets of all
+  // method invocations in that method.
+  private static Map<String, List<Integer>> methodNameToMethodCallOffsets =
+      new HashMap<String, List<Integer>>();
+
+  /**
+   * Adds a lambda expression bytecode offset to the current list of
+   * offsets for methodName.  This method must be called with
+   * monotonically increasing offsets for any one method.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset to add
+   */
+  public static void addMethodCallToMethod(String methodName, Integer offset) {
+    List<Integer> offsetList =
+        methodNameToMethodCallOffsets.get(methodName);
+    if (offsetList == null) {
+      offsetList = new ArrayList<Integer>();
+      methodNameToMethodCallOffsets.put(methodName, offsetList);
+    }
+    offsetList.add(offset);
+  }
+
+  /**
+   * Returns the index of the given offset within the list of offsets
+   * for the given method, using 0-based indexing, or returns a negative
+   * number if the offset is not one of the offsets in the context.
+   *
+   * @param methodName the name of the method
+   * @param offset the offset of the lambda expression
+   * @return the index of the given offset, or a negative number
+   *  if the offset does not exist inside the context
+   */
+  public static Integer
+  getMethodCallIndex(String methodName, Integer offset) {
+    List<Integer> offsetList =
+        methodNameToMethodCallOffsets.get(methodName);
+    if (offsetList == null) {
+      return -1;
+    }
+
+    return offsetList.indexOf(offset);
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/MethodOffsetClassVisitor.java b/annotation-file-utilities/src/annotator/scanner/MethodOffsetClassVisitor.java
new file mode 100644
index 0000000..7834ca6
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/MethodOffsetClassVisitor.java
@@ -0,0 +1,219 @@
+package annotator.scanner;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.TypePath;
+
+import annotations.io.classfile.CodeOffsetAdapter;
+
+import com.sun.tools.javac.util.Pair;
+
+
+/**
+ * MethodOffsetClassVisitor is a class visitor that should be passed to
+ * ASM's ClassReader in order to retrieve extra information about method
+ * offsets needed by all of the annotator.scanner classes.  This visitor
+ * should visit every class that is to be annotated, and should be done
+ * before trying to match elements in the tree to the various criterion.
+ */
+// Note: in order to ensure all labels are visited, this class
+// needs to extend ClassWriter and not other class visitor classes.
+// There is no good reason why this is the case with ASM.
+public class MethodOffsetClassVisitor extends ClassWriter {
+  CodeOffsetAdapter coa;
+  MethodVisitor mcoa;
+
+  // This field should be set by entry on a method through visitMethod,
+  // and so all the visit* methods in LocalVariableMethodVisitor
+  private String methodName;
+
+  public MethodOffsetClassVisitor(ClassReader cr) {
+    super(true, false);
+    this.methodName = "LocalVariableVisitor: DEFAULT_METHOD";
+    coa = new CodeOffsetAdapter(cr);
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access, String name,
+        String desc, String signature, String[  ] exceptions) {
+    methodName = name + desc.substring(0, desc.indexOf(")") + 1);
+    mcoa = coa.visitMethod(access, name, desc, signature, exceptions);
+    return new MethodOffsetMethodVisitor(
+        super.visitMethod(access, name, desc, signature, exceptions));
+  }
+
+  /**
+   * MethodOffsetMethodVisitor is the method visitor that
+   * MethodOffsetClassVisitor uses to visit particular methods and gather
+   * all the offset information by calling the appropriate static
+   * methods in annotator.scanner classes.
+   */
+  private class MethodOffsetMethodVisitor extends MethodAdapter {
+    private Label lastLabel;
+
+    public MethodOffsetMethodVisitor(MethodVisitor mv) {
+      super(mv);
+      lastLabel = null;
+    }
+
+    private int labelOffset() {
+      try {
+        return lastLabel.getOffset();
+      } catch (Exception ex) {
+        return 0;  // TODO: find a better default?
+      }
+    }
+
+    @Override
+    public void visitLocalVariable(String name, String desc,
+          String signature, Label start, Label end, int index)  {
+      super.visitLocalVariable(name, desc, signature, start, end, index);
+      LocalVariableScanner.addToMethodNameIndexMap(
+          Pair.of(methodName, Pair.of(index, start.getOffset())),
+          name);
+      LocalVariableScanner.addToMethodNameCounter(
+          methodName, name, start.getOffset());
+      mcoa.visitLocalVariable(name, desc, signature, start, end, index);
+    }
+
+    @Override
+    public void visitLabel(Label label) {
+      super.visitLabel(label);
+      lastLabel = label;
+      mcoa.visitLabel(label);
+    }
+
+    @Override
+    public void visitTypeInsn(int opcode,  String desc)   {
+      super.visitTypeInsn(opcode, desc);
+      switch (opcode) {
+      case Opcodes.CHECKCAST:
+        // CastScanner.addCastToMethod(methodName, labelOffset() + 1);
+        CastScanner.addCastToMethod(methodName,
+            coa.getMethodCodeOffset());
+        break;
+      case Opcodes.NEW:
+      case Opcodes.ANEWARRAY:
+        NewScanner.addNewToMethod(methodName, labelOffset());
+        break;
+      case Opcodes.INSTANCEOF:
+        InstanceOfScanner.addInstanceOfToMethod(methodName,
+            labelOffset() + 1);
+        break;
+      }
+      mcoa.visitTypeInsn(opcode, desc);
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(String desc, int dims)  {
+      super.visitMultiANewArrayInsn(desc, dims);
+      NewScanner.addNewToMethod(methodName, labelOffset());
+      mcoa.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    @Override
+    public void visitIntInsn(int opcode, int operand)  {
+      super.visitIntInsn(opcode, operand);
+      if (opcode == Opcodes.NEWARRAY) {
+        NewScanner.addNewToMethod(methodName, labelOffset());
+      }
+      mcoa.visitIntInsn(opcode, operand);
+    }
+
+    @Override
+    public void visitMethodInsn(int opcode, String owner, String name,
+        String desc) {
+      super.visitMethodInsn(opcode, owner, name, desc);
+      switch (opcode) {
+      case Opcodes.INVOKEINTERFACE:
+      case Opcodes.INVOKESTATIC:
+      case Opcodes.INVOKEVIRTUAL:
+        MethodCallScanner.addMethodCallToMethod(methodName,
+            labelOffset());
+        break;
+      default:
+        break;
+      }
+      mcoa.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc,
+        Handle bsm, Object... bsmArgs) {
+      super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+      LambdaScanner.addLambdaExpressionToMethod(methodName,
+          labelOffset());
+      mcoa.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+    }
+
+    @Override
+    public void visitCode() {
+      super.visitCode();
+      mcoa.visitCode();
+    }
+
+    @Override
+    public void visitInsn(int opcode) {
+      super.visitInsn(opcode);
+      mcoa.visitInsn(opcode);
+    }
+
+    @Override
+    public void visitVarInsn(int opcode, int var) {
+      super.visitVarInsn(opcode, var);
+      mcoa.visitVarInsn(opcode, var);
+    }
+
+    @Override
+    public void visitFieldInsn(int opcode, String owner, String name,
+        String desc) {
+      super.visitFieldInsn(opcode, owner, name, desc);
+      mcoa.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    @Override
+    public void visitJumpInsn(int opcode, Label label) {
+      super.visitJumpInsn(opcode, label);
+      mcoa.visitJumpInsn(opcode, label);
+    }
+
+    @Override
+    public void visitLdcInsn(Object cst) {
+      super.visitLdcInsn(cst);
+      mcoa.visitLdcInsn(cst);
+    }
+
+    @Override
+    public void visitIincInsn(int var, int increment) {
+      super.visitIincInsn(var, increment);
+      mcoa.visitIincInsn(var, increment);
+    }
+
+    @Override
+    public void visitTableSwitchInsn(int min, int max, Label dflt,
+        Label[] labels) {
+      super.visitTableSwitchInsn(min, max, dflt, labels);
+      mcoa.visitTableSwitchInsn(min, max, dflt, labels);
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+      super.visitLookupSwitchInsn(dflt, keys, labels);
+      mcoa.visitLookupSwitchInsn(dflt, keys, labels);
+    }
+
+    @Override
+    public void visitEnd() {
+      super.visitEnd();
+      mcoa.visitEnd();
+    }
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/scanner/NewScanner.java b/annotation-file-utilities/src/annotator/scanner/NewScanner.java
new file mode 100644
index 0000000..5c18827
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/scanner/NewScanner.java
@@ -0,0 +1,123 @@
+package annotator.scanner;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.util.TreePath;
+
+import plume.Pair;
+
+/**
+ * NewScanner scans the source tree and determines the index of a given new,
+ * where the i^th index corresponds to the i^th new, using 0-based indexing.
+ */
+public class NewScanner extends CommonScanner {
+    private static boolean debug = false;
+
+    static Map<Pair<TreePath,Tree>, Integer> cache = new HashMap<Pair<TreePath,Tree>, Integer>();
+
+    /**
+     * Computes the index of the given new tree amongst all new trees
+     * inside its method, using 0-based indexing. The tree has to be
+     * either a NewClassTree or a NewArrayTree.  If the tree is not in
+     * a method, then the index is computed
+     *
+     * @param origpath
+     *            the path ending in the given cast tree
+     * @param tree
+     *            the cast tree to search for
+     * @return the index of the given cast tree
+     */
+    public static int indexOfNewTree(TreePath origpath, Tree tree) {
+        debug("indexOfNewTree: " + origpath.getLeaf());
+
+        Pair<TreePath,Tree> args = Pair.of(origpath, tree);
+        if (cache.containsKey(args)) {
+            return cache.get(args);
+        }
+
+        TreePath path = findCountingContext(origpath);
+        if (path == null) {
+            return -1;
+        }
+
+        NewScanner lvts = new NewScanner(tree);
+        lvts.scan(path, null);
+        cache.put(args, lvts.index);
+
+        return lvts.index;
+    }
+
+    private int index = -1;
+    private boolean done = false;
+    private final Tree tree;
+
+    private NewScanner(Tree tree) {
+        this.index = -1;
+        this.done = false;
+        this.tree = tree;
+    }
+
+    @Override
+    public Void visitNewClass(NewClassTree node, Void p) {
+        if (!done) {
+            index++;
+        }
+        if (tree == node) {
+            done = true;
+        }
+        return super.visitNewClass(node, p);
+    }
+
+    @Override
+    public Void visitNewArray(NewArrayTree node, Void p) {
+        if (!done) {
+            index++;
+        }
+        if (tree == node) {
+            done = true;
+        }
+        return super.visitNewArray(node, p);
+    }
+
+    public static void debug(String s) {
+        if (debug) {
+            System.out.println(s);
+        }
+    }
+
+    private static Map<String, List<Integer>> methodNameToNewOffsets =
+            new HashMap<String, List<Integer>>();
+
+    public static void addNewToMethod(String methodName, Integer offset) {
+        debug("adding new to method: " + methodName + " offset: " + offset);
+        List<Integer> offsetList = methodNameToNewOffsets.get(methodName);
+        if (offsetList == null) {
+            offsetList = new ArrayList<Integer>();
+            methodNameToNewOffsets.put(methodName, offsetList);
+        }
+        offsetList.add(offset);
+    }
+
+    public static Integer getMethodNewIndex(String methodName, Integer offset) {
+        List<Integer> offsetList = methodNameToNewOffsets.get(methodName);
+        if (offsetList == null) {
+            throw new RuntimeException("NewScanner.getMethodNewIndex() : "
+                    + "did not find offsets for method: " + methodName);
+        }
+
+        Integer offsetIndex = offsetList.indexOf(offset);
+        if (offsetIndex < 0) {
+            throw new RuntimeException("NewScanner.getMethodNewIndex() : "
+                    + "in method: " + methodName + " did not find offset: "
+                    + offset);
+        }
+
+        return offsetIndex;
+    }
+}
diff --git a/annotation-file-utilities/src/annotator/specification/CriterionList.java b/annotation-file-utilities/src/annotator/specification/CriterionList.java
new file mode 100644
index 0000000..5580059
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/specification/CriterionList.java
@@ -0,0 +1,95 @@
+package annotator.specification;
+
+import annotator.find.Criteria;
+import annotator.find.Criterion;
+
+// The notion of a CriterionList as a list of independent elements (with the
+// list being satisfied if each of its elements is) is broken.  For example, a
+// class may have an inner class of the same name, and the list would be
+// satisfied for either one.  The same goes for method names, and (most
+// problematically, because it comes up most often) for relative location in
+// GenericArrayLocationCriterion, where the criterion [1] would also match [2
+// 1] because it is a suffix of it, or [2 1] would also match [3 2 1].  A
+// related problem is the idea of checking from the bottom to the top of a
+// TreePath whether it satisfies a criterion.  I have put into place piecemeal
+// fixes for some of these, but really the CriterionList needs to be turned
+// into a path that is followed, and checking needs to start at the top of the
+// tree (at the CompilationUnit). -MDE 9/2009
+
+/**
+ * A CriterionList is a singly-linked list of Criterion meant to be treated
+ * as a stack.  It is useful for creating base criteria and passing
+ * independent copies to different parts of a specification that creates
+ * all the criterion.  A CriterionList is immutable, and so copies
+ * created by the add() function can safely be passed anywhere.
+ * It is supposed to be easier to manipulate than a Criteria.
+ */
+public class CriterionList {
+  // This really is a simple data structure to facilitate creation
+  //  of specifications.  TODO: make it a private class?
+  private Criterion current;
+  private CriterionList next;
+
+  /**
+   * Creates a new CriterionList with no criterion.
+   */
+  public CriterionList() {
+    next = null;
+    current = null;
+  }
+
+  /**
+   * Creates a new CriterionList containing just the given Criterion.
+   *
+   * @param c the sole criterion the list contains at the moment
+   */
+  public CriterionList(Criterion c) {
+    current = c;
+    next = null;
+  }
+
+  private CriterionList(Criterion c, CriterionList n) {
+    current = c;
+    next = n;
+  }
+
+  /**
+   * Adds the given criterion to the present list and returns a
+   * newly-allocated list containing the result.  Does not modify its
+   * argument.
+   *
+   * @param c the criterion to add
+   * @return a new list containing the given criterion and the rest of the
+   *  criterion already in this list
+   */
+  public CriterionList add(Criterion c) {
+    return new CriterionList(c, this);
+  }
+
+  /**
+   * Creates a Criteria object representing all the criterion in this list.
+   *
+   * @return a Criteria that contains all the criterion in this list
+   */
+  public Criteria criteria() {
+    Criteria criteria = new Criteria();
+
+    CriterionList c = this;
+    while (c != null && c.current != null) {
+      criteria.add(c.current);
+      c = c.next;
+    }
+
+    return criteria;
+  }
+
+  @Override
+  public String toString() {
+    if (current == null) { return "[]"; }
+    StringBuilder sb = new StringBuilder("[").append(current);
+    for (CriterionList n = next; n.next != null ; n = n.next) {
+      sb.append(", ").append(n.current);
+    }
+    return sb.append("]").toString();
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java b/annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java
new file mode 100644
index 0000000..79c1583
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/specification/IndexFileSpecification.java
@@ -0,0 +1,744 @@
+package annotator.specification;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.objectweb.asm.ClassReader;
+
+import plume.FileIOException;
+import plume.Pair;
+import type.DeclaredType;
+import type.Type;
+import annotations.Annotation;
+import annotations.el.ABlock;
+import annotations.el.AClass;
+import annotations.el.AElement;
+import annotations.el.AExpression;
+import annotations.el.AField;
+import annotations.el.AMethod;
+import annotations.el.AScene;
+import annotations.el.ATypeElement;
+import annotations.el.ATypeElementWithType;
+import annotations.el.AnnotationDef;
+import annotations.el.BoundLocation;
+import annotations.el.InnerTypeLocation;
+import annotations.el.LocalLocation;
+import annotations.el.RelativeLocation;
+import annotations.el.TypeIndexLocation;
+import annotations.field.AnnotationFieldType;
+import annotations.io.ASTPath;
+import annotations.io.IndexFileParser;
+import annotations.util.coll.VivifyingMap;
+import annotator.find.AnnotationInsertion;
+import annotator.find.CastInsertion;
+import annotator.find.CloseParenthesisInsertion;
+import annotator.find.ConstructorInsertion;
+import annotator.find.Criteria;
+import annotator.find.GenericArrayLocationCriterion;
+import annotator.find.Insertion;
+import annotator.find.IntersectionTypeLocationCriterion;
+import annotator.find.NewInsertion;
+import annotator.find.ReceiverInsertion;
+import annotator.scanner.MethodOffsetClassVisitor;
+
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.sun.source.tree.Tree;
+
+public class IndexFileSpecification implements Specification {
+  private final Multimap<Insertion, Annotation> insertionSources =
+      LinkedHashMultimap.<Insertion, Annotation>create();
+  private final List<Insertion> insertions = new ArrayList<Insertion>();
+  private final AScene scene;
+  private final String indexFileName;
+
+  // If set, do not attempt to read class files with Asm.
+  // Mostly for debugging and workarounds.
+  public static boolean noAsm = false;
+
+  private static boolean debug = false;
+
+  private ConstructorInsertion cons = null;
+
+  public IndexFileSpecification(String indexFileName) {
+    this.indexFileName = indexFileName;
+    scene = new AScene();
+  }
+
+  @Override
+  public List<Insertion> parse() throws FileIOException {
+    try {
+      Map<String, AnnotationDef> annotationDefs =
+          IndexFileParser.parseFile(indexFileName, scene);
+      Set<String> defKeys = annotationDefs.keySet();
+      Set<String> ambiguous = new LinkedHashSet<String>();
+      // If a qualified name's unqualified counterpart maps to null in
+      // defKeys, it means that the unqualified name is ambiguous and
+      // thus should always be qualified.
+      for (String key : defKeys) {
+        int ix = Math.max(key.lastIndexOf("."), key.lastIndexOf("$"));
+        if (ix >= 0) {
+          String name = key.substring(ix+1);
+          // containsKey() would give wrong result here
+          if (annotationDefs.get(name) == null) { ambiguous.add(name); }
+        }
+      }
+      Insertion.setAlwaysQualify(ambiguous);
+    } catch (FileIOException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new RuntimeException("Exception while parsing index file", e);
+    }
+
+    if (debug) {
+      System.out.printf("Scene parsed from %s:%n", indexFileName);
+      System.out.println(scene.unparse());
+    }
+
+    parseScene();
+//    debug("---------------------------------------------------------");
+    return this.insertions;
+  }
+
+  public Map<String, Set<String>> annotationImports() {
+    return scene.imports;
+  }
+
+  public Multimap<Insertion, Annotation> insertionSources() {
+    return insertionSources;
+  }
+
+  private void addInsertionSource(Insertion ins, Annotation anno) {
+    insertionSources.put(ins, anno);
+  }
+
+  private static void debug(String s) {
+    if (debug) {
+      System.out.println(s);
+    }
+  }
+
+  /*
+  private static void debug(String s, Object... args) {
+    if (debug) {
+      System.out.printf(s, args);
+    }
+  }
+  */
+
+  public AScene getScene() { return scene; }
+
+  /** Fill in this.insertions with insertion pairs. */
+  private void parseScene() {
+    debug("parseScene()");
+
+    // Empty criterion to work from.
+    CriterionList clist = new CriterionList();
+
+    VivifyingMap<String, AElement> packages = scene.packages;
+    for (Map.Entry<String, AElement> entry : packages.entrySet()) {
+      parsePackage(clist, entry.getKey(), entry.getValue());
+    }
+
+    VivifyingMap<String, AClass> classes = scene.classes;
+    for (Map.Entry<String, AClass> entry : classes.entrySet()) {
+      String key = entry.getKey();
+      AClass clazz = entry.getValue();
+      if (key.endsWith(".package-info")) {
+        // strip off suffix to get package name
+        parsePackage(clist, key.substring(0, key.length()-13), clazz);
+      } else {
+        parseClass(clist, key, clazz);
+      }
+    }
+  }
+
+  // There is no .class file corresponding to the package-info.java file.
+  private void parsePackage(CriterionList clist, String packageName, AElement element) {
+    // There is no Tree.Kind.PACKAGE, only Tree.Kind.COMPILATION_UNIT.
+    // CompilationUnitTree has getPackageName and getPackageAnnotations
+    CriterionList packageClist = clist.add(Criteria.packageDecl(packageName));
+    parseElement(packageClist, element);
+  }
+
+
+
+  /** Fill in this.insertions with insertion pairs.
+   * @param className is fully qualified
+   */
+  private void parseClass(CriterionList clist, String className, AClass clazz) {
+    cons = null;  // 0 or 1 per class
+    if (! noAsm) {
+      //  load extra info using asm
+      debug("parseClass(" + className + ")");
+      try {
+        ClassReader classReader = new ClassReader(className);
+        MethodOffsetClassVisitor cv = new MethodOffsetClassVisitor(classReader);
+        classReader.accept(cv, false);
+        debug("Done reading " + className + ".class");
+      } catch (IOException e) {
+        // If .class file not found, still proceed, in case
+        // user only wants method signature annotations.
+        // (TODO: It would be better to store which classes could not be
+        // found, then issue a warning only if an attempt is made to use
+        // the (missing) information.  See
+        // https://github.com/typetools/annotation-tools/issues/34 .)
+        System.out.println("Warning: IndexFileSpecification did not find classfile for: " + className);
+        // throw new RuntimeException("IndexFileSpecification.parseClass: " + e);
+      } catch (RuntimeException e) {
+        System.err.println("IndexFileSpecification had a problem reading class: " + className);
+        throw e;
+      } catch (Error e) {
+        System.err.println("IndexFileSpecification had a problem reading class: " + className);
+        throw e;
+      }
+    }
+
+    CriterionList clistSansClass = clist;
+
+    clist = clist.add(Criteria.inClass(className, true));
+    CriterionList classClist = clistSansClass.add(Criteria.is(Tree.Kind.CLASS, className));
+    parseElement(classClist, clazz);
+
+    VivifyingMap<BoundLocation, ATypeElement> bounds = clazz.bounds;
+    for (Entry<BoundLocation, ATypeElement> entry : bounds.entrySet()) {
+      BoundLocation boundLoc = entry.getKey();
+      ATypeElement bound = entry.getValue();
+      CriterionList boundList = clist.add(Criteria.classBound(className, boundLoc));
+      for (Entry<InnerTypeLocation, ATypeElement> innerEntry : bound.innerTypes.entrySet()) {
+        InnerTypeLocation innerLoc = innerEntry.getKey();
+        AElement ae = innerEntry.getValue();
+        CriterionList innerBoundList = boundList.add(Criteria.atLocation(innerLoc));
+        parseElement(innerBoundList, ae);
+      }
+      CriterionList outerClist = boundList.add(Criteria.atLocation());
+      parseElement(outerClist, bound);
+    }
+
+    clist = clist.add(Criteria.inClass(className, /*exactMatch=*/ false));
+
+    VivifyingMap<TypeIndexLocation, ATypeElement> extimpl = clazz.extendsImplements;
+    for (Entry<TypeIndexLocation, ATypeElement> entry : extimpl.entrySet()) {
+      TypeIndexLocation eiLoc = entry.getKey();
+      ATypeElement ei = entry.getValue();
+      CriterionList eiList = clist.add(Criteria.atExtImplsLocation(className, eiLoc));
+
+      for (Entry<InnerTypeLocation, ATypeElement> innerEntry : ei.innerTypes.entrySet()) {
+        InnerTypeLocation innerLoc = innerEntry.getKey();
+        AElement ae = innerEntry.getValue();
+        CriterionList innerBoundList = eiList.add(Criteria.atLocation(innerLoc));
+        parseElement(innerBoundList, ae);
+      }
+      parseElement(eiList, ei);
+    }
+
+    parseASTInsertions(clist, clazz.insertAnnotations, clazz.insertTypecasts);
+
+    for (Map.Entry<String, AField> entry : clazz.fields.entrySet()) {
+//      clist = clist.add(Criteria.notInMethod()); // TODO: necessary? what is in class but not in method?
+      parseField(clist, entry.getKey(), entry.getValue());
+    }
+    for (Map.Entry<String, AMethod> entry : clazz.methods.entrySet()) {
+      parseMethod(clist, className, entry.getKey(), entry.getValue());
+    }
+    for (Map.Entry<Integer, ABlock> entry : clazz.staticInits.entrySet()) {
+      parseStaticInit(clist, className, entry.getKey(), entry.getValue());
+    }
+    for (Map.Entry<Integer, ABlock> entry : clazz.instanceInits.entrySet()) {
+      parseInstanceInit(clist, className, entry.getKey(), entry.getValue());
+    }
+    for (Map.Entry<String, AExpression> entry : clazz.fieldInits.entrySet()) {
+      parseFieldInit(clist, className, entry.getKey(), entry.getValue());
+    }
+
+    debug("parseClass(" + className + "):  done");
+  }
+
+  /** Fill in this.insertions with insertion pairs. */
+  private void parseField(CriterionList clist, String fieldName, AField field) {
+    // parse declaration annotations
+    parseElement(clist.add(Criteria.field(fieldName, true)), field);
+
+    // parse type annotations
+    clist = clist.add(Criteria.field(fieldName, false));
+    parseInnerAndOuterElements(clist, field.type);
+    parseASTInsertions(clist, field.insertAnnotations, field.insertTypecasts);
+  }
+
+  private void parseStaticInit(CriterionList clist, String className, int blockID, ABlock block) {
+    clist = clist.add(Criteria.inStaticInit(blockID));
+    // the method name argument is not used for static initializers, which are only used
+    // in source specifications. Same for instance and field initializers.
+    // the empty () are there to prevent the whole string to be removed in later parsing.
+    parseBlock(clist, className, "static init number " + blockID + "()", block);
+  }
+
+  private void parseInstanceInit(CriterionList clist, String className, int blockID, ABlock block) {
+    clist = clist.add(Criteria.inInstanceInit(blockID));
+    parseBlock(clist, className, "instance init number " + blockID + "()", block);
+  }
+
+  // keep the descriptive strings for field initializers and static inits consistent
+  // with text used in NewCriterion.
+
+  private void parseFieldInit(CriterionList clist, String className, String fieldName, AExpression exp) {
+    clist = clist.add(Criteria.inFieldInit(fieldName));
+    parseExpression(clist, className, "init for field " + fieldName + "()", exp);
+  }
+
+  /**
+   * Fill in this.insertions with insertion pairs.
+   * @param clist the criteria specifying the location of the insertions
+   * @param element holds the annotations to be inserted
+   * @return a list of the {@link AnnotationInsertion}s that are created
+   */
+  private List<Insertion> parseElement(CriterionList clist, AElement element) {
+    return parseElement(clist, element, new ArrayList<Insertion>(), false);
+  }
+
+  /**
+   * Fill in this.insertions with insertion pairs.
+   * @param clist the criteria specifying the location of the insertions
+   * @param element holds the annotations to be inserted
+   * @param isCastInsertion {@code true} if this for a cast insertion, {@code false}
+   *          otherwise.
+   * @return a list of the {@link AnnotationInsertion}s that are created
+   */
+  private List<Insertion> parseElement(CriterionList clist, AElement element,
+      boolean isCastInsertion) {
+    return parseElement(clist, element, new ArrayList<Insertion>(), isCastInsertion);
+  }
+
+  /**
+   * Fill in this.insertions with insertion pairs.
+   * @param clist the criteria specifying the location of the insertions
+   * @param element holds the annotations to be inserted
+   * @param add {@code true} if the create {@link AnnotationInsertion}s should
+   *         be added to {@link #insertions}, {@code false} otherwise.
+   * @return a list of the {@link AnnotationInsertion}s that are created
+   */
+  private List<Insertion> parseElement(CriterionList clist, AElement element,
+      List<Insertion> innerTypeInsertions) {
+    return parseElement(clist, element, innerTypeInsertions, false);
+  }
+
+  /**
+   * Fill in this.insertions with insertion pairs.
+   * @param clist the criteria specifying the location of the insertions
+   * @param element holds the annotations to be inserted
+   * @param innerTypeInsertions the insertions on the inner type of this
+   *         element. This is only used for receiver and "new" insertions.
+   *         See {@link ReceiverInsertion} for more details.
+   * @param isCastInsertion {@code true} if this for a cast insertion, {@code false}
+   *          otherwise.
+   * @return a list of the {@link AnnotationInsertion}s that are created
+   */
+  private List<Insertion> parseElement(CriterionList clist, AElement element,
+      List<Insertion> innerTypeInsertions, boolean isCastInsertion) {
+    // Use at most one receiver and one cast insertion and add all of the
+    // annotations to the one insertion.
+    ReceiverInsertion receiver = null;
+    NewInsertion neu = null;
+    CastInsertion cast = null;
+    CloseParenthesisInsertion closeParen = null;
+    List<Insertion> annotationInsertions = new ArrayList<Insertion>();
+    Set<Pair<String, Annotation>> elementAnnotations = getElementAnnotations(element);
+    if (elementAnnotations.isEmpty()) {
+      Criteria criteria = clist.criteria();
+      if (element instanceof ATypeElementWithType) {
+        // Still insert even if it's a cast insertion with no outer
+        // annotations to just insert a cast, or insert a cast with
+        // annotations on the compound types.
+        Pair<CastInsertion, CloseParenthesisInsertion> pair =
+            createCastInsertion(((ATypeElementWithType) element).getType(),
+                null, innerTypeInsertions, criteria);
+        cast = pair.a;
+        closeParen = pair.b;
+      } else if (!innerTypeInsertions.isEmpty()) {
+        if (isOnReceiver(criteria)) {
+          receiver = new ReceiverInsertion(new DeclaredType(),
+              criteria, innerTypeInsertions);
+        } else if (isOnNew(criteria)) {
+          neu = new NewInsertion(new DeclaredType(),
+              criteria, innerTypeInsertions);
+        }
+      }
+    }
+
+    for (Pair<String, Annotation> p : elementAnnotations) {
+      List<Insertion> elementInsertions = new ArrayList<Insertion>();
+      String annotationString = p.a;
+      Annotation annotation = p.b;
+      Criteria criteria = clist.criteria();
+      Boolean isDeclarationAnnotation = !annotation.def.isTypeAnnotation()
+          || criteria.isOnFieldDeclaration();
+      if (noTypePath(criteria) && isOnReceiver(criteria)) {
+        if (receiver == null) {
+          DeclaredType type = new DeclaredType();
+          type.addAnnotation(annotationString);
+          receiver = new ReceiverInsertion(type, criteria, innerTypeInsertions);
+          elementInsertions.add(receiver);
+        } else {
+          receiver.getType().addAnnotation(annotationString);
+        }
+        addInsertionSource(receiver, annotation);
+      } else if (noTypePath(criteria) && isOnNew(criteria)) {
+        if (neu == null) {
+          DeclaredType type = new DeclaredType();
+          type.addAnnotation(annotationString);
+          neu = new NewInsertion(type, criteria, innerTypeInsertions);
+          elementInsertions.add(neu);
+        } else {
+          neu.getType().addAnnotation(annotationString);
+        }
+        addInsertionSource(neu, annotation);
+      } else if (element instanceof ATypeElementWithType) {
+        if (cast == null) {
+          Pair<CastInsertion, CloseParenthesisInsertion> insertions = createCastInsertion(
+              ((ATypeElementWithType) element).getType(), annotationString,
+              innerTypeInsertions, criteria);
+          cast = insertions.a;
+          closeParen = insertions.b;
+          elementInsertions.add(cast);
+          elementInsertions.add(closeParen);
+          // no addInsertionSource, as closeParen is not explicit in scene
+        } else {
+          cast.getType().addAnnotation(annotationString);
+        }
+        addInsertionSource(cast, annotation);
+      } else {
+        RelativeLocation loc = criteria.getCastRelativeLocation();
+        if (loc != null && loc.type_index > 0) {
+          criteria.add(new IntersectionTypeLocationCriterion(loc));
+        }
+        Insertion ins = new AnnotationInsertion(annotationString, criteria,
+                                      isDeclarationAnnotation);
+        debug("parsed: " + ins);
+        if (!isCastInsertion) {
+            // Annotations on compound types of a cast insertion will be
+            // inserted directly on the cast insertion.
+            // this.insertions.add(ins);
+            elementInsertions.add(ins);
+        }
+        annotationInsertions.add(ins);
+        addInsertionSource(ins, annotation);
+      }
+      this.insertions.addAll(elementInsertions);
+
+      // exclude expression annotations
+      if (noTypePath(criteria) && isOnNullaryConstructor(criteria)) {
+        if (cons == null) {
+          DeclaredType type = new DeclaredType(criteria.getClassName());
+          cons = new ConstructorInsertion(type, criteria,
+              new ArrayList<Insertion>());
+          this.insertions.add(cons);
+        }
+        // no addInsertionSource, as cons is not explicit in scene
+        for (Insertion i : elementInsertions) {
+          if (i.getKind() == Insertion.Kind.RECEIVER) {
+            cons.addReceiverInsertion((ReceiverInsertion) i);
+          } else if (criteria.isOnReturnType()) {
+            ((DeclaredType) cons.getType()).addAnnotation(annotationString);
+          } else if (isDeclarationAnnotation) {
+            cons.addDeclarationInsertion(i);
+            i.setInserted(true);
+          } else {
+            annotationInsertions.add(i);
+          }
+        }
+      }
+      elementInsertions.clear();
+    }
+    if (receiver != null) {
+        this.insertions.add(receiver);
+    }
+    if (neu != null) {
+        this.insertions.add(neu);
+    }
+    if (cast != null) {
+        this.insertions.add(cast);
+        this.insertions.add(closeParen);
+    }
+    return annotationInsertions;
+  }
+
+  private boolean noTypePath(Criteria criteria) {
+    GenericArrayLocationCriterion galc =
+        criteria.getGenericArrayLocation();
+    return galc == null || galc.getLocation().isEmpty();
+  }
+
+  public static boolean isOnReceiver(Criteria criteria) {
+    ASTPath astPath = criteria.getASTPath();
+    if (astPath == null) { return criteria.isOnReceiver(); }
+    if (astPath.isEmpty()) { return false; }
+    ASTPath.ASTEntry entry = astPath.get(-1);
+    return entry.childSelectorIs(ASTPath.PARAMETER)
+        && entry.getArgument() < 0;
+  }
+
+  public static boolean isOnNew(Criteria criteria) {
+    ASTPath astPath = criteria.getASTPath();
+    if (astPath == null || astPath.isEmpty()) { return criteria.isOnNew(); }
+    ASTPath.ASTEntry entry = astPath.get(-1);
+    Tree.Kind kind = entry.getTreeKind();
+    return kind == Tree.Kind.NEW_ARRAY
+            && entry.childSelectorIs(ASTPath.TYPE)
+            && entry.getArgument() == 0
+        || kind == Tree.Kind.NEW_CLASS
+            && entry.childSelectorIs(ASTPath.IDENTIFIER);
+  }
+
+  private static boolean isOnNullaryConstructor(Criteria criteria) {
+    if (criteria.isOnMethod("<init>()V")) {
+      ASTPath astPath = criteria.getASTPath();
+      if (astPath == null || astPath.isEmpty()) {
+        return !criteria.isOnNew();  // exclude expression annotations
+      }
+      ASTPath.ASTEntry entry = astPath.get(0);
+      return entry.getTreeKind() == Tree.Kind.METHOD
+          && (entry.childSelectorIs(ASTPath.TYPE) || isOnReceiver(criteria));
+    }
+    return false;
+  }
+
+  /**
+   * Creates the {@link CastInsertion} and {@link CloseParenthesisInsertion}
+   * for a cast insertion.
+   *
+   * @param type the cast type to insert
+   * @param annotationString the annotation on the outermost type, or
+   *         {@code null} if none. With no outermost annotation this cast
+   *         insertion will either be a cast without any annotations or a cast
+   *         with annotations only on the compound types.
+   * @param innerTypeInsertions the annotations on the inner types
+   * @param criteria the criteria for the location of this insertion
+   * @return the {@link CastInsertion} and {@link CloseParenthesisInsertion}
+   */
+  private Pair<CastInsertion, CloseParenthesisInsertion> createCastInsertion(
+      Type type, String annotationString, List<Insertion> innerTypeInsertions,
+      Criteria criteria) {
+    if (annotationString != null) {
+      type.addAnnotation(annotationString);
+    }
+    Insertion.decorateType(innerTypeInsertions, type, criteria.getASTPath());
+    CastInsertion cast = new CastInsertion(criteria, type);
+    CloseParenthesisInsertion closeParen = new CloseParenthesisInsertion(
+        criteria, cast.getSeparateLine());
+    return new Pair<CastInsertion, CloseParenthesisInsertion>(cast, closeParen);
+  }
+
+  /**
+   * Fill in this.insertions with insertion pairs for the outer and inner types.
+   * @param clist the criteria specifying the location of the insertions
+   * @param typeElement holds the annotations to be inserted
+   */
+  private void parseInnerAndOuterElements(CriterionList clist, ATypeElement typeElement) {
+    parseInnerAndOuterElements(clist, typeElement, false);
+  }
+
+  /**
+   * Fill in this.insertions with insertion pairs for the outer and inner types.
+   * @param clist the criteria specifying the location of the insertions
+   * @param typeElement holds the annotations to be inserted
+   * @param isCastInsertion {@code true} if this for a cast insertion, {@code false}
+   *          otherwise.
+   */
+  private void parseInnerAndOuterElements(CriterionList clist,
+      ATypeElement typeElement, boolean isCastInsertion) {
+    List<Insertion> innerInsertions = new ArrayList<Insertion>();
+    for (Entry<InnerTypeLocation, ATypeElement> innerEntry: typeElement.innerTypes.entrySet()) {
+      InnerTypeLocation innerLoc = innerEntry.getKey();
+      AElement innerElement = innerEntry.getValue();
+      CriterionList innerClist = clist.add(Criteria.atLocation(innerLoc));
+      innerInsertions.addAll(parseElement(innerClist, innerElement, isCastInsertion));
+    }
+    CriterionList outerClist = clist;
+    if (!isCastInsertion) {
+      // Cast insertion is never on an existing type.
+      outerClist = clist.add(Criteria.atLocation());
+    }
+    parseElement(outerClist, typeElement, innerInsertions);
+  }
+
+  // Returns a string representation of the annotations at the element.
+  private Set<Pair<String, Annotation>>
+  getElementAnnotations(AElement element) {
+    Set<Pair<String, Annotation>> result =
+        new LinkedHashSet<Pair<String, Annotation>>(
+            element.tlAnnotationsHere.size());
+    for (Annotation a : element.tlAnnotationsHere) {
+      AnnotationDef adef = a.def;
+      String annotationString = "@" + adef.name;
+
+      if (a.fieldValues.size() == 1 && a.fieldValues.containsKey("value")) {
+        annotationString += "(" + formatFieldValue(a, "value") + ")";
+      } else if (a.fieldValues.size() > 0) {
+        annotationString += "(";
+        boolean first = true;
+        for (String field : a.fieldValues.keySet()) {
+          // parameters of the annotation
+          if (!first) {
+            annotationString += ", ";
+          }
+          annotationString += field + "=" + formatFieldValue(a, field);
+          first = false;
+        }
+        annotationString += ")";
+      }
+      // annotationString += " ";
+      result.add(new Pair<String, Annotation>(annotationString, a));
+    }
+    return result;
+  }
+
+  private String formatFieldValue(Annotation a, String field) {
+    AnnotationFieldType fieldType = a.def.fieldTypes.get(field);
+    assert fieldType != null;
+    return fieldType.format(a.fieldValues.get(field));
+  }
+
+  private void parseMethod(CriterionList clist, String className, String methodName, AMethod method) {
+    // Being "in" a method refers to being somewhere in the
+    // method's tree, which includes return types, parameters, receiver, and
+    // elements inside the method body.
+    clist = clist.add(Criteria.inMethod(methodName));
+
+    // parse declaration annotations
+    parseElement(clist, method);
+
+    // parse receiver
+    CriterionList receiverClist = clist.add(Criteria.receiver(methodName));
+    parseInnerAndOuterElements(receiverClist, method.receiver.type);
+
+    // parse return type
+    CriterionList returnClist = clist.add(Criteria.returnType(className, methodName));
+    parseInnerAndOuterElements(returnClist, method.returnType);
+
+    // parse bounds of method
+    for (Entry<BoundLocation, ATypeElement> entry : method.bounds.entrySet()) {
+      BoundLocation boundLoc = entry.getKey();
+      ATypeElement bound = entry.getValue();
+      CriterionList boundClist = clist.add(Criteria.methodBound(methodName, boundLoc));
+      parseInnerAndOuterElements(boundClist, bound);
+    }
+
+    // parse parameters of method
+    for (Entry<Integer, AField> entry : method.parameters.entrySet()) {
+      Integer index = entry.getKey();
+      AField param = entry.getValue();
+      CriterionList paramClist = clist.add(Criteria.param(methodName, index));
+      // parse declaration annotations
+      // parseField(paramClist, index.toString(), param);
+      parseInnerAndOuterElements(paramClist, param.type);
+    }
+
+    // parse insert annotations/typecasts of method
+    parseASTInsertions(clist, method.insertAnnotations, method.insertTypecasts);
+
+    // parse body of method
+    parseBlock(clist, className, methodName, method.body);
+  }
+
+  private void parseBlock(CriterionList clist, String className, String methodName, ABlock block) {
+    // parse locals of method
+    for (Entry<LocalLocation, AField> entry : block.locals.entrySet()) {
+      LocalLocation loc = entry.getKey();
+      AElement var = entry.getValue();
+      CriterionList varClist = clist.add(Criteria.local(methodName, loc));
+      // parse declaration annotations
+      parseElement(varClist, var);
+      parseInnerAndOuterElements(varClist, var.type);
+    }
+
+    parseExpression(clist, className, methodName, block);
+  }
+
+  private void parseExpression(CriterionList clist, String className, String methodName, AExpression exp) {
+    // parse typecasts of method
+    for (Entry<RelativeLocation, ATypeElement> entry : exp.typecasts.entrySet()) {
+      RelativeLocation loc = entry.getKey();
+      ATypeElement cast = entry.getValue();
+      CriterionList castClist = clist.add(Criteria.cast(methodName, loc));
+      parseInnerAndOuterElements(castClist, cast);
+    }
+
+    // parse news (object creation) of method
+    for (Entry<RelativeLocation, ATypeElement> entry : exp.news.entrySet()) {
+      RelativeLocation loc = entry.getKey();
+      ATypeElement newObject = entry.getValue();
+      CriterionList newClist = clist.add(Criteria.newObject(methodName, loc));
+      parseInnerAndOuterElements(newClist, newObject);
+    }
+
+    // parse instanceofs of method
+    for (Entry<RelativeLocation, ATypeElement> entry : exp.instanceofs.entrySet()) {
+      RelativeLocation loc = entry.getKey();
+      ATypeElement instanceOf = entry.getValue();
+      CriterionList instanceOfClist = clist.add(Criteria.instanceOf(methodName, loc));
+      parseInnerAndOuterElements(instanceOfClist, instanceOf);
+    }
+
+    // parse member references of method
+    for (Entry<RelativeLocation, ATypeElement> entry : exp.refs.entrySet()) {
+      RelativeLocation loc = entry.getKey();
+      ATypeElement ref = entry.getValue();
+      CriterionList instanceOfClist =
+          clist.add(Criteria.memberReference(methodName, loc));
+      parseInnerAndOuterElements(instanceOfClist, ref);
+    }
+
+    // parse method invocations of method
+    for (Entry<RelativeLocation, ATypeElement> entry : exp.calls.entrySet()) {
+      RelativeLocation loc = entry.getKey();
+      ATypeElement call = entry.getValue();
+      CriterionList instanceOfClist =
+          clist.add(Criteria.methodCall(methodName, loc));
+      parseInnerAndOuterElements(instanceOfClist, call);
+    }
+
+    // parse lambda expressions of method
+    for (Entry<RelativeLocation, AMethod> entry : exp.funs.entrySet()) {
+      RelativeLocation loc = entry.getKey();
+      AMethod lambda = entry.getValue();
+      CriterionList lambdaClist = clist.add(Criteria.lambda(methodName, loc));
+      parseLambdaExpression(className, methodName, lambda, lambdaClist);
+    }
+  }
+
+  private void parseLambdaExpression(String className, String methodName,
+      AMethod lambda, CriterionList clist) {
+    for (Entry<Integer, AField> entry : lambda.parameters.entrySet()) {
+      Integer index = entry.getKey();
+      AField param = entry.getValue();
+      CriterionList paramClist = clist.add(Criteria.param("(anonymous)", index));
+      parseInnerAndOuterElements(paramClist, param.type);
+      parseASTInsertions(paramClist, param.insertAnnotations, param.insertTypecasts);
+    }
+    parseBlock(clist, className, methodName, lambda.body);
+  }
+
+  private void parseASTInsertions(CriterionList clist,
+      VivifyingMap<ASTPath, ATypeElement> insertAnnotations,
+      VivifyingMap<ASTPath, ATypeElementWithType> insertTypecasts) {
+    for (Entry<ASTPath, ATypeElement> entry : insertAnnotations.entrySet()) {
+      ASTPath astPath = entry.getKey();
+      ATypeElement insertAnnotation = entry.getValue();
+      CriterionList insertAnnotationClist =
+          clist.add(Criteria.astPath(astPath));
+      parseInnerAndOuterElements(insertAnnotationClist,
+          insertAnnotation, true);
+    }
+    for (Entry<ASTPath, ATypeElementWithType> entry : insertTypecasts.entrySet()) {
+      ASTPath astPath = entry.getKey();
+      ATypeElementWithType insertTypecast = entry.getValue();
+      CriterionList insertTypecastClist = clist.add(Criteria.astPath(astPath));
+      parseInnerAndOuterElements(insertTypecastClist, insertTypecast, true);
+    }
+  }
+}
diff --git a/annotation-file-utilities/src/annotator/specification/Specification.java b/annotation-file-utilities/src/annotator/specification/Specification.java
new file mode 100644
index 0000000..6477e47
--- /dev/null
+++ b/annotation-file-utilities/src/annotator/specification/Specification.java
@@ -0,0 +1,22 @@
+package annotator.specification;
+
+import java.util.List;
+
+import plume.FileIOException;
+import annotator.find.Insertion;
+// import annotations.io.FileIOException;
+
+/**
+ * Represents a file containing a "specification" for placing annotations on
+ * program elements.
+ */
+public interface Specification {
+
+    /**
+     * Parses the specification file.
+     *
+     * @return the insertions that the annotator should make, as determined from
+     *         parsing the specification file
+     */
+    List<Insertion> parse() throws FileIOException;
+}
diff --git a/annotation-file-utilities/tests/-SEE-THE-MAKEFILE- b/annotation-file-utilities/tests/-SEE-THE-MAKEFILE-
new file mode 100644
index 0000000..f11c322
--- /dev/null
+++ b/annotation-file-utilities/tests/-SEE-THE-MAKEFILE-
@@ -0,0 +1 @@
+See the Makefile.
diff --git a/annotation-file-utilities/tests/ASTInsert.goal b/annotation-file-utilities/tests/ASTInsert.goal
new file mode 100644
index 0000000..73327d4
--- /dev/null
+++ b/annotation-file-utilities/tests/ASTInsert.goal
@@ -0,0 +1,99 @@
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+
+@interface Bla {}
+
+public class ASTInsert {
+
+    PrintStream out;
+    private int c = ((@checkers.nullness.quals.Nullable int) (((@checkers.nullness.quals.Nullable int) (12)) + ((@checkers.nullness.quals.Nullable int) (13))));
+    private String str = "this" + ((@checkers.nullness.quals.Nullable String) ("is")).concat(((@checkers.nullness.quals.Nullable String) ("string")));
+    private @Bla String @Bla [] sa = new @Bla String @Bla [] {};
+
+    void m() {
+        int i;
+    }
+
+    int m(String y, String[] z, int i) {
+        String x = ((@checkers.nullness.quals.Nullable String) (new String()));
+        String s;
+        s = ((@checkers.nullness.quals.Nullable String) (((@checkers.nullness.quals.Nullable String) (x)) + ((@checkers.nullness.quals.Nullable String) (x))));
+        s = y;
+        s = z[((@checkers.nullness.quals.Nullable int) (0))];
+        s = x;
+        int j = 0;
+        switch (((@checkers.nullness.quals.Nullable int) (i + 2))) {
+            case 1:
+                j = i + ((@checkers.nullness.quals.Nullable int) (1));
+                System.out.println(1);
+                break;
+            case 2:
+                j = i + 2;
+                System.out.println(((@checkers.nullness.quals.Nullable int) (2)));
+                break;
+            default:
+                j = i + 3;
+                System.out.println(-1);
+        }
+        j *= ((@checkers.nullness.quals.Nullable int) (i));
+        j = s != x ? ((@checkers.nullness.quals.Nullable int) (j)) : i;
+        do {
+            int h = ((@checkers.nullness.quals.Nullable int) (i)) & j;
+        } while (i < j);
+        for (int i2 : new int[5]) {
+            j = ((@checkers.nullness.quals.Nullable int) (i2));
+        }
+        for (int a = 0, b = ((@checkers.nullness.quals.Nullable int) (0)); a < ((@checkers.nullness.quals.Nullable int) (j)); a = ((@checkers.nullness.quals.Nullable int) (a + 1)), b++)
+            a = ((@checkers.nullness.quals.Nullable int) (b));
+        if (((@checkers.nullness.quals.Nullable int) (i)) < j)
+            i = j;
+        else
+            j = ((@checkers.nullness.quals.Nullable int) (i));
+        boolean b = ((@checkers.nullness.quals.Nullable String) (x)) instanceof String;
+        label: b = ((@checkers.nullness.quals.Nullable boolean) (false));
+        Object o = ((@checkers.nullness.quals.Nullable ASTInsert) (this)).out;
+        m(y, z, ((@checkers.nullness.quals.Nullable int) (i)));
+        int[][] test = new int[4][((@checkers.nullness.quals.Nullable int) (5))];
+        int[][] test2 = {{1, 2}, {1, 2, ((@checkers.nullness.quals.Nullable int) (3))}};
+        new String(((@checkers.nullness.quals.Nullable String) ("test")));
+        if (i < 1)
+            return ((@checkers.nullness.quals.Nullable int) (18));
+        synchronized (o) {
+            i = ((@checkers.nullness.quals.Nullable int) (i + i));
+        }
+        if (j < 1)
+            throw ((@checkers.nullness.quals.Nullable IllegalStateException) (new IllegalStateException()));
+        try {
+            int t = ((@checkers.nullness.quals.Nullable int) (1));
+        } catch (Error e) {
+            i = j;
+        } catch (RuntimeException e) {
+            j = ((@checkers.nullness.quals.Nullable int) (i));
+        } finally {
+            j = i + ((@checkers.nullness.quals.Nullable int) (j));
+        }
+        j = (int) (i + ((@checkers.nullness.quals.Nullable int) (j)));
+        j = - ((@checkers.nullness.quals.Nullable int) (j));
+        while (i < ((@checkers.nullness.quals.Nullable int) (j)))
+            i = ((@checkers.nullness.quals.Nullable int) (i)) + 1;
+        ((@checkers.nullness.quals.Nullable ASTInsert) (this)).out.println();
+        ((@checkers.nullness.quals.Nullable PrintStream) (System.out)).println();
+        Object obj = ((@checkers.nullness.quals.Nullable @A @B @C Object) (null));
+        return 0;
+    }
+
+    public <T> void invoc(T t1, T t2) {}
+    public void context() {
+        this.<@checkers.nullness.quals.NonNull String>invoc("a", null);
+    }
+}
+
+class Wild<X extends List<? extends @Bla Object>> {
+  Wild(@Bla Wild<X> n, X p) {
+  }
+}
+
+class Unbound<X extends @A Object> {}
+
+class Bound<@A X extends @B Object & @C Comparable<@D int @E []> & @F Map<@G ? extends @H Object, @I ?>, @J Y> {}
diff --git a/annotation-file-utilities/tests/ASTInsert.jaif b/annotation-file-utilities/tests/ASTInsert.jaif
new file mode 100644
index 0000000..fa88b47
--- /dev/null
+++ b/annotation-file-utilities/tests/ASTInsert.jaif
@@ -0,0 +1,101 @@
+package:
+annotation @Bla:
+annotation @A:
+annotation @B:
+annotation @C:
+annotation @D:
+annotation @E:
+annotation @F:
+annotation @G:
+annotation @H:
+annotation @I:
+annotation @J:
+
+package checkers.nullness.quals:
+annotation @Nullable: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER})
+annotation @NonNull: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER})
+
+package :
+class ASTInsert:
+
+    field c:
+        insert-annotation Variable.initializer, Binary.rightOperand: @Nullable
+        insert-annotation Variable.initializer, Binary.leftOperand: @Nullable
+        insert-annotation Variable.initializer: @Nullable
+
+    field str:
+        insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable
+        insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @Nullable
+
+    field sa:
+        insert-annotation Variable.initializer, NewArray.type 0: @Bla
+        insert-annotation Variable.type, ArrayType.type: @Bla
+        insert-annotation Variable.type: @Bla
+        insert-annotation Variable.initializer, NewArray.type 1: @Bla
+
+    method m(Ljava/lang/String;[Ljava/lang/String;I)I:
+        insert-annotation Block.statement 0, Variable.initializer: @Nullable
+        insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable
+        insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable
+        insert-annotation Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @Nullable
+        insert-annotation Block.statement 7, Switch.expression, Parenthesized.expression: @Nullable
+        insert-annotation Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable
+        insert-annotation Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @Nullable
+        insert-annotation Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @Nullable
+        insert-annotation Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @Nullable
+        insert-annotation Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @Nullable
+        insert-annotation Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 12, ForLoop.initializer 1, Variable.initializer: @Nullable
+        insert-annotation Block.statement 12, ForLoop.condition, Binary.rightOperand: @Nullable
+        insert-annotation Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @Nullable
+        insert-annotation Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 14, Variable.initializer, InstanceOf.expression: @Nullable
+        insert-annotation Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 16, Variable.initializer, MemberSelect.expression: @Nullable
+        insert-annotation Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @Nullable
+        insert-annotation Block.statement 18, Variable.initializer, NewArray.dimension 1: @Nullable
+        insert-annotation Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @Nullable
+        insert-annotation Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @Nullable
+        insert-annotation Block.statement 21, If.thenStatement, Return.expression: @Nullable
+        insert-annotation Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 23, If.thenStatement, Throw.expression: @Nullable
+        insert-annotation Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @Nullable
+        insert-annotation Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable
+        insert-annotation Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable
+        insert-annotation Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @Nullable
+        insert-annotation Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @Nullable
+        insert-annotation Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @Nullable
+        insert-annotation Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable
+        insert-annotation Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @Nullable
+        insert-annotation Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable
+        insert-annotation Block.statement 30, Variable.initializer: @Nullable @A @B @C
+
+    method context()V:
+        insert-annotation Block.statement 0, ExpressionStatement.expression, MethodInvocation.typeArgument 0: @NonNull
+
+class Wild:
+bound 0 & 0:
+inner-type 3, 0, 2, 0: @Bla
+
+class Wild:
+method <init>(LWild;Ljava/util/List;)V:
+parameter 0:
+type: @Bla
+
+class Unbound:
+bound 0 & 0: @A
+
+class Bound:
+	typeparam 0: @A
+	typeparam 1: @J
+	bound 0 & 0: @B
+	bound 0 & 1: @C
+		inner-type 3, 0: @E
+		inner-type 3, 0, 0, 0: @D
+	bound 0 & 2: @F
+		inner-type 3, 0: @G
+		inner-type 3, 0, 2, 0: @H
+		inner-type 3, 1: @I
diff --git a/annotation-file-utilities/tests/ASTInsert.java b/annotation-file-utilities/tests/ASTInsert.java
new file mode 100644
index 0000000..b98916f
--- /dev/null
+++ b/annotation-file-utilities/tests/ASTInsert.java
@@ -0,0 +1,99 @@
+import java.io.PrintStream;
+import java.util.List;
+import java.util.Map;
+
+@interface Bla {}
+
+public class ASTInsert {
+
+    PrintStream out;
+    private int c = 12 + 13;
+    private String str = "this" + "is".concat("string");
+    private String[] sa = {};
+
+    void m() {
+        int i;
+    }
+
+    int m(String y, String[] z, int i) {
+        String x = new String();
+        String s;
+        s = x + x;
+        s = y;
+        s = z[0];
+        s = x;
+        int j = 0;
+        switch (i + 2) {
+            case 1:
+                j = i + 1;
+                System.out.println(1);
+                break;
+            case 2:
+                j = i + 2;
+                System.out.println(2);
+                break;
+            default:
+                j = i + 3;
+                System.out.println(-1);
+        }
+        j *= i;
+        j = s != x ? j : i;
+        do {
+            int h = i & j;
+        } while (i < j);
+        for (int i2 : new int[5]) {
+            j = i2;
+        }
+        for (int a = 0, b = 0; a < j; a = a + 1, b++)
+            a = b;
+        if (i < j)
+            i = j;
+        else
+            j = i;
+        boolean b = x instanceof String;
+        label: b = false;
+        Object o = this.out;
+        m(y, z, i);
+        int[][] test = new int[4][5];
+        int[][] test2 = {{1, 2}, {1, 2, 3}};
+        new String("test");
+        if (i < 1)
+            return 18;
+        synchronized (o) {
+            i = i + i;
+        }
+        if (j < 1)
+            throw new IllegalStateException();
+        try {
+            int t = 1;
+        } catch (Error e) {
+            i = j;
+        } catch (RuntimeException e) {
+            j = i;
+        } finally {
+            j = i + j;
+        }
+        j = (int) (i + j);
+        j = -j;
+        while (i < j)
+            i = i + 1;
+        this.out.println();
+        System.out.println();
+        Object obj = null;
+        return 0;
+    }
+
+    public <T> void invoc(T t1, T t2) {}
+    public void context() {
+        this.<String>invoc("a", null);
+    }
+}
+
+class Wild<X extends List<?>> {
+  Wild(Wild<X> n, X p) {
+  }
+}
+
+class Unbound<X> {}
+
+class Bound<X extends Object & Comparable<int[]> & Map<? extends Object, ?>, Y> {}
diff --git a/annotation-file-utilities/tests/Abbreviation.goal b/annotation-file-utilities/tests/Abbreviation.goal
new file mode 100644
index 0000000..ee67d1b
--- /dev/null
+++ b/annotation-file-utilities/tests/Abbreviation.goal
@@ -0,0 +1,8 @@
+// Try to add annotations from different packages that have the same
+// name.  Results should be the same whether _abbreviate_ is set to true
+// or to false.
+class Abbreviation {
+  public Abbreviation(@org.checkerframework.checker.tainting.qual.Tainted Object o) {
+  }
+}
+
diff --git a/annotation-file-utilities/tests/Abbreviation.jaif b/annotation-file-utilities/tests/Abbreviation.jaif
new file mode 100644
index 0000000..8c94f85
--- /dev/null
+++ b/annotation-file-utilities/tests/Abbreviation.jaif
@@ -0,0 +1,9 @@
+package org.checkerframework.checker.tainting.qual:
+annotation @Tainted:
+
+package :
+class Abbreviation:
+    method <init>(Ljava/lang/Object;)V:
+        parameter 0:
+            type: @org.checkerframework.checker.tainting.qual.Tainted
+
diff --git a/annotation-file-utilities/tests/Abbreviation.java b/annotation-file-utilities/tests/Abbreviation.java
new file mode 100644
index 0000000..6f4a893
--- /dev/null
+++ b/annotation-file-utilities/tests/Abbreviation.java
@@ -0,0 +1,8 @@
+// Try to add annotations from different packages that have the same
+// name.  Results should be the same whether _abbreviate_ is set to true
+// or to false.
+class Abbreviation {
+  public Abbreviation(Object o) {
+  }
+}
+
diff --git a/annotation-file-utilities/tests/AnonInner.goal b/annotation-file-utilities/tests/AnonInner.goal
new file mode 100644
index 0000000..81ea573
--- /dev/null
+++ b/annotation-file-utilities/tests/AnonInner.goal
@@ -0,0 +1,19 @@
+public class AnonInner {
+  class InnerOne {
+    Object m1(String p) {
+      return new @A Object() {
+        public @B Object e1(Object o) {
+          return new @C Object();
+        }
+      };
+    }
+    Object m2(String p) {
+      return new @D Object() {
+        public @E Object e2(Object o) {
+          return new @F Object();
+        }
+      };
+    }
+
+  }
+}
diff --git a/annotation-file-utilities/tests/AnonInner.jaif b/annotation-file-utilities/tests/AnonInner.jaif
new file mode 100644
index 0000000..6fa99c4
--- /dev/null
+++ b/annotation-file-utilities/tests/AnonInner.jaif
@@ -0,0 +1,43 @@
+package:
+annotation @A:
+package:
+annotation @B:
+package:
+annotation @C:
+package:
+annotation @D:
+package:
+annotation @E:
+package:
+annotation @F:
+
+
+package:
+class AnonInner$InnerOne:
+method m1(Ljava/lang/String;)LObject;:
+new *0: @A
+
+package:
+class AnonInner$InnerOne$1:
+method e1(Ljava/lang/Object;)LObject;:
+return: @B
+
+package:
+class AnonInner$InnerOne$1:
+method e1(Ljava/lang/Object;)LObject;:
+new *0: @C
+
+package:
+class AnonInner$InnerOne:
+method m2(Ljava/lang/String;)LObject;:
+new *0: @D
+
+package:
+class AnonInner$InnerOne$2:
+method e2(Ljava/lang/Object;)LObject;:
+return: @E
+
+package:
+class AnonInner$InnerOne$2:
+method e2(Ljava/lang/Object;)LObject;:
+new *0: @F
diff --git a/annotation-file-utilities/tests/AnonInner.java b/annotation-file-utilities/tests/AnonInner.java
new file mode 100644
index 0000000..599dd59
--- /dev/null
+++ b/annotation-file-utilities/tests/AnonInner.java
@@ -0,0 +1,19 @@
+public class AnonInner {
+  class InnerOne {
+    Object m1(String p) {
+      return new Object() {
+        public Object e1(Object o) {
+          return new Object();
+        }
+      };
+    }
+    Object m2(String p) {
+      return new Object() {
+        public Object e2(Object o) {
+          return new Object();
+        }
+      };
+    }
+
+  }
+}
diff --git a/annotation-file-utilities/tests/ArrayLiteral.goal b/annotation-file-utilities/tests/ArrayLiteral.goal
new file mode 100644
index 0000000..eb2209d
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayLiteral.goal
@@ -0,0 +1,11 @@
+class ArrayLiteral {
+    @checkers.inference.quals.VarAnnot(2)
+    int @checkers.inference.quals.VarAnnot(3) [] s0= ((new int @checkers.inference.quals.VarAnnot(1) [] {}));
+    @checkers.inference.quals.VarAnnot(5)
+    int @checkers.inference.quals.VarAnnot(6) [] s1 = ((new int @checkers.inference.quals.VarAnnot(4) [] {0, 1}));
+    @checkers.inference.quals.VarAnnot(8)
+    String @checkers.inference.quals.VarAnnot(9) [] s2 = ((new String @checkers.inference.quals.VarAnnot(7) [] {"TEST"}));
+    @checkers.inference.quals.VarAnnot(11)
+    String @checkers.inference.quals.VarAnnot(12) [] s3 = ((new String @checkers.inference.quals.VarAnnot(10) [] { "TEST" }));
+}
+
diff --git a/annotation-file-utilities/tests/ArrayLiteral.jaif b/annotation-file-utilities/tests/ArrayLiteral.jaif
new file mode 100644
index 0000000..f46a8db
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayLiteral.jaif
@@ -0,0 +1,27 @@
+package checkers.inference.quals:
+  annotation @VarAnnot:
+    int value
+
+package :
+class ArrayLiteral:
+
+field s0:
+insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(1)
+insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(2)
+insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(3)
+
+field s1:
+insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(4)
+insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(5)
+insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(6)
+
+field s2:
+insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(7)
+insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(8)
+insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(9)
+
+field s3:
+insert-annotation Variable.initializer: @checkers.inference.quals.VarAnnot(10)
+insert-annotation Variable.type, ArrayType.type: @checkers.inference.quals.VarAnnot(11)
+insert-annotation Variable.type: @checkers.inference.quals.VarAnnot(12)
+
diff --git a/annotation-file-utilities/tests/ArrayLiteral.java b/annotation-file-utilities/tests/ArrayLiteral.java
new file mode 100644
index 0000000..33148af
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayLiteral.java
@@ -0,0 +1,7 @@
+class ArrayLiteral {
+    int[] s0= {};
+    int[] s1 = {0, 1};
+    String[] s2 = {"TEST"};
+    String [] s3 = { "TEST" };
+}
+
diff --git a/annotation-file-utilities/tests/ArrayMultiDim.goal b/annotation-file-utilities/tests/ArrayMultiDim.goal
new file mode 100644
index 0000000..85e25a0
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayMultiDim.goal
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+import java.util.*;
+
+public class ArrayMultiDim {
+  @java.lang.C Object @java.lang.D [] @java.lang.E [] @java.lang.F [] field;
+  Object field2 = new @java.lang.C String @java.lang.D [1] @java.lang.E [2] @java.lang.F [3];
+  @java.lang.C Object @java.lang.D [] @java.lang.E [] @java.lang.F [] field3 = new @java.lang.C Object @java.lang.D [1] @java.lang.E [2] @java.lang.F [3];
+}
diff --git a/annotation-file-utilities/tests/ArrayMultiDim.jaif b/annotation-file-utilities/tests/ArrayMultiDim.jaif
new file mode 100644
index 0000000..3563ed9
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayMultiDim.jaif
@@ -0,0 +1,34 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @H: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ArrayMultiDim:
+
+    field field:
+        type: @D
+            inner-type 0, 0: @E
+            inner-type 0, 0, 0, 0: @F
+            inner-type 0, 0, 0, 0, 0, 0: @C
+
+    field field2:
+        new *0: @D
+            inner-type 0, 0: @E
+            inner-type 0, 0, 0, 0: @F
+            inner-type 0, 0, 0, 0, 0, 0: @C
+
+    field field3:
+        type: @D
+            inner-type 0, 0: @E
+            inner-type 0, 0, 0, 0: @F
+            inner-type 0, 0, 0, 0, 0, 0: @C
+        new *0: @D
+            inner-type 0, 0: @E
+            inner-type 0, 0, 0, 0: @F
+            inner-type 0, 0, 0, 0, 0, 0: @C
diff --git a/annotation-file-utilities/tests/ArrayMultiDim.java b/annotation-file-utilities/tests/ArrayMultiDim.java
new file mode 100644
index 0000000..5a2b6b5
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayMultiDim.java
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+import java.util.*;
+
+public class ArrayMultiDim {
+  Object[][][] field;
+  Object field2 = new String[1][2][3];
+  Object[][][] field3 = new Object[1][2][3];
+}
diff --git a/annotation-file-utilities/tests/ArrayParamSimple.goal b/annotation-file-utilities/tests/ArrayParamSimple.goal
new file mode 100644
index 0000000..66ad0be
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayParamSimple.goal
@@ -0,0 +1,21 @@
+package annotator.tests;
+
+public class ArrayParamSimple {
+
+  public void m1(@java.lang.Tainted Integer[] arg) { }
+
+  public void m2(Integer @java.lang.Tainted [] arg) { }
+
+  public void m3(@java.lang.Nullable Integer[] arg) { }
+
+  public void m4(Integer @java.lang.Nullable [] arg) { }
+
+  public void m5(Integer[] arg) { }
+
+  public void m6(Integer[] arg) { }
+
+  public void m7(Integer[] arg) { }
+
+  public void m8(Integer[] arg) { }
+
+}
diff --git a/annotation-file-utilities/tests/ArrayParamSimple.jaif b/annotation-file-utilities/tests/ArrayParamSimple.jaif
new file mode 100644
index 0000000..1f5bd02
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayParamSimple.jaif
@@ -0,0 +1,36 @@
+package java.lang:
+
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+// Note no @Target meta-annotation
+annotation @Nullable:
+
+package annotator.tests:
+class ArrayParamSimple:
+
+    method <init>()V:
+
+    method m1([Ljava/lang/Integer;)V:
+        parameter #0:
+	    type:
+                inner-type 0, 0: @java.lang.Tainted
+
+    method m2([Ljava/lang/Integer;)V:
+        parameter #0:
+	    type: @java.lang.Tainted
+                inner-type 0, 0:
+
+    method m3([Ljava/lang/Integer;)V:
+        parameter #0:
+	    type:
+                inner-type 0, 0: @java.lang.Nullable
+
+    method m4([Ljava/lang/Integer;)V:
+        parameter #0:
+	    type: @java.lang.Nullable
+                inner-type 0, 0:
+
+// The tool properly issues a warning about inner-type on a declaration location.
+//     method m5([Ljava/lang/Integer;)V:
+//         parameter #0:
+//             inner-type 0, 0: @java.lang.Nullable
diff --git a/annotation-file-utilities/tests/ArrayParamSimple.java b/annotation-file-utilities/tests/ArrayParamSimple.java
new file mode 100644
index 0000000..adf7470
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayParamSimple.java
@@ -0,0 +1,21 @@
+package annotator.tests;
+
+public class ArrayParamSimple {
+
+  public void m1(Integer[] arg) { }
+
+  public void m2(Integer[] arg) { }
+
+  public void m3(Integer[] arg) { }
+
+  public void m4(Integer[] arg) { }
+
+  public void m5(Integer[] arg) { }
+
+  public void m6(Integer[] arg) { }
+
+  public void m7(Integer[] arg) { }
+
+  public void m8(Integer[] arg) { }
+
+}
diff --git a/annotation-file-utilities/tests/ArrayReturnTypeSimple.goal b/annotation-file-utilities/tests/ArrayReturnTypeSimple.goal
new file mode 100644
index 0000000..1b5263d
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayReturnTypeSimple.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class ArrayReturnTypeSimple {
+  private @java.lang.UnderInitialization Object[] foo() {
+    return null;
+  }
+}
diff --git a/annotation-file-utilities/tests/ArrayReturnTypeSimple.jaif b/annotation-file-utilities/tests/ArrayReturnTypeSimple.jaif
new file mode 100644
index 0000000..2157d68
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayReturnTypeSimple.jaif
@@ -0,0 +1,12 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ArrayReturnTypeSimple:
+
+    method <init>()V:
+
+    method foo()[Ljava/lang/Object;:
+        return:
+            inner-type 0, 0: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/ArrayReturnTypeSimple.java b/annotation-file-utilities/tests/ArrayReturnTypeSimple.java
new file mode 100644
index 0000000..370a222
--- /dev/null
+++ b/annotation-file-utilities/tests/ArrayReturnTypeSimple.java
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class ArrayReturnTypeSimple {
+  private Object[] foo() {
+    return null;
+  }
+}
diff --git a/annotation-file-utilities/tests/BoundClassMultiple.goal b/annotation-file-utilities/tests/BoundClassMultiple.goal
new file mode 100644
index 0000000..2e99ceb
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundClassMultiple.goal
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+public class BoundClassMultiple<
+  T extends @java.lang.A Date,
+  U extends @java.lang.B Object & @java.lang.C List & @java.lang.D Serializable,
+  V extends @java.lang.E Object & Comparable<@java.lang.F V>> {
+}
diff --git a/annotation-file-utilities/tests/BoundClassMultiple.jaif b/annotation-file-utilities/tests/BoundClassMultiple.jaif
new file mode 100644
index 0000000..9b3bce9
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundClassMultiple.jaif
@@ -0,0 +1,29 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundClassMultiple:
+    bound 0 & 0: @java.lang.A
+    bound 1 & 0: @java.lang.B
+    bound 1 & 1: @java.lang.C
+    bound 1 & 2: @java.lang.D
+    bound 2 & 0: @java.lang.E
+    inner-type 3, 0: @java.lang.F
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/BoundClassMultiple.java b/annotation-file-utilities/tests/BoundClassMultiple.java
new file mode 100644
index 0000000..a7020ab
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundClassMultiple.java
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.List;
+
+public class BoundClassMultiple<
+  T extends Date,
+  U extends List & Serializable,
+  V extends Comparable<V>> {
+}
diff --git a/annotation-file-utilities/tests/BoundClassSimple.goal b/annotation-file-utilities/tests/BoundClassSimple.goal
new file mode 100644
index 0000000..6b2b267
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundClassSimple.goal
@@ -0,0 +1,8 @@
+package annotator.tests;
+
+import java.util.Date;
+
+public class BoundClassSimple<T extends @java.lang.Tainted Date> {
+  T field = null;
+  Date misleadingField = new Date();
+}
diff --git a/annotation-file-utilities/tests/BoundClassSimple.jaif b/annotation-file-utilities/tests/BoundClassSimple.jaif
new file mode 100644
index 0000000..05edaf6
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundClassSimple.jaif
@@ -0,0 +1,12 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundClassSimple:
+    bound 0 & 0: @java.lang.Tainted
+
+    field field:
+    field misleadingField:
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/BoundClassSimple.java b/annotation-file-utilities/tests/BoundClassSimple.java
new file mode 100644
index 0000000..b2af877
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundClassSimple.java
@@ -0,0 +1,8 @@
+package annotator.tests;
+
+import java.util.Date;
+
+public class BoundClassSimple<T extends Date> {
+  T field = null;
+  Date misleadingField = new Date();
+}
diff --git a/annotation-file-utilities/tests/BoundMethodMultiple.goal b/annotation-file-utilities/tests/BoundMethodMultiple.goal
new file mode 100644
index 0000000..32e2e56
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundMethodMultiple.goal
@@ -0,0 +1,24 @@
+package annotator.tests;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.Date;
+
+public class BoundMethodMultiple {
+  public <T> void foo(@java.lang.A Object o) {
+
+  }
+
+  public <T extends @java.lang.B Date> void foo(T o) {
+
+  }
+
+  public <T extends @java.lang.C Object & @java.lang.D List & @java.lang.E Serializable> void foo(T t) {
+
+  }
+
+  public <T extends @java.lang.F Date, U extends @java.lang.G Map> void foo(T t, U u) {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/BoundMethodMultiple.jaif b/annotation-file-utilities/tests/BoundMethodMultiple.jaif
new file mode 100644
index 0000000..8dba019
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundMethodMultiple.jaif
@@ -0,0 +1,42 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundMethodMultiple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        parameter #0:
+          type: @java.lang.A
+
+    method foo(Lannotator/tests/Date;)V:
+        bound 0 & 0: @java.lang.B
+
+    method foo(Ljava/util/List;)V:
+        bound 0 & 0: @java.lang.C
+        bound 0 & 1: @java.lang.D
+        bound 0 & 2: @java.lang.E
+
+    method foo(Lannotator/tests/Date;Ljava/util/Map;)V:
+        bound 0 & 0: @java.lang.F
+        bound 1 & 1: @java.lang.G
+
diff --git a/annotation-file-utilities/tests/BoundMethodMultiple.java b/annotation-file-utilities/tests/BoundMethodMultiple.java
new file mode 100644
index 0000000..8419d9d
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundMethodMultiple.java
@@ -0,0 +1,24 @@
+package annotator.tests;
+
+import java.io.Serializable;
+import java.util.List;
+import java.util.Map;
+import java.util.Date;
+
+public class BoundMethodMultiple {
+  public <T> void foo(Object o) {
+
+  }
+
+  public <T extends Date> void foo(T o) {
+
+  }
+
+  public <T extends List & Serializable> void foo(T t) {
+
+  }
+
+  public <T extends Date, U extends Map> void foo(T t, U u) {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/BoundMethodSimple.goal b/annotation-file-utilities/tests/BoundMethodSimple.goal
new file mode 100644
index 0000000..a5db040
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundMethodSimple.goal
@@ -0,0 +1,20 @@
+package annotator.tests;
+
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+public class BoundMethodSimple {
+  public <T extends @java.lang.Tainted Date> void foo(T t) {
+    System.out.println(t);
+  }
+
+  public <T extends @java.lang.Tainted Object> void foo2(T t) {
+    System.out.println(t);
+  }
+
+  public static <T extends @org.checkerframework.checker.nullness.qual.Nullable @org.checkerframework.checker.nullness.qual.UnknownKeyFor Object> void
+  foo3(List<T> list, Comparator<? super T> c) {
+  }
+}
+
diff --git a/annotation-file-utilities/tests/BoundMethodSimple.jaif b/annotation-file-utilities/tests/BoundMethodSimple.jaif
new file mode 100644
index 0000000..abefd4b
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundMethodSimple.jaif
@@ -0,0 +1,21 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package org.checkerframework.checker.nullness.qual:
+annotation @Nullable: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @UnknownKeyFor: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundMethodSimple:
+
+    method <init>()V:
+
+    method foo(Lannotator/tests/Date;)V:
+        bound 0 & 0: @java.lang.Tainted
+
+    method foo2(Ljava/lang/Object;)V:
+        bound 0 & 0: @java.lang.Tainted
+
+    method foo3(Ljava/util/List;Ljava/util/Comparator;)V:
+        bound 0 & 0: @org.checkerframework.checker.nullness.qual.Nullable @org.checkerframework.checker.nullness.qual.UnknownKeyFor
+
diff --git a/annotation-file-utilities/tests/BoundMethodSimple.java b/annotation-file-utilities/tests/BoundMethodSimple.java
new file mode 100644
index 0000000..b681ad4
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundMethodSimple.java
@@ -0,0 +1,20 @@
+package annotator.tests;
+
+import java.util.Comparator;
+import java.util.Date;
+import java.util.List;
+
+public class BoundMethodSimple {
+  public <T extends Date> void foo(T t) {
+    System.out.println(t);
+  }
+
+  public <T> void foo2(T t) {
+    System.out.println(t);
+  }
+
+  public static <T> void
+  foo3(List<T> list, Comparator<? super T> c) {
+  }
+}
+
diff --git a/annotation-file-utilities/tests/BoundZero.goal b/annotation-file-utilities/tests/BoundZero.goal
new file mode 100644
index 0000000..5b3fe66
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundZero.goal
@@ -0,0 +1,28 @@
+package annotator.tests;
+
+import static java.lang.annotation.ElementType.TYPE_USE;
+import java.lang.annotation.Target;
+
+//@skip-test
+@Target({TYPE_USE}) @interface A {}
+@Target({TYPE_USE}) @interface B {}
+@Target({TYPE_USE}) @interface C {}
+@Target({TYPE_USE}) @interface X {}
+@Target({TYPE_USE}) @interface Y {}
+
+class BoundZero<U extends @annotator.tests.A @annotator.tests.B @annotator.tests.C Object> {
+  <T extends @annotator.tests.X @annotator.tests.Y Object> void m1(T o) {}
+  <T extends @Y @annotator.tests.X Object & Comparable<T>> void m2(T o) {}
+  <T extends @annotator.tests.Y @annotator.tests.X Object & Comparable<T>> void m3(T o) {}
+  <T extends java.lang.@annotator.tests.X @annotator.tests.Y Object & Comparable<T>> void m4(T o) {}
+  <T extends java.lang.@Y @annotator.tests.X Object & Comparable<T>> void m5(T o) {}
+  <T extends java.lang.@annotator.tests.Y @annotator.tests.X Object & Comparable<T>> void m6(T o) {}
+  <T extends @annotator.tests.X Object & @annotator.tests.Y Comparable<T>> void m7(T o) {}
+  <T extends @annotator.tests.X Object & @Y Comparable<T>> void m8(T o) {}
+  <T extends @annotator.tests.X Object & @annotator.tests.Y Comparable<T>> void m9(T o) {}
+  class Inner<@X V extends @Y @annotator.tests.A @annotator.tests.B @annotator.tests.C Object> {}
+}
+
+class Outer<S extends @annotator.tests.A @annotator.tests.B @annotator.tests.C Object & java.io.Serializable> {
+}
+
diff --git a/annotation-file-utilities/tests/BoundZero.jaif b/annotation-file-utilities/tests/BoundZero.jaif
new file mode 100644
index 0000000..d97761f
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundZero.jaif
@@ -0,0 +1,44 @@
+package annotator.tests:
+annotation @A: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+annotation @B: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+annotation @C: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+annotation @X: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+annotation @Y: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+
+class BoundZero:
+    bound 0&0: @A @B @C
+
+    method m1(Ljava/lang/Object;)V:
+        bound 0&0: @X @Y
+
+    method m2(Ljava/lang/Object;)V:
+        bound 0&0: @X
+
+    method m3(Ljava/lang/Object;)V:
+        bound 0&0: @X
+
+    method m4(Ljava/lang/Object;)V:
+        bound 0&0: @X @Y
+
+    method m5(Ljava/lang/Object;)V:
+        bound 0&0: @X
+
+    method m6(Ljava/lang/Object;)V:
+        bound 0&0: @X
+
+    method m7(Ljava/lang/Object;)V:
+        bound 0&0: @X
+        bound 0&1: @Y
+
+    method m8(Ljava/lang/Object;)V:
+        bound 0&0: @X
+
+    method m9(Ljava/lang/Object;)V:
+        bound 0&0: @X
+
+class BoundZero$Inner:
+    bound 0&0: @A @B @C
+
+class Outer:
+    bound 0&0: @A @B @C
+
diff --git a/annotation-file-utilities/tests/BoundZero.java b/annotation-file-utilities/tests/BoundZero.java
new file mode 100644
index 0000000..4b1a4c8
--- /dev/null
+++ b/annotation-file-utilities/tests/BoundZero.java
@@ -0,0 +1,27 @@
+package annotator.tests;
+
+import static java.lang.annotation.ElementType.TYPE_USE;
+import java.lang.annotation.Target;
+
+@Target({TYPE_USE}) @interface A {}
+@Target({TYPE_USE}) @interface B {}
+@Target({TYPE_USE}) @interface C {}
+@Target({TYPE_USE}) @interface X {}
+@Target({TYPE_USE}) @interface Y {}
+
+class BoundZero<U> {
+  <T extends Object> void m1(T o) {}
+  <T extends @Y Object & Comparable<T>> void m2(T o) {}
+  <T extends @annotator.tests.Y Object & Comparable<T>> void m3(T o) {}
+  <T extends java.lang.Object & Comparable<T>> void m4(T o) {}
+  <T extends java.lang.@Y Object & Comparable<T>> void m5(T o) {}
+  <T extends java.lang.@annotator.tests.Y Object & Comparable<T>> void m6(T o) {}
+  <T extends Comparable<T>> void m7(T o) {}
+  <T extends @Y Comparable<T>> void m8(T o) {}
+  <T extends @annotator.tests.Y Comparable<T>> void m9(T o) {}
+  class Inner<@X V extends @Y Object> {}
+}
+
+class Outer<S extends java.io.Serializable> {
+}
+
diff --git a/annotation-file-utilities/tests/CastInsert.goal b/annotation-file-utilities/tests/CastInsert.goal
new file mode 100644
index 0000000..447ce9a
--- /dev/null
+++ b/annotation-file-utilities/tests/CastInsert.goal
@@ -0,0 +1,89 @@
+import java.io.PrintStream;
+
+public class CastInsert {
+
+    PrintStream out;
+    private int c = ((@checkers.nullness.quals.Nullable Integer) (((@checkers.nullness.quals.Nullable Integer) (12)) + ((@checkers.nullness.quals.Nullable Integer) (13))));
+    private String str = "this" + ((@checkers.nullness.quals.Nullable String) ("is")).concat(((@checkers.nullness.quals.Nullable String) ("string")));
+
+    void m() {
+        int i;
+    }
+
+    int m(String y, String[] z, int i) {
+        String x = ((@checkers.nullness.quals.Nullable String) (new String()));
+        String s;
+        s = ((@checkers.nullness.quals.Nullable String) (((@checkers.nullness.quals.Nullable String) (x)) + ((@checkers.nullness.quals.Nullable String) (x))));
+        s = y;
+        s = z[((@checkers.nullness.quals.Nullable Integer) (0))];
+        s = x;
+        int j = 0;
+        switch (((@checkers.nullness.quals.Nullable Integer) (i + 2))) {
+            case 1:
+                j = i + ((@checkers.nullness.quals.Nullable Integer) (1));
+                System.out.println(1);
+                break;
+            case 2:
+                j = i + 2;
+                System.out.println(((@checkers.nullness.quals.Nullable Integer) (2)));
+                break;
+            default:
+                j = i + 3;
+                System.out.println(-1);
+        }
+        j *= ((@checkers.nullness.quals.Nullable Integer) (i));
+        j = s != x ? ((@checkers.nullness.quals.Nullable Integer) (j)) : i;
+        do {
+            int h = ((@checkers.nullness.quals.Nullable Integer) (i)) & j;
+        } while (i < j);
+        for (int i2 : new int[5]) {
+            j = ((@checkers.nullness.quals.Nullable Integer) (i2));
+        }
+        for (int a = 0, b = ((@checkers.nullness.quals.Nullable Integer) (0)); a < ((@checkers.nullness.quals.Nullable Integer) (j)); a = ((@checkers.nullness.quals.Nullable Integer) (a + 1)), b++)
+            a = ((@checkers.nullness.quals.Nullable Integer) (b));
+        if (((@checkers.nullness.quals.Nullable Integer) (i)) < j)
+            i = j;
+        else
+            j = ((@checkers.nullness.quals.Nullable Integer) (i));
+        boolean b = ((@checkers.nullness.quals.Nullable String) (x)) instanceof String;
+        label: b = ((@checkers.nullness.quals.Nullable Boolean) (false));
+        Object o = ((@checkers.nullness.quals.Nullable CastInsert) (this)).out;
+        m(y, z, ((@checkers.nullness.quals.Nullable Integer) (i)));
+        int[][] test = new int[4][((@checkers.nullness.quals.Nullable Integer) (5))];
+        int[][] test2 = {{1, 2}, {1, 2, ((@checkers.nullness.quals.Nullable Integer) (3))}};
+        new String(((@checkers.nullness.quals.Nullable String) ("test")));
+        if (i < 1)
+            return ((@checkers.nullness.quals.Nullable Integer) (18));
+        synchronized (o) {
+            i = ((@checkers.nullness.quals.Nullable Integer) (i + i));
+        }
+        if (j < 1)
+            throw ((@checkers.nullness.quals.Nullable RuntimeException) (new IllegalStateException()));
+        try {
+            int t = ((@checkers.nullness.quals.Nullable Integer) (1));
+        } catch (Error e) {
+            i = j;
+        } catch (RuntimeException e) {
+            j = ((@checkers.nullness.quals.Nullable Integer) (i));
+        } finally {
+            j = i + ((@checkers.nullness.quals.Nullable Integer) (j));
+        }
+        j = (int) (i + ((@checkers.nullness.quals.Nullable Integer) (j)));
+        j = - ((@checkers.nullness.quals.Nullable Integer) (j));
+        while (i < ((@checkers.nullness.quals.Nullable Integer) (j)))
+            i = ((@checkers.nullness.quals.Nullable Integer) (i)) + 1;
+        ((@checkers.nullness.quals.Nullable CastInsert) (this)).out.println();
+        ((@checkers.nullness.quals.Nullable PrintStream) (System.out)).println();
+        Object obj = ((@checkers.nullness.quals.Nullable @annotation.A @annotation.B @annotation.C String) (null));
+        return 0;
+    }
+
+    void m2 (Object o) {
+        o = ((@checkers.nullness.quals.Nullable Map<@annotation.A String, @annotation.B Integer>) (o));
+        o = ((@annotation.B Object @annotation.A [] @checkers.nullness.quals.Nullable []) (o));
+        o = ((@annotation.B int @annotation.A [] @checkers.nullness.quals.Nullable []) (o));
+        o = ((@checkers.nullness.quals.Nullable List<@annotation.A ? extends @annotation.B Object>) (o));
+        o = ((int) (o));
+        o = ((Map<@annotation.A String, @annotation.B Integer>) (o));
+    }
+}
diff --git a/annotation-file-utilities/tests/CastInsert.jaif b/annotation-file-utilities/tests/CastInsert.jaif
new file mode 100644
index 0000000..4797cd4
--- /dev/null
+++ b/annotation-file-utilities/tests/CastInsert.jaif
@@ -0,0 +1,77 @@
+package annotation:
+annotation @A:
+annotation @B:
+annotation @C:
+
+package checkers.nullness.quals:
+annotation @Nullable: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER})
+
+package :
+class CastInsert:
+
+    field c:
+        insert-typecast Variable.initializer, Binary.rightOperand: @Nullable Integer
+        insert-typecast Variable.initializer, Binary.leftOperand: @Nullable Integer
+        insert-typecast Variable.initializer: @Nullable Integer
+
+    field str:
+        insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable String
+        insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @Nullable String
+
+    method m(Ljava/lang/String;[Ljava/lang/String;I)I:
+        insert-typecast Block.statement 0, Variable.initializer: @Nullable String
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @Nullable String
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable String
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable String
+        insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @Nullable Integer
+        insert-typecast Block.statement 7, Switch.expression, Parenthesized.expression: @Nullable Integer
+        insert-typecast Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable Integer
+        insert-typecast Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @Nullable Integer
+        insert-typecast Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @Nullable Integer
+        insert-typecast Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @Nullable Integer
+        insert-typecast Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @Nullable Integer
+        insert-typecast Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer
+        insert-typecast Block.statement 12, ForLoop.initializer 1, Variable.initializer: @Nullable Integer
+        insert-typecast Block.statement 12, ForLoop.condition, Binary.rightOperand: @Nullable Integer
+        insert-typecast Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer
+        insert-typecast Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @Nullable Integer
+        insert-typecast Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @Nullable Integer
+        insert-typecast Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @Nullable Integer
+        insert-typecast Block.statement 14, Variable.initializer, InstanceOf.expression: @Nullable String
+        insert-typecast Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @Nullable Boolean
+        insert-typecast Block.statement 16, Variable.initializer, MemberSelect.expression: @Nullable CastInsert
+        insert-typecast Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @Nullable Integer
+        insert-typecast Block.statement 18, Variable.initializer, NewArray.dimension 1: @Nullable Integer
+        insert-typecast Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @Nullable Integer
+        insert-typecast Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @Nullable String
+        insert-typecast Block.statement 21, If.thenStatement, Return.expression: @Nullable Integer
+        insert-typecast Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer
+        insert-typecast Block.statement 23, If.thenStatement, Throw.expression: @Nullable RuntimeException
+        insert-typecast Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @Nullable Integer
+        insert-typecast Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Integer
+        insert-typecast Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @Nullable Integer
+        insert-typecast Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @Nullable Integer
+        insert-typecast Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @Nullable Integer
+        insert-typecast Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @Nullable Integer
+        insert-typecast Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @Nullable Integer
+        insert-typecast Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @Nullable CastInsert
+        insert-typecast Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @Nullable PrintStream
+        insert-typecast Block.statement 30, Variable.initializer: @Nullable @A @B @C String
+
+    method m2(Ljava/lang/Object;)V:
+        insert-typecast Block.statement 0, ExpressionStatement.expression, Assignment.expression: @Nullable Map<String, Integer>
+            inner-type 3, 0: @A
+            inner-type 3, 1: @B
+        insert-typecast Block.statement 1, ExpressionStatement.expression, Assignment.expression: @Nullable Object[][]
+            inner-type 0, 0: @A
+            inner-type 0, 0, 0, 0: @B
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @Nullable int[][]
+            inner-type 0, 0: @A
+            inner-type 0, 0, 0, 0: @B
+        insert-typecast Block.statement 3, ExpressionStatement.expression, Assignment.expression: @Nullable List<? extends Object>
+            inner-type 3, 0: @A
+            inner-type 3, 0, 2, 0: @B
+        insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression: int
+        insert-typecast Block.statement 5, ExpressionStatement.expression, Assignment.expression: Map<String, Integer>
+            inner-type 3, 0: @A
+            inner-type 3, 1: @B
diff --git a/annotation-file-utilities/tests/CastInsert.java b/annotation-file-utilities/tests/CastInsert.java
new file mode 100644
index 0000000..ea066b6
--- /dev/null
+++ b/annotation-file-utilities/tests/CastInsert.java
@@ -0,0 +1,89 @@
+import java.io.PrintStream;
+
+public class CastInsert {
+
+    PrintStream out;
+    private int c = 12 + 13;
+    private String str = "this" + "is".concat("string");
+
+    void m() {
+        int i;
+    }
+
+    int m(String y, String[] z, int i) {
+        String x = new String();
+        String s;
+        s = x + x;
+        s = y;
+        s = z[0];
+        s = x;
+        int j = 0;
+        switch (i + 2) {
+            case 1:
+                j = i + 1;
+                System.out.println(1);
+                break;
+            case 2:
+                j = i + 2;
+                System.out.println(2);
+                break;
+            default:
+                j = i + 3;
+                System.out.println(-1);
+        }
+        j *= i;
+        j = s != x ? j : i;
+        do {
+            int h = i & j;
+        } while (i < j);
+        for (int i2 : new int[5]) {
+            j = i2;
+        }
+        for (int a = 0, b = 0; a < j; a = a + 1, b++)
+            a = b;
+        if (i < j)
+            i = j;
+        else
+            j = i;
+        boolean b = x instanceof String;
+        label: b = false;
+        Object o = this.out;
+        m(y, z, i);
+        int[][] test = new int[4][5];
+        int[][] test2 = {{1, 2}, {1, 2, 3}};
+        new String("test");
+        if (i < 1)
+            return 18;
+        synchronized (o) {
+            i = i + i;
+        }
+        if (j < 1)
+            throw new IllegalStateException();
+        try {
+            int t = 1;
+        } catch (Error e) {
+            i = j;
+        } catch (RuntimeException e) {
+            j = i;
+        } finally {
+            j = i + j;
+        }
+        j = (int) (i + j);
+        j = -j;
+        while (i < j)
+            i = i + 1;
+        this.out.println();
+        System.out.println();
+        Object obj = null;
+        return 0;
+    }
+
+    void m2 (Object o) {
+        o = o;
+        o = o;
+        o = o;
+        o = o;
+        o = o;
+        o = o;
+    }
+}
diff --git a/annotation-file-utilities/tests/ClassAnnotationParameter.goal b/annotation-file-utilities/tests/ClassAnnotationParameter.goal
new file mode 100644
index 0000000..76e8bd4
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassAnnotationParameter.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class ClassAnnotationParameter {
+  @java.lang.TestAnnotation(java.lang.Object.class)
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassAnnotationParameter.jaif b/annotation-file-utilities/tests/ClassAnnotationParameter.jaif
new file mode 100644
index 0000000..96ccf7f
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassAnnotationParameter.jaif
@@ -0,0 +1,9 @@
+package java.lang:
+
+annotation @TestAnnotation:
+    Class value
+
+package annotator.tests:
+class ClassAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value=java.lang.Object.class)
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassAnnotationParameter.java b/annotation-file-utilities/tests/ClassAnnotationParameter.java
new file mode 100644
index 0000000..993aa38
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassAnnotationParameter.java
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+public class ClassAnnotationParameter {
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassListAnnotationParameter.goal b/annotation-file-utilities/tests/ClassListAnnotationParameter.goal
new file mode 100644
index 0000000..4a41594
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassListAnnotationParameter.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class ClassListAnnotationParameter {
+  @java.lang.TestAnnotation({java.lang.Object.class,java.lang.String.class})
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassListAnnotationParameter.jaif b/annotation-file-utilities/tests/ClassListAnnotationParameter.jaif
new file mode 100644
index 0000000..b971989
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassListAnnotationParameter.jaif
@@ -0,0 +1,9 @@
+package java.lang:
+
+annotation @TestAnnotation:
+    Class[] value
+
+package annotator.tests:
+class ClassListAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value={java.lang.Object.class, java.lang.String.class})
diff --git a/annotation-file-utilities/tests/ClassListAnnotationParameter.java b/annotation-file-utilities/tests/ClassListAnnotationParameter.java
new file mode 100644
index 0000000..0ad674b
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassListAnnotationParameter.java
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+public class ClassListAnnotationParameter {
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/ClassSimple.goal b/annotation-file-utilities/tests/ClassSimple.goal
new file mode 100644
index 0000000..05707b2
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassSimple.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.Date;
+
+@java.lang.Tainted public class ClassSimple {
+  public Integer field;
+}
diff --git a/annotation-file-utilities/tests/ClassSimple.jaif b/annotation-file-utilities/tests/ClassSimple.jaif
new file mode 100644
index 0000000..8e51dac
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassSimple.jaif
@@ -0,0 +1,9 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @DoesNotExist: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ClassSimple: @java.lang.Tainted
+
+package:
+class ClassSimple: @java.lang.DoesNotExist
diff --git a/annotation-file-utilities/tests/ClassSimple.java b/annotation-file-utilities/tests/ClassSimple.java
new file mode 100644
index 0000000..e95912c
--- /dev/null
+++ b/annotation-file-utilities/tests/ClassSimple.java
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.Date;
+
+public class ClassSimple {
+  public Integer field;
+}
diff --git a/annotation-file-utilities/tests/ComplexLocationOne.goal b/annotation-file-utilities/tests/ComplexLocationOne.goal
new file mode 100644
index 0000000..dc26a83
--- /dev/null
+++ b/annotation-file-utilities/tests/ComplexLocationOne.goal
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+import java.util.Map;
+
+public class ComplexLocationOne {
+  public @java.lang.A List< @java.lang.B Map<@java.lang.C Integer, @java.lang.D String @java.lang.E []>> field;
+  public List< Outer<@java.lang.A Integer, @java.lang.B String[]>.@java.lang.C Inner<@java.lang.D Integer, @java.lang.E String[]>> entries;
+  class Outer<W, X> { class Inner<Y, Z> {} }
+}
diff --git a/annotation-file-utilities/tests/ComplexLocationOne.jaif b/annotation-file-utilities/tests/ComplexLocationOne.jaif
new file mode 100644
index 0000000..c94733e
--- /dev/null
+++ b/annotation-file-utilities/tests/ComplexLocationOne.jaif
@@ -0,0 +1,33 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ComplexLocationOne:
+
+    field field:
+        type: @java.lang.A
+        inner-type 3, 0: @java.lang.B
+        inner-type 3, 0, 3, 0: @java.lang.C
+        inner-type 3, 0, 3, 1, 0, 0: @java.lang.D
+        inner-type 3, 0, 3, 1: @java.lang.E
+
+    field entries:
+        type:
+            inner-type 3, 0, 3, 0: @java.lang.A
+            inner-type 3, 0, 3, 1, 0, 0: @java.lang.B
+            inner-type 3, 0, 1, 0: @java.lang.C
+            inner-type 3, 0, 1, 0, 3, 0: @java.lang.D
+            inner-type 3, 0, 1, 0, 3, 1, 0, 0: @java.lang.E
+
diff --git a/annotation-file-utilities/tests/ComplexLocationOne.java b/annotation-file-utilities/tests/ComplexLocationOne.java
new file mode 100644
index 0000000..9e45d45
--- /dev/null
+++ b/annotation-file-utilities/tests/ComplexLocationOne.java
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+import java.util.Map;
+
+public class ComplexLocationOne {
+  public List< Map<Integer, String []>> field;
+  public List< Outer<Integer, String[]>.Inner<Integer, String[]>> entries;
+  class Outer<W, X> { class Inner<Y, Z> {} }
+}
diff --git a/annotation-file-utilities/tests/ComplexLocationTwo.goal b/annotation-file-utilities/tests/ComplexLocationTwo.goal
new file mode 100644
index 0000000..5be0c43
--- /dev/null
+++ b/annotation-file-utilities/tests/ComplexLocationTwo.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.*;
+
+public class ComplexLocationTwo {
+  @java.lang.A Map<@java.lang.B Comparable<@java.lang.C Object @java.lang.D [] @java.lang.E [] @java.lang.F []>, @java.lang.G List<@java.lang.H Date>> field;
+}
diff --git a/annotation-file-utilities/tests/ComplexLocationTwo.jaif b/annotation-file-utilities/tests/ComplexLocationTwo.jaif
new file mode 100644
index 0000000..1b0255d
--- /dev/null
+++ b/annotation-file-utilities/tests/ComplexLocationTwo.jaif
@@ -0,0 +1,22 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @H: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ComplexLocationTwo:
+
+    field field:
+        type: @java.lang.A
+        inner-type 3, 0: @B
+        inner-type 3, 0, 3, 0: @D
+        inner-type 3, 0, 3, 0, 0, 0: @E
+        inner-type 3, 0, 3, 0, 0, 0, 0, 0: @F
+        inner-type 3, 0, 3, 0, 0, 0, 0, 0, 0, 0: @C
+        inner-type 3, 1: @G
+        inner-type 3, 1, 3, 0: @H
diff --git a/annotation-file-utilities/tests/ComplexLocationTwo.java b/annotation-file-utilities/tests/ComplexLocationTwo.java
new file mode 100644
index 0000000..c27f96d
--- /dev/null
+++ b/annotation-file-utilities/tests/ComplexLocationTwo.java
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.*;
+
+public class ComplexLocationTwo {
+  Map<Comparable<Object[][][]>, List<Date>> field;
+}
diff --git a/annotation-file-utilities/tests/ConstructorParam.goal b/annotation-file-utilities/tests/ConstructorParam.goal
new file mode 100644
index 0000000..580b999
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorParam.goal
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class ConstructorParam {
+  public ConstructorParam(@java.lang.UnderInitialization int paramB) {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/ConstructorParam.jaif b/annotation-file-utilities/tests/ConstructorParam.jaif
new file mode 100644
index 0000000..d58b454
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorParam.jaif
@@ -0,0 +1,11 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ConstructorParam:
+
+    method <init>(I)V:
+        parameter #0:
+          type: @java.lang.UnderInitialization
+
+
diff --git a/annotation-file-utilities/tests/ConstructorParam.java b/annotation-file-utilities/tests/ConstructorParam.java
new file mode 100644
index 0000000..d459c81
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorParam.java
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class ConstructorParam {
+  public ConstructorParam(int paramB) {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/ConstructorParamMultiple.goal b/annotation-file-utilities/tests/ConstructorParamMultiple.goal
new file mode 100644
index 0000000..6c2b20b
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorParamMultiple.goal
@@ -0,0 +1,12 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class ConstructorParamMultiple {
+  public ConstructorParamMultiple(
+      /* @Tainted*/ @java.lang.Tainted Object a,
+      /* @Tainted*/ @java.lang.Tainted List</* @UnderInitialization*/ @java.lang.UnderInitialization Integer> b,
+      /* @Tainted*/ @java.lang.Tainted int c) {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/ConstructorParamMultiple.jaif b/annotation-file-utilities/tests/ConstructorParamMultiple.jaif
new file mode 100644
index 0000000..e6049c9
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorParamMultiple.jaif
@@ -0,0 +1,18 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ConstructorParamMultiple:
+
+    method <init>(Ljava/lang/Object;Ljava/util/List;I)V:
+        parameter #0:
+            type: @java.lang.Tainted
+        parameter #1:
+            type: @java.lang.Tainted
+            inner-type 3, 0: @java.lang.UnderInitialization
+        parameter #2:
+            type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/ConstructorParamMultiple.java b/annotation-file-utilities/tests/ConstructorParamMultiple.java
new file mode 100644
index 0000000..060c394
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorParamMultiple.java
@@ -0,0 +1,12 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class ConstructorParamMultiple {
+  public ConstructorParamMultiple(
+      /* @Tainted*/ Object a,
+      /* @Tainted*/ List</* @UnderInitialization*/ Integer> b,
+      /* @Tainted*/ int c) {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/ConstructorReceivers.goal b/annotation-file-utilities/tests/ConstructorReceivers.goal
new file mode 100644
index 0000000..7c0954d
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorReceivers.goal
@@ -0,0 +1,58 @@
+package annotator.tests;
+
+public class ConstructorReceivers {
+    class C0 {
+        public C0(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this) {}
+        public   C0(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this, int i) {}
+    }
+
+    class C1 {
+        public C1(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this) {}
+        public   C1(@checkers.tainting.quals.Tainted ConstructorReceivers ConstructorReceivers.this, int i) {}
+    }
+
+    class C2 {
+        public C2(@checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C ConstructorReceivers ConstructorReceivers.this) {}
+    }
+
+    class C3 {
+        public C3(@checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C ConstructorReceivers ConstructorReceivers.this) {}
+    }
+
+    class P0<K, V> {
+        class C4 {
+            public C4(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {}
+
+            public C4(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, int i) {}
+        }
+
+        class C5 {
+            public C5(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {}
+
+            public C5(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, int i) {}
+        }
+
+        class C6 {
+            public C6(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {}
+
+            public C6(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P0.this, ConstructorReceivers.P0<K, V> other) {}
+        }
+
+        class C7 {
+            public C7(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {}
+
+            public C7(@checkers.tainting.quals.Tainted ConstructorReceivers.P0<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P0.this, ConstructorReceivers.P0<K, V> other) {}
+        }
+    }
+
+    class P1<K extends Object, V> {
+        class C8 {
+            public C8(@checkers.tainting.quals.Tainted ConstructorReceivers.P1<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P1.this) {}
+        }
+
+        class C9 {
+            public C9(@checkers.tainting.quals.Tainted ConstructorReceivers.P1<@annotation.Inner(0) K, @annotation.Inner(1) V> ConstructorReceivers.P1.this) {}
+        }
+    }
+}
+
diff --git a/annotation-file-utilities/tests/ConstructorReceivers.jaif b/annotation-file-utilities/tests/ConstructorReceivers.jaif
new file mode 100644
index 0000000..097a0b0
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorReceivers.jaif
@@ -0,0 +1,142 @@
+package annotation:
+annotation @A:
+annotation @B:
+annotation @C:
+annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package checkers.tainting.quals:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER})
+
+package annotator.tests:
+class ConstructorReceivers:
+
+    method <init>()V:
+        return:
+
+class ConstructorReceivers$C0:
+
+    field this$0:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+
+    method <init>(I)V:
+        return:
+        receiver: @Tainted
+
+class ConstructorReceivers$C1:
+
+    field this$0:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+
+    method <init>(I)V:
+        return:
+        receiver: @Tainted
+
+class ConstructorReceivers$C2:
+
+    field this$0:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted @A @B @C
+
+class ConstructorReceivers$C3:
+
+    field this$0:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted @A @B @C
+
+class ConstructorReceivers$P0$C4:
+
+    field this$1:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+
+    method <init>(I)V:
+        return:
+        receiver: @Tainted
+
+class ConstructorReceivers$P0$C5:
+
+    field this$1:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+
+    method <init>(I)V:
+        return:
+        receiver: @Tainted
+
+class ConstructorReceivers$P0$C6:
+
+    field this$1:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+
+    method <init>(Lannotator/tests/ConstructorReceivers$P0;)V:
+        return:
+        receiver: @Tainted
+            inner-type 1, 0, 3, 0: @Inner(0)
+            inner-type 1, 0, 3, 1: @Inner(1)
+
+class ConstructorReceivers$P0$C7:
+
+    field this$1:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+
+    method <init>(Lannotator/tests/ConstructorReceivers$P0;)V:
+        return:
+        receiver: @Tainted
+            inner-type 1, 0, 3, 0: @Inner(0)
+            inner-type 1, 0, 3, 1: @Inner(1)
+
+class ConstructorReceivers$P0:
+
+    field this$0:
+
+    method <init>()V:
+        return:
+
+class ConstructorReceivers$P1$C8:
+
+    field this$1:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+            inner-type 1, 0, 3, 0: @Inner(0)
+            inner-type 1, 0, 3, 1: @Inner(1)
+
+class ConstructorReceivers$P1$C9:
+
+    field this$1:
+
+    method <init>()V:
+        return:
+        receiver: @Tainted
+            inner-type 1, 0, 3, 0: @Inner(0)
+            inner-type 1, 0, 3, 1: @Inner(1)
+
+class ConstructorReceivers$P1:
+
+    field this$0:
+
+    method <init>()V:
+        return:
+
diff --git a/annotation-file-utilities/tests/ConstructorReceivers.java b/annotation-file-utilities/tests/ConstructorReceivers.java
new file mode 100644
index 0000000..03b0776
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorReceivers.java
@@ -0,0 +1,58 @@
+package annotator.tests;
+
+public class ConstructorReceivers {
+    class C0 {
+        public C0() {}
+        public   C0(int i) {}
+    }
+
+    class C1 {
+        public C1(ConstructorReceivers ConstructorReceivers.this) {}
+        public   C1(ConstructorReceivers ConstructorReceivers.this, int i) {}
+    }
+
+    class C2 {
+        public C2() {}
+    }
+
+    class C3 {
+        public C3(ConstructorReceivers ConstructorReceivers.this) {}
+    }
+
+    class P0<K, V> {
+        class C4 {
+            public C4() {}
+
+            public C4(int i) {}
+        }
+
+        class C5 {
+            public C5(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {}
+
+            public C5(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, int i) {}
+        }
+
+        class C6 {
+            public C6() {}
+
+            public C6(ConstructorReceivers.P0<K, V> other) {}
+        }
+
+        class C7 {
+            public C7(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this) {}
+
+            public C7(ConstructorReceivers.P0<K, V> ConstructorReceivers.P0.this, ConstructorReceivers.P0<K, V> other) {}
+        }
+    }
+
+    class P1<K extends Object, V> {
+        class C8 {
+            public C8() {}
+        }
+
+        class C9 {
+            public C9(ConstructorReceivers.P1<K, V> ConstructorReceivers.P1.this) {}
+        }
+    }
+}
+
diff --git a/annotation-file-utilities/tests/ConstructorReturn.goal b/annotation-file-utilities/tests/ConstructorReturn.goal
new file mode 100644
index 0000000..6e4c6d9
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorReturn.goal
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+class ConstructorReturn {
+   public @java.lang.Tainted ConstructorReturn() { }
+}
+
diff --git a/annotation-file-utilities/tests/ConstructorReturn.jaif b/annotation-file-utilities/tests/ConstructorReturn.jaif
new file mode 100644
index 0000000..2c7a752
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorReturn.jaif
@@ -0,0 +1,8 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ConstructorReturn:
+    method ConstructorReturn()V:
+return: @Tainted
+
diff --git a/annotation-file-utilities/tests/ConstructorReturn.java b/annotation-file-utilities/tests/ConstructorReturn.java
new file mode 100644
index 0000000..8d8c0ad
--- /dev/null
+++ b/annotation-file-utilities/tests/ConstructorReturn.java
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+class ConstructorReturn {
+   public ConstructorReturn() { }
+}
+
diff --git a/annotation-file-utilities/tests/Date.goal b/annotation-file-utilities/tests/Date.goal
new file mode 100644
index 0000000..a8b960d
--- /dev/null
+++ b/annotation-file-utilities/tests/Date.goal
@@ -0,0 +1,5 @@
+package annotator.tests;
+
+public class Date {
+  private long t;
+}
diff --git a/annotation-file-utilities/tests/Date.jaif b/annotation-file-utilities/tests/Date.jaif
new file mode 100644
index 0000000..a2007c7
--- /dev/null
+++ b/annotation-file-utilities/tests/Date.jaif
@@ -0,0 +1,7 @@
+package annotator.tests:
+class Date:
+
+    field t:
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/Date.java b/annotation-file-utilities/tests/Date.java
new file mode 100644
index 0000000..a8b960d
--- /dev/null
+++ b/annotation-file-utilities/tests/Date.java
@@ -0,0 +1,5 @@
+package annotator.tests;
+
+public class Date {
+  private long t;
+}
diff --git a/annotation-file-utilities/tests/DeclarationAnnotation.goal b/annotation-file-utilities/tests/DeclarationAnnotation.goal
new file mode 100644
index 0000000..ef31041
--- /dev/null
+++ b/annotation-file-utilities/tests/DeclarationAnnotation.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class DeclarationAnnotation {
+  @java.lang.SuppressWarnings("string argument")
+  public void foo() {
+  }
+}
diff --git a/annotation-file-utilities/tests/DeclarationAnnotation.jaif b/annotation-file-utilities/tests/DeclarationAnnotation.jaif
new file mode 100644
index 0000000..ae42163
--- /dev/null
+++ b/annotation-file-utilities/tests/DeclarationAnnotation.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+
+annotation @SuppressWarnings:
+    String value
+
+package annotator.tests:
+class DeclarationAnnotation:
+
+    method foo()V: @java.lang.SuppressWarnings("string argument")
+
diff --git a/annotation-file-utilities/tests/DeclarationAnnotation.java b/annotation-file-utilities/tests/DeclarationAnnotation.java
new file mode 100644
index 0000000..da33178
--- /dev/null
+++ b/annotation-file-utilities/tests/DeclarationAnnotation.java
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+public class DeclarationAnnotation {
+  public void foo() {
+  }
+}
diff --git a/annotation-file-utilities/tests/DuplicateAnnotation.goal b/annotation-file-utilities/tests/DuplicateAnnotation.goal
new file mode 100644
index 0000000..7192e8d
--- /dev/null
+++ b/annotation-file-utilities/tests/DuplicateAnnotation.goal
@@ -0,0 +1,32 @@
+package annotator.tests;
+
+// This test ensures that no annotation is re-inserted, if it already
+// existed in the original source code.
+public class DuplicateAnnotation {
+
+  @SuppressWarnings("A")
+  void m1() { }
+
+  @java.lang.SuppressWarnings("B")
+  void m2() { }
+
+  /*@SuppressWarnings("C")*/
+  void m3() { }
+
+  /*@java.lang.SuppressWarnings("D")*/
+  void m4() { }
+
+  @java.lang.SuppressWarnings("E")
+  void m5() { }
+
+  void m6() {
+    @SuppressWarnings("F")
+    @java.lang.Deprecated
+    Object o = new Object();
+  }
+
+  int /*@annotator.tests.TA*/ [] i;
+}
+
+@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+@interface TA {}
diff --git a/annotation-file-utilities/tests/DuplicateAnnotation.jaif b/annotation-file-utilities/tests/DuplicateAnnotation.jaif
new file mode 100644
index 0000000..a5516e7
--- /dev/null
+++ b/annotation-file-utilities/tests/DuplicateAnnotation.jaif
@@ -0,0 +1,19 @@
+package java.lang:
+annotation @SuppressWarnings:
+    String value
+annotation @Deprecated:
+
+package annotator.tests:
+annotation @TA: @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+
+class DuplicateAnnotation:
+    field i:
+        type: @TA
+
+    method m1()V: @java.lang.SuppressWarnings("A")
+    method m2()V: @java.lang.SuppressWarnings("B")
+    method m3()V: @java.lang.SuppressWarnings("C")
+    method m4()V: @java.lang.SuppressWarnings("D")
+    method m5()V: @java.lang.SuppressWarnings("E")
+    method m6()V:
+        local o: @java.lang.SuppressWarnings("F") @java.lang.Deprecated
diff --git a/annotation-file-utilities/tests/DuplicateAnnotation.java b/annotation-file-utilities/tests/DuplicateAnnotation.java
new file mode 100644
index 0000000..f6e1f7d
--- /dev/null
+++ b/annotation-file-utilities/tests/DuplicateAnnotation.java
@@ -0,0 +1,30 @@
+package annotator.tests;
+
+// This test ensures that no annotation is re-inserted, if it already
+// existed in the original source code.
+public class DuplicateAnnotation {
+
+  @SuppressWarnings("A")
+  void m1() { }
+
+  @java.lang.SuppressWarnings("B")
+  void m2() { }
+
+  /*@SuppressWarnings("C")*/
+  void m3() { }
+
+  /*@java.lang.SuppressWarnings("D")*/
+  void m4() { }
+
+  void m5() { }
+
+  void m6() {
+    @SuppressWarnings("F")
+    Object o = new Object();
+  }
+
+  int /*@annotator.tests.TA*/ [] i;
+}
+
+@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE})
+@interface TA {}
diff --git a/annotation-file-utilities/tests/EnumAnnotationParameter.goal b/annotation-file-utilities/tests/EnumAnnotationParameter.goal
new file mode 100644
index 0000000..1b0a1e6
--- /dev/null
+++ b/annotation-file-utilities/tests/EnumAnnotationParameter.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class EnumAnnotationParameter {
+  @java.lang.TestAnnotation(java.lang.TestEnum.A)
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumAnnotationParameter.jaif b/annotation-file-utilities/tests/EnumAnnotationParameter.jaif
new file mode 100644
index 0000000..ef626da
--- /dev/null
+++ b/annotation-file-utilities/tests/EnumAnnotationParameter.jaif
@@ -0,0 +1,9 @@
+package java.lang:
+
+annotation @TestAnnotation:
+    enum java.lang.TestEnum value
+
+package annotator.tests:
+class EnumAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value=A)
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumAnnotationParameter.java b/annotation-file-utilities/tests/EnumAnnotationParameter.java
new file mode 100644
index 0000000..58498e7
--- /dev/null
+++ b/annotation-file-utilities/tests/EnumAnnotationParameter.java
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+public class EnumAnnotationParameter {
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumListAnnotationParameter.goal b/annotation-file-utilities/tests/EnumListAnnotationParameter.goal
new file mode 100644
index 0000000..d962d41
--- /dev/null
+++ b/annotation-file-utilities/tests/EnumListAnnotationParameter.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class EnumListAnnotationParameter {
+  @java.lang.TestAnnotation({java.lang.TestEnum.A,java.lang.TestEnum.B})
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/EnumListAnnotationParameter.jaif b/annotation-file-utilities/tests/EnumListAnnotationParameter.jaif
new file mode 100644
index 0000000..238fba2
--- /dev/null
+++ b/annotation-file-utilities/tests/EnumListAnnotationParameter.jaif
@@ -0,0 +1,9 @@
+package java.lang:
+
+annotation @TestAnnotation:
+    enum java.lang.TestEnum[] value
+
+package annotator.tests:
+class EnumListAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value={A,B})
diff --git a/annotation-file-utilities/tests/EnumListAnnotationParameter.java b/annotation-file-utilities/tests/EnumListAnnotationParameter.java
new file mode 100644
index 0000000..aa1345f
--- /dev/null
+++ b/annotation-file-utilities/tests/EnumListAnnotationParameter.java
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+public class EnumListAnnotationParameter {
+  public void foo() {
+  }
+}
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/ExtImpl.goal b/annotation-file-utilities/tests/ExtImpl.goal
new file mode 100644
index 0000000..9a01f5a
--- /dev/null
+++ b/annotation-file-utilities/tests/ExtImpl.goal
@@ -0,0 +1,57 @@
+package annotator.tests;
+
+public class ExtImpl {
+  class Top<X, Y> {}
+  interface Iface<A, B> {}
+  interface Iface2<C, D> {}
+  interface Iface3 {}
+  interface Iface4<T, @org.checkerframework.checker.nullness.qual.NonNull S extends @org.checkerframework.checker.nullness.qual.NonNull @org.checkerframework.checker.nullness.qual.UnknownKeyFor Iface4<T, S>> {}
+
+  class C1 extends @A Top<@B Object, @C String> implements @D Iface<@E Integer, @F String> {}
+
+  class C2 implements @A Iface<@B String, @C Object>, @D Iface2<@E Object, @F Float> {}
+
+  class C3 {
+    class Iface3 implements annotator.tests.ExtImpl.Iface3 {}
+
+    /*
+     * the jaif file  says that the simple name of
+     * the return type in JVM format is
+     * LIface3;
+     */
+    @java.lang.SuppressWarnings({})
+    annotator.tests.ExtImpl.C3.Iface3 getI1() {
+      return null;
+    }
+
+    /*
+     * in this case, the jaif file uses the fully qualified name
+     * for the return type
+     * Lannotator.tests.ExtImpl.C3.Iface3;
+     */
+    @java.lang.SuppressWarnings({})
+    annotator.tests.ExtImpl.C3.Iface3 getI2() {
+      return null;
+    }
+
+    /*
+     * the jaif file uses the simple name of the return type
+     * LC3$Iface3;
+     */
+    @java.lang.SuppressWarnings({})
+    Iface3 getI3() {
+      return null;
+    }
+
+    /*
+     * in the jaif file, the return type is Iface3
+     * (ambiguous: could be short for the interface
+     * annotator.tests.ExtImpl.Iface3)
+     */
+    @java.lang.SuppressWarnings({})
+    Iface3 getI4() {
+      return null;
+    }
+  }
+}
+
diff --git a/annotation-file-utilities/tests/ExtImpl.jaif b/annotation-file-utilities/tests/ExtImpl.jaif
new file mode 100644
index 0000000..a925aba
--- /dev/null
+++ b/annotation-file-utilities/tests/ExtImpl.jaif
@@ -0,0 +1,48 @@
+package org.checkerframework.checker.nullness.qual:
+
+annotation @NonNull: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME)
+
+annotation @UnknownKeyFor: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME)
+
+package:
+
+annotation @A:
+annotation @B:
+annotation @C:
+annotation @D:
+annotation @E:
+annotation @F:
+
+package java.lang:
+
+annotation @SuppressWarnings:
+    String[] value
+
+package annotator.tests:
+
+class ExtImpl$C1:
+extends: @A
+inner-type 3, 0: @B
+inner-type 3, 1: @C
+implements 0: @D
+inner-type 3, 0: @E
+inner-type 3, 1: @F
+
+class ExtImpl$C2:
+implements 0: @A
+inner-type 3, 0: @B
+inner-type 3, 1: @C
+implements 1: @D
+inner-type 3, 0: @E
+inner-type 3, 1: @F
+
+class ExtImpl$C3:
+    method getI1()LIface3;: @SuppressWarnings({})
+    method getI2()Lannotator.tests.ExtImpl$C3$Iface3;: @SuppressWarnings({})
+    method getI3()LExtImpl$C3$Iface3;: @SuppressWarnings({})
+    method getI4()LIface3;: @SuppressWarnings({})
+
+class ExtImpl$Iface4:
+    typeparam 1: @org.checkerframework.checker.nullness.qual.NonNull
+    bound 1&1: @org.checkerframework.checker.nullness.qual.UnknownKeyFor @org.checkerframework.checker.nullness.qual.NonNull
+
diff --git a/annotation-file-utilities/tests/ExtImpl.java b/annotation-file-utilities/tests/ExtImpl.java
new file mode 100644
index 0000000..c612e2d
--- /dev/null
+++ b/annotation-file-utilities/tests/ExtImpl.java
@@ -0,0 +1,53 @@
+package annotator.tests;
+
+public class ExtImpl {
+  class Top<X, Y> {}
+  interface Iface<A, B> {}
+  interface Iface2<C, D> {}
+  interface Iface3 {}
+  interface Iface4<T, S extends Iface4<T, S>> {}
+
+  class C1 extends Top<Object, String> implements Iface<Integer, String> {}
+
+  class C2 implements Iface<String, Object>, Iface2<Object, Float> {}
+
+  class C3 {
+    class Iface3 implements annotator.tests.ExtImpl.Iface3 {}
+
+    /*
+     * the jaif file  says that the simple name of
+     * the return type in JVM format is
+     * LIface3;
+     */
+    annotator.tests.ExtImpl.C3.Iface3 getI1() {
+      return null;
+    }
+
+    /*
+     * in this case, the jaif file uses the fully qualified name
+     * for the return type
+     * Lannotator.tests.ExtImpl.C3.Iface3;
+     */
+    annotator.tests.ExtImpl.C3.Iface3 getI2() {
+      return null;
+    }
+
+    /*
+     * the jaif file uses the simple name of the return type
+     * LC3$Iface3;
+     */
+    Iface3 getI3() {
+      return null;
+    }
+
+    /*
+     * in the jaif file, the return type is Iface3
+     * (ambiguous: could be short for the interface
+     * annotator.tests.ExtImpl.Iface3)
+     */
+    Iface3 getI4() {
+      return null;
+    }
+  }
+}
+
diff --git a/annotation-file-utilities/tests/FieldGenericArray.goal b/annotation-file-utilities/tests/FieldGenericArray.goal
new file mode 100644
index 0000000..1a9f531
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldGenericArray.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class FieldGenericArray {
+  @InsideArray List<@GenericType Integer> @OuterMostType [] field;
+}
diff --git a/annotation-file-utilities/tests/FieldGenericArray.jaif b/annotation-file-utilities/tests/FieldGenericArray.jaif
new file mode 100644
index 0000000..7ea2624
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldGenericArray.jaif
@@ -0,0 +1,19 @@
+package :
+annotation @OuterMostType: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @InsideArray: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @GenericType: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldGenericArray:
+
+    field field:
+        type: @OuterMostType
+        inner-type 0, 0: @InsideArray
+        inner-type 0, 0, 3, 0: @GenericType
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/FieldGenericArray.java b/annotation-file-utilities/tests/FieldGenericArray.java
new file mode 100644
index 0000000..5391ce9
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldGenericArray.java
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class FieldGenericArray {
+  List<Integer>[] field;
+}
diff --git a/annotation-file-utilities/tests/FieldMultiple.goal b/annotation-file-utilities/tests/FieldMultiple.goal
new file mode 100644
index 0000000..8869abc
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldMultiple.goal
@@ -0,0 +1,12 @@
+package annotator.tests;
+
+@skip-test
+
+public class FieldMultiple {
+  public @java.lang.Tainted Integer foo;
+  public @java.lang.Tainted Integer bar;
+  @java.lang.Tainted
+  public Integer i;
+  @java.lang.Tainted
+  public Integer d;
+}
diff --git a/annotation-file-utilities/tests/FieldMultiple.jaif b/annotation-file-utilities/tests/FieldMultiple.jaif
new file mode 100644
index 0000000..3706c60
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldMultiple.jaif
@@ -0,0 +1,18 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldMultiple:
+
+    field foo:
+        type: @java.lang.Tainted
+
+    field bar:
+        type: @java.lang.Tainted
+
+    field i: @java.lang.Tainted
+
+    field d: @java.lang.Tainted
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/FieldMultiple.java b/annotation-file-utilities/tests/FieldMultiple.java
new file mode 100644
index 0000000..3b75510
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldMultiple.java
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+public class FieldMultiple {
+  public Integer foo, bar;
+  public Integer i, d;
+}
diff --git a/annotation-file-utilities/tests/FieldSimple.goal b/annotation-file-utilities/tests/FieldSimple.goal
new file mode 100644
index 0000000..5e8f1fd
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimple.goal
@@ -0,0 +1,5 @@
+package annotator.tests;
+
+public class FieldSimple {
+  private @java.lang.UnderInitialization Integer field;
+}
diff --git a/annotation-file-utilities/tests/FieldSimple.jaif b/annotation-file-utilities/tests/FieldSimple.jaif
new file mode 100644
index 0000000..aaf0872
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimple.jaif
@@ -0,0 +1,11 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldSimple:
+
+    field field:
+      type: @java.lang.UnderInitialization
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/FieldSimple.java b/annotation-file-utilities/tests/FieldSimple.java
new file mode 100644
index 0000000..656e066
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimple.java
@@ -0,0 +1,5 @@
+package annotator.tests;
+
+public class FieldSimple {
+  private Integer field;
+}
diff --git a/annotation-file-utilities/tests/FieldSimpleArray.goal b/annotation-file-utilities/tests/FieldSimpleArray.goal
new file mode 100644
index 0000000..1b299ca
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimpleArray.goal
@@ -0,0 +1,5 @@
+package annotator.tests;
+
+public class FieldSimpleArray {
+  @java.lang.UnderInitialization Integer @java.lang.Tainted [] field;
+}
diff --git a/annotation-file-utilities/tests/FieldSimpleArray.jaif b/annotation-file-utilities/tests/FieldSimpleArray.jaif
new file mode 100644
index 0000000..d7ad5a5
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimpleArray.jaif
@@ -0,0 +1,15 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldSimpleArray:
+
+    field field:
+        type: @java.lang.Tainted
+        inner-type 0, 0: @java.lang.UnderInitialization
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/FieldSimpleArray.java b/annotation-file-utilities/tests/FieldSimpleArray.java
new file mode 100644
index 0000000..1c18d78
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimpleArray.java
@@ -0,0 +1,5 @@
+package annotator.tests;
+
+public class FieldSimpleArray {
+  Integer[] field;
+}
diff --git a/annotation-file-utilities/tests/FieldSimpleGeneric.goal b/annotation-file-utilities/tests/FieldSimpleGeneric.goal
new file mode 100644
index 0000000..71270eb
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimpleGeneric.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class FieldSimpleGeneric {
+  @java.lang.UnderInitialization List<@java.lang.Tainted Integer> field;
+}
diff --git a/annotation-file-utilities/tests/FieldSimpleGeneric.jaif b/annotation-file-utilities/tests/FieldSimpleGeneric.jaif
new file mode 100644
index 0000000..6271030
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimpleGeneric.jaif
@@ -0,0 +1,15 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldSimpleGeneric:
+
+    field field:
+        type: @java.lang.UnderInitialization
+        inner-type 3, 0: @java.lang.Tainted
+
+    method <init>()V:
+
diff --git a/annotation-file-utilities/tests/FieldSimpleGeneric.java b/annotation-file-utilities/tests/FieldSimpleGeneric.java
new file mode 100644
index 0000000..257b91e
--- /dev/null
+++ b/annotation-file-utilities/tests/FieldSimpleGeneric.java
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class FieldSimpleGeneric {
+  List<Integer> field;
+}
diff --git a/annotation-file-utilities/tests/GenericAnnoBound.goal b/annotation-file-utilities/tests/GenericAnnoBound.goal
new file mode 100644
index 0000000..733b058
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericAnnoBound.goal
@@ -0,0 +1,9 @@
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE_USE)
+@interface Bla {}
+
+public class GenericAnnoBound<X extends @Bla Object> {
+  GenericAnnoBound(@Bla GenericAnnoBound<X> n, X p) {
+  }
+}
diff --git a/annotation-file-utilities/tests/GenericAnnoBound.jaif b/annotation-file-utilities/tests/GenericAnnoBound.jaif
new file mode 100644
index 0000000..d228304
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericAnnoBound.jaif
@@ -0,0 +1,8 @@
+package:
+annotation @Bla:
+
+class GenericAnnoBound:
+method <init>(LGenericAnnoBound;Ljava/lang/Object;)V:
+parameter 0:
+type: @Bla
+
diff --git a/annotation-file-utilities/tests/GenericAnnoBound.java b/annotation-file-utilities/tests/GenericAnnoBound.java
new file mode 100644
index 0000000..a5c6ee4
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericAnnoBound.java
@@ -0,0 +1,9 @@
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE_USE)
+@interface Bla {}
+
+public class GenericAnnoBound<X extends @Bla Object> {
+  GenericAnnoBound(GenericAnnoBound<X> n, X p) {
+  }
+}
diff --git a/annotation-file-utilities/tests/GenericArg.goal b/annotation-file-utilities/tests/GenericArg.goal
new file mode 100644
index 0000000..15e5255
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericArg.goal
@@ -0,0 +1,31 @@
+public class GenericArg<X> {
+
+  void mp(X p) {
+    @X
+    Object l;
+  }
+
+  X mr() {
+    @X
+    Object r;
+    return null;
+  }
+
+  <Y extends Number> void foo(Y p) {
+    @X
+    Object k;
+  }
+
+  <Z extends Integer> Z bar() {
+    @X
+    Integer j;
+    return null;
+  }
+
+  class Tricky {
+    void argh(X p) {
+      @X
+      Object a;
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/GenericArg.jaif b/annotation-file-utilities/tests/GenericArg.jaif
new file mode 100644
index 0000000..9e597be
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericArg.jaif
@@ -0,0 +1,20 @@
+package:
+annotation @X:
+
+class GenericArg:
+method mp(Ljava/lang/Object;)V:
+local l: @X
+
+method mr()Ljava/lang/Object;:
+local r: @X
+
+method foo(Ljava/lang/Number;)V:
+local k: @X
+
+method bar()Ljava/lang/Integer;:
+local j: @X
+
+class GenericArg$Tricky:
+method argh(Ljava/lang/Object;)V:
+local a: @X
+
diff --git a/annotation-file-utilities/tests/GenericArg.java b/annotation-file-utilities/tests/GenericArg.java
new file mode 100644
index 0000000..82806a0
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericArg.java
@@ -0,0 +1,26 @@
+public class GenericArg<X> {
+
+  void mp(X p) {
+    Object l;
+  }
+
+  X mr() {
+    Object r;
+    return null;
+  }
+
+  <Y extends Number> void foo(Y p) {
+    Object k;
+  }
+
+  <Z extends Integer> Z bar() {
+    Integer j;
+    return null;
+  }
+
+  class Tricky {
+    void argh(X p) {
+      Object a;
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/GenericCell.goal b/annotation-file-utilities/tests/GenericCell.goal
new file mode 100644
index 0000000..3ce9212
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericCell.goal
@@ -0,0 +1,32 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class GenericCell {
+  private @java.lang.ThisUnderInitialization List<@java.lang.ThisUnderInitialization IntCell> internalList;
+
+  public GenericCell(@java.lang.UnderInitialization List<@java.lang.UnderInitialization IntCell> list) {
+    internalList = list;
+  }
+
+  public @java.lang.PolyRaw List<@java.lang.PolyRaw IntCell> getList(@java.lang.PolyRaw GenericCell this) {
+    return internalList;
+  }
+
+  public static class IntCell {
+    private int i;
+
+    public IntCell(int in) {
+      this.i = in;
+    }
+
+    public void set(int in) {
+      this.i = in;
+    }
+
+    public int get() {
+      return i;
+    }
+  }
+
+}
diff --git a/annotation-file-utilities/tests/GenericCell.jaif b/annotation-file-utilities/tests/GenericCell.jaif
new file mode 100644
index 0000000..60d7e94
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericCell.jaif
@@ -0,0 +1,26 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @PolyRaw: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @ThisUnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class GenericCell:
+
+    field internalList:
+        type: @java.lang.ThisUnderInitialization
+        inner-type 3, 0: @java.lang.ThisUnderInitialization
+
+    method <init>(Ljava/util/List;)V:
+        parameter #0:
+            type: @java.lang.UnderInitialization
+            inner-type 3, 0: @java.lang.UnderInitialization
+
+    method getList()Ljava/util/List;:
+        return: @java.lang.PolyRaw
+            inner-type 3, 0: @java.lang.PolyRaw
+        receiver: @java.lang.PolyRaw
+
diff --git a/annotation-file-utilities/tests/GenericCell.java b/annotation-file-utilities/tests/GenericCell.java
new file mode 100644
index 0000000..688d472
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericCell.java
@@ -0,0 +1,32 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class GenericCell {
+  private List<IntCell> internalList;
+
+  public GenericCell(List<IntCell> list) {
+    internalList = list;
+  }
+
+  public List<IntCell> getList() {
+    return internalList;
+  }
+
+  public static class IntCell {
+    private int i;
+
+    public IntCell(int in) {
+      this.i = in;
+    }
+
+    public void set(int in) {
+      this.i = in;
+    }
+
+    public int get() {
+      return i;
+    }
+  }
+
+}
diff --git a/annotation-file-utilities/tests/GenericCellDoubled.goal b/annotation-file-utilities/tests/GenericCellDoubled.goal
new file mode 100644
index 0000000..2790cf8
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericCellDoubled.goal
@@ -0,0 +1,20 @@
+import java.lang.annotation.*;
+import java.util.*;
+
+@Target(ElementType.TYPE_USE)
+@interface X {}
+@Target(ElementType.TYPE_USE)
+@interface Y {}
+
+public class GenericCellDoubled {
+  List<@X Object> f;
+  @X List<@X Object> g;
+  @X List<@X ArrayList<@X Object>> h;
+  @X List<@X ArrayList<@X Object>> i;
+
+  Map<List<Object>, ArrayList<@X Integer>> j1;
+  @X Map<List<@X Object>, @X ArrayList<@X Integer>> j2;
+  @X Map<@X List<@X Object>, @X @Y ArrayList<@X Integer>> j3;
+
+  @X @Y List k;
+}
diff --git a/annotation-file-utilities/tests/GenericCellDoubled.jaif b/annotation-file-utilities/tests/GenericCellDoubled.jaif
new file mode 100644
index 0000000..4d67ac9
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericCellDoubled.jaif
@@ -0,0 +1,47 @@
+package:
+annotation @X:
+annotation @Y:
+
+package:
+class GenericCellDoubled:
+
+field f:
+type:
+inner-type 3, 0: @X
+
+field g:
+type:
+inner-type 3, 0: @X
+
+field h:
+type:
+inner-type 3, 0, 3, 0: @X
+
+field i:
+type:
+inner-type 3, 0: @X
+
+field j1:
+type:
+inner-type 3, 1, 3, 0: @X
+
+field j2:
+type:
+inner-type 3, 1, 3, 0: @X
+
+field j3:
+type:
+inner-type 3, 1, 3, 0: @X
+
+// duplicate, @X already there -> no effect
+field j3:
+type:
+inner-type 3, 1: @X
+
+// real
+field j3:
+type:
+inner-type 3, 1: @Y
+
+field k:
+type: @Y
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/GenericCellDoubled.java b/annotation-file-utilities/tests/GenericCellDoubled.java
new file mode 100644
index 0000000..3dc9684
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericCellDoubled.java
@@ -0,0 +1,20 @@
+import java.lang.annotation.*;
+import java.util.*;
+
+@Target(ElementType.TYPE_USE)
+@interface X {}
+@Target(ElementType.TYPE_USE)
+@interface Y {}
+
+public class GenericCellDoubled {
+  List<Object> f;
+  @X List<Object> g;
+  @X List<@X ArrayList<Object>> h;
+  @X List<ArrayList<@X Object>> i;
+
+  Map<List<Object>, ArrayList<Integer>> j1;
+  @X Map<List<@X Object>, @X ArrayList<Integer>> j2;
+  @X Map<@X List<@X Object>, @X ArrayList<Integer>> j3;
+
+  @X List k;
+}
diff --git a/annotation-file-utilities/tests/GenericMultiLevel.goal b/annotation-file-utilities/tests/GenericMultiLevel.goal
new file mode 100644
index 0000000..dfb5059
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericMultiLevel.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.*;
+
+public class GenericMultiLevel {
+  @java.lang.A Map<@java.lang.B Comparable<@java.lang.C Object>, @java.lang.G List<@java.lang.H Date>> field;
+}
diff --git a/annotation-file-utilities/tests/GenericMultiLevel.jaif b/annotation-file-utilities/tests/GenericMultiLevel.jaif
new file mode 100644
index 0000000..5bef1b2
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericMultiLevel.jaif
@@ -0,0 +1,19 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @H: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class GenericMultiLevel:
+
+    field field:
+        type: @java.lang.A
+        inner-type 3, 0: @B
+        inner-type 3, 0, 3, 0: @C
+        inner-type 3, 1: @G
+        inner-type 3, 1, 3, 0: @H
diff --git a/annotation-file-utilities/tests/GenericMultiLevel.java b/annotation-file-utilities/tests/GenericMultiLevel.java
new file mode 100644
index 0000000..c881b4b
--- /dev/null
+++ b/annotation-file-utilities/tests/GenericMultiLevel.java
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+import java.util.*;
+
+public class GenericMultiLevel {
+  Map<Comparable<Object>, List<Date>> field;
+}
diff --git a/annotation-file-utilities/tests/ImplicitUpper.goal b/annotation-file-utilities/tests/ImplicitUpper.goal
new file mode 100644
index 0000000..657ff49
--- /dev/null
+++ b/annotation-file-utilities/tests/ImplicitUpper.goal
@@ -0,0 +1,8 @@
+class ExplicitUpper<X extends @A Object> extends @B Object {
+}
+
+public class ImplicitUpper<Y extends @A Object> extends @B Object {
+}
+
+class Monomorphic extends @B Object {
+}
diff --git a/annotation-file-utilities/tests/ImplicitUpper.jaif b/annotation-file-utilities/tests/ImplicitUpper.jaif
new file mode 100644
index 0000000..2c057e5
--- /dev/null
+++ b/annotation-file-utilities/tests/ImplicitUpper.jaif
@@ -0,0 +1,16 @@
+package:
+annotation @A:
+package:
+annotation @B:
+
+class ExplicitUpper:
+bound 0 & 0: @A
+extends: @B
+
+class ImplicitUpper:
+bound 0 & 0: @A
+extends: @B
+
+class Monomorphic:
+    insert-annotation Class.bound -1: @B
+
diff --git a/annotation-file-utilities/tests/ImplicitUpper.java b/annotation-file-utilities/tests/ImplicitUpper.java
new file mode 100644
index 0000000..79e55b5
--- /dev/null
+++ b/annotation-file-utilities/tests/ImplicitUpper.java
@@ -0,0 +1,8 @@
+class ExplicitUpper<X extends Object> extends Object {
+}
+
+public class ImplicitUpper<Y> {
+}
+
+class Monomorphic {
+}
diff --git a/annotation-file-utilities/tests/Initializers.goal b/annotation-file-utilities/tests/Initializers.goal
new file mode 100644
index 0000000..c9ddfd1
--- /dev/null
+++ b/annotation-file-utilities/tests/Initializers.goal
@@ -0,0 +1,31 @@
+package annotator.tests;
+
+public class Initializers {
+  static {
+    String s1 = new @X String();
+  }
+
+  static {
+    String s2 = new @X String();
+  }
+
+  {
+    Object o1 = new @X Object();
+  }
+
+  {
+    Object o2 = new @X Object();
+  }
+
+  enum MyEnum {
+    A;
+
+    static {
+      String s = new @X String();
+    }
+
+    {
+      Object o = new @X Object();
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/Initializers.jaif b/annotation-file-utilities/tests/Initializers.jaif
new file mode 100644
index 0000000..1488dc8
--- /dev/null
+++ b/annotation-file-utilities/tests/Initializers.jaif
@@ -0,0 +1,29 @@
+package :
+annotation @X:
+
+package annotator.tests:
+
+class Initializers:
+staticinit *0:
+new *0: @X
+
+class Initializers:
+staticinit *1:
+new *0: @X
+
+class Initializers:
+instanceinit *0:
+new *0: @X
+
+class Initializers:
+instanceinit *1:
+new *0: @X
+
+class Initializers$MyEnum:
+staticinit *0:
+new *0: @X
+
+class Initializers$MyEnum:
+instanceinit *0:
+new *0: @X
+
diff --git a/annotation-file-utilities/tests/Initializers.java b/annotation-file-utilities/tests/Initializers.java
new file mode 100644
index 0000000..9c712cc
--- /dev/null
+++ b/annotation-file-utilities/tests/Initializers.java
@@ -0,0 +1,31 @@
+package annotator.tests;
+
+public class Initializers {
+  static {
+    String s1 = new String();
+  }
+
+  static {
+    String s2 = new String();
+  }
+
+  {
+    Object o1 = new Object();
+  }
+
+  {
+    Object o2 = new Object();
+  }
+
+  enum MyEnum {
+    A;
+
+    static {
+      String s = new String();
+    }
+
+    {
+      Object o = new Object();
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/InnerClass.goal b/annotation-file-utilities/tests/InnerClass.goal
new file mode 100644
index 0000000..2caf521
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClass.goal
@@ -0,0 +1,34 @@
+public class InnerClass {
+
+    void m() {
+        @A(1) Object o = new @A(2) Object();
+        if (o instanceof @A(3) String) {
+            @A(4) String s = (@A(5) String) o;
+        }
+
+        class Inner {
+
+            void m() {
+                @B(1) Object o = new @B(2) Object();
+                if (o instanceof @B(3) String) {
+                    @B(4) String s = (@B(5) String) o;
+                }
+            }
+        }
+
+        new InnerClass() {
+
+            void m() {
+                @C(1) Object o = new @C(2) Object();
+                if (o instanceof @C(3) String) {
+                    @C(4) String s = (@C(5) String) o;
+                }
+            }
+        };
+
+        o = new @D(2) Object();
+        if (o instanceof @D(3) String) {
+            @D(4) String s = (@D(5) String) o;
+        }
+    }
+}
diff --git a/annotation-file-utilities/tests/InnerClass.jaif b/annotation-file-utilities/tests/InnerClass.jaif
new file mode 100644
index 0000000..1c96781
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClass.jaif
@@ -0,0 +1,37 @@
+package:
+    annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+    annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+    annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+    annotation @D: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+
+    class InnerClass:
+        method m()V:
+            local o: @A(1)
+            new *0: @A(2)
+            instanceof *0: @A(3)
+            local s *0: @A(4)
+            typecast *0: @A(5)
+            new *2: @D(2)
+            instanceof *1: @D(3)
+            local s *1: @D(4)
+            typecast *1: @D(5)
+
+    class InnerClass$1Inner:
+        method m()V:
+            local o: @B(1)
+            new *0: @B(2)
+            instanceof *0: @B(3)
+            local s *0: @B(4)
+            typecast *0: @B(5)
+
+    class InnerClass$1:
+        method m()V:
+            local o: @C(1)
+            new *0: @C(2)
+            instanceof *0: @C(3)
+            local s *0: @C(4)
+            typecast *0: @C(5)
diff --git a/annotation-file-utilities/tests/InnerClass.java b/annotation-file-utilities/tests/InnerClass.java
new file mode 100644
index 0000000..035f072
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClass.java
@@ -0,0 +1,34 @@
+public class InnerClass {
+
+    void m() {
+        Object o = new Object();
+        if (o instanceof String) {
+            String s = (String) o;
+        }
+
+        class Inner {
+
+            void m() {
+                Object o = new Object();
+                if (o instanceof String) {
+                    String s = (String) o;
+                }
+            }
+        }
+
+        new InnerClass() {
+
+            void m() {
+                Object o = new Object();
+                if (o instanceof String) {
+                    String s = (String) o;
+                }
+            }
+        };
+
+        o = new Object();
+        if (o instanceof String) {
+            String s = (String) o;
+        }
+    }
+}
diff --git a/annotation-file-utilities/tests/InnerClassAnonymous.goal b/annotation-file-utilities/tests/InnerClassAnonymous.goal
new file mode 100644
index 0000000..84416b3
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClassAnonymous.goal
@@ -0,0 +1,30 @@
+package annotator.tests;
+
+import java.io.Serializable;
+
+public class InnerClassAnonymous {
+  public Object field;
+
+  public class NamedInnerClass {
+    public Object namedField;
+  }
+
+  public Serializable foo() {
+    return new Serializable() {
+      public final @java.lang.Tainted Object serialVersionUID = null;
+    };
+  }
+
+  public Serializable bar() {
+    return new Serializable() {
+      private static final @NonNegative long serialVersionUID = 0;
+    };
+  }
+
+  public Serializable baz() {
+    return new Serializable() {
+      private static final @InnerlyAnnotated long serialVersionUID = 0;
+    };
+  }
+
+}
diff --git a/annotation-file-utilities/tests/InnerClassAnonymous.jaif b/annotation-file-utilities/tests/InnerClassAnonymous.jaif
new file mode 100644
index 0000000..6c752a2
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClassAnonymous.jaif
@@ -0,0 +1,23 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package:
+annotation @NonNegative: @java.lang.annotation.Target(value={TYPE_USE})
+annotation @InnerlyAnnotated: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InnerClassAnonymous$1:
+
+    field serialVersionUID:
+        type: @java.lang.Tainted
+
+class InnerClassAnonymous$2:
+
+    field serialVersionUID:
+        type: @NonNegative
+
+class InnerClassAnonymous$3:
+
+    field serialVersionUID:
+        type: @InnerlyAnnotated
+
diff --git a/annotation-file-utilities/tests/InnerClassAnonymous.java b/annotation-file-utilities/tests/InnerClassAnonymous.java
new file mode 100644
index 0000000..335e21d
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClassAnonymous.java
@@ -0,0 +1,30 @@
+package annotator.tests;
+
+import java.io.Serializable;
+
+public class InnerClassAnonymous {
+  public Object field;
+
+  public class NamedInnerClass {
+    public Object namedField;
+  }
+
+  public Serializable foo() {
+    return new Serializable() {
+      public final Object serialVersionUID = null;
+    };
+  }
+
+  public Serializable bar() {
+    return new Serializable() {
+      private static final long serialVersionUID = 0;
+    };
+  }
+
+  public Serializable baz() {
+    return new Serializable() {
+      private static final long serialVersionUID = 0;
+    };
+  }
+
+}
diff --git a/annotation-file-utilities/tests/InnerClassSimple.goal b/annotation-file-utilities/tests/InnerClassSimple.goal
new file mode 100644
index 0000000..3ebb0ff
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClassSimple.goal
@@ -0,0 +1,12 @@
+package annotator.tests;
+
+import java.util.Date;
+
+@skip-test
+public class InnerClassSimple {
+  public Integer field;
+
+  @java.lang.Tainted @java.lang.DoesNotExist public class ActualInnerClass {
+    @java.lang.Tainted Date d;
+  }
+}
diff --git a/annotation-file-utilities/tests/InnerClassSimple.jaif b/annotation-file-utilities/tests/InnerClassSimple.jaif
new file mode 100644
index 0000000..5a126cd
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClassSimple.jaif
@@ -0,0 +1,13 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @DoesNotExist: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InnerClassSimple$ActualInnerClass: @java.lang.Tainted
+
+    field d:
+        type: @java.lang.Tainted
+
+package annotator.tests:
+class InnerClassSimple$ActualInnerClass: @java.lang.DoesNotExist
+
diff --git a/annotation-file-utilities/tests/InnerClassSimple.java b/annotation-file-utilities/tests/InnerClassSimple.java
new file mode 100644
index 0000000..7f13944
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerClassSimple.java
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+import java.util.Date;
+
+public class InnerClassSimple {
+  public Integer field;
+
+  public class ActualInnerClass {
+    Date d;
+  }
+}
diff --git a/annotation-file-utilities/tests/InnerReceivers.goal b/annotation-file-utilities/tests/InnerReceivers.goal
new file mode 100644
index 0000000..33e278e
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerReceivers.goal
@@ -0,0 +1,71 @@
+package annotator.tests;
+
+public class InnerReceivers {
+
+    InnerReceivers i = new InnerReceivers() {
+
+        void m() {}
+
+        class Inner {
+            void m(@A Inner this) {}
+
+            void m1(@B Inner this) {}
+        }
+    };
+
+    void m(@C InnerReceivers this) {}
+
+    void m1(@D InnerReceivers this) {}
+
+    void m2(annotator.tests.@D1 InnerReceivers this) {}
+
+    class Inner1<Y, Z> {
+
+        void m(@E(0) InnerReceivers.@E(1) Inner1<@E(2) Y, @E(3) Z> this) {}
+
+        void m1(@F InnerReceivers.Inner1<Y, Z> this) {}
+
+        void m2(annotator.tests.@F1 InnerReceivers.Inner1<Y, Z> this) {}
+
+        class Inner2 {
+
+            void m(@G(0) InnerReceivers.@G(1) Inner1<@G(2) Y, @G(3) Z>.@G(4) Inner2 this) {}
+
+            void m1(@H InnerReceivers.Inner1<Y, Z>.Inner2 this) {}
+        }
+    }
+
+    static class StaticInner1 {
+
+        void m(InnerReceivers.@I StaticInner1 this) {}
+
+        void m1(InnerReceivers.@J StaticInner1 this) {}
+
+        void m2(annotator.tests.InnerReceivers.@K StaticInner1 this) {}
+    }
+
+    static class StaticInner3<Y, Z> {
+
+        void m(InnerReceivers.@I1(0) StaticInner3<@I1(1) Y, @I1(2) Z> this) {}
+
+        void m1(InnerReceivers.@J StaticInner3<Y, Z> this) {}
+
+        void m2(annotator.tests.InnerReceivers.@K StaticInner3<Y, Z> this) {}
+    }
+}
+
+class Outer<K> {
+    static class StaticInner2 {
+
+        void m(Outer.@L StaticInner2 this) {}
+
+        void m1(Outer.@M StaticInner2 this) {}
+
+        void m2(annotator.tests.Outer.@N StaticInner2 this) {}
+
+        static class StaticInner3 {
+
+            void m(Outer.StaticInner2.@O StaticInner3 this) {}
+        }
+    }
+}
diff --git a/annotation-file-utilities/tests/InnerReceivers.jaif b/annotation-file-utilities/tests/InnerReceivers.jaif
new file mode 100644
index 0000000..b15b53d
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerReceivers.jaif
@@ -0,0 +1,108 @@
+package:
+    annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @D: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @D1: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @E: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+    annotation @F: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @F1: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @G: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+    annotation @H: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @I: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @I1: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+    annotation @J: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @K: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @L: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @M: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @N: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @O: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @P: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @X: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+    // Try to insert a receiver parameter on a method declaration in an
+    // anonymous inner class. This is illegal, so should do nothing.
+    class InnerReceivers$1:
+        method m()V:
+            receiver: @X
+
+    class InnerReceivers$1$Inner:
+        method m()V:
+            receiver: @A
+        method m1()V:
+            receiver: @B
+
+    class InnerReceivers:
+        method m()V:
+            receiver: @C
+        method m1()V:
+            receiver: @D
+        method m2()V:
+            receiver: @D1
+
+    class InnerReceivers$Inner1:
+        method m()V:
+            receiver: @E(0)
+                inner-type 1, 0: @E(1)
+                inner-type 1, 0, 3, 0: @E(2)
+                inner-type 1, 0, 3, 1: @E(3)
+                // The following two annotations are in incorrect locations and
+                // should not be inserted.
+                inner-type 3, 0: @X
+                inner-type 3, 1: @X
+        method m1()V:
+            receiver: @F
+        method m2()V:
+            receiver: @F1
+
+    class InnerReceivers$Inner1$Inner2:
+        method m()V:
+            receiver: @G(0)
+                inner-type 1, 0: @G(1)
+                inner-type 1, 0, 3, 0: @G(2)
+                inner-type 1, 0, 3, 1: @G(3)
+                inner-type 1, 0, 1, 0: @G(4)
+        method m1()V:
+            receiver: @H
+
+    class InnerReceivers$StaticInner1:
+        method m()V:
+            receiver: @I
+                // Incorrect location, should not be inserted
+                inner-type 1, 0: @X
+        method m1()V:
+            receiver: @J
+        method m2()V:
+            receiver: @K
+
+    class InnerReceivers$StaticInner3:
+        method m()V:
+            receiver: @I1(0)
+                inner-type 3, 0: @I1(1)
+                inner-type 3, 1: @I1(2)
+                // Incorrect location, should not be inserted
+                inner-type 1, 0: @X
+        method m1()V:
+            receiver: @J
+        method m2()V:
+            receiver: @K
+
+    class Outer$StaticInner2:
+        method m()V:
+            receiver: @L
+                // Incorrect location, should not be inserted
+                inner-type 1, 0: @X
+        method m1()V:
+            receiver: @M
+        method m2()V:
+            receiver: @N
+
+    class Outer$StaticInner2$StaticInner3:
+        method m()V:
+            receiver: @O
+                // Incorrect location, should not be inserted
+                inner-type 1, 0: @X
diff --git a/annotation-file-utilities/tests/InnerReceivers.java b/annotation-file-utilities/tests/InnerReceivers.java
new file mode 100644
index 0000000..d33134a
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerReceivers.java
@@ -0,0 +1,71 @@
+package annotator.tests;
+
+public class InnerReceivers {
+
+    InnerReceivers i = new InnerReceivers() {
+
+        void m() {}
+
+        class Inner {
+            void m() {}
+
+            void m1(Inner this) {}
+        }
+    };
+
+    void m() {}
+
+    void m1(InnerReceivers this) {}
+
+    void m2(annotator.tests.InnerReceivers this) {}
+
+    class Inner1<Y, Z> {
+
+        void m() {}
+
+        void m1(InnerReceivers.Inner1<Y, Z> this) {}
+
+        void m2(annotator.tests.InnerReceivers.Inner1<Y, Z> this) {}
+
+        class Inner2 {
+
+            void m() {}
+
+            void m1(InnerReceivers.Inner1<Y, Z>.Inner2 this) {}
+        }
+    }
+
+    static class StaticInner1 {
+
+        void m() {}
+
+        void m1(InnerReceivers.StaticInner1 this) {}
+
+        void m2(annotator.tests.InnerReceivers.StaticInner1 this) {}
+    }
+
+    static class StaticInner3<Y, Z> {
+
+        void m() {}
+
+        void m1(InnerReceivers.StaticInner3<Y, Z> this) {}
+
+        void m2(annotator.tests.InnerReceivers.StaticInner3<Y, Z> this) {}
+    }
+}
+
+class Outer<K> {
+    static class StaticInner2 {
+
+        void m() {}
+
+        void m1(Outer.StaticInner2 this) {}
+
+        void m2(annotator.tests.Outer.StaticInner2 this) {}
+
+        static class StaticInner3 {
+
+            void m() {}
+        }
+    }
+}
diff --git a/annotation-file-utilities/tests/InnerTypeResolution.goal b/annotation-file-utilities/tests/InnerTypeResolution.goal
new file mode 100644
index 0000000..f1cbc0c
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerTypeResolution.goal
@@ -0,0 +1,16 @@
+package annotator.tests;
+
+import java.util.Map;
+
+public class InnerTypeResolution {
+    @java.lang.annotation.Deprecated
+    Map.Entry method01(@java.lang.annotation.Tainted Map m) {
+        return null;
+    }
+
+    @java.lang.annotation.Deprecated
+    Map.Entry method02(java.util.Map<@java.lang.annotation.Tainted String, Object> m) {
+        return null;
+    }
+}
+
diff --git a/annotation-file-utilities/tests/InnerTypeResolution.jaif b/annotation-file-utilities/tests/InnerTypeResolution.jaif
new file mode 100644
index 0000000..54331ba
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerTypeResolution.jaif
@@ -0,0 +1,21 @@
+package java.lang.annotation:
+annotation @Deprecated:
+annotation @Tainted:
+
+package annotator.tests:
+class InnerTypeResolution:
+
+    method <init>()V:
+        return:
+
+    method method01(Ljava/util/Map;)Ljava/util/Map$Entry;: @Deprecated
+        return:
+        parameter #0:
+            type: @Tainted
+
+    method method02(Ljava/util/Map;)Ljava/util/Map$Entry;: @Deprecated
+        return:
+        parameter #0:
+            type:
+                inner-type 3, 0: @Tainted
+
diff --git a/annotation-file-utilities/tests/InnerTypeResolution.java b/annotation-file-utilities/tests/InnerTypeResolution.java
new file mode 100644
index 0000000..e7fee87
--- /dev/null
+++ b/annotation-file-utilities/tests/InnerTypeResolution.java
@@ -0,0 +1,14 @@
+package annotator.tests;
+
+import java.util.Map;
+
+public class InnerTypeResolution {
+    Map.Entry method01(Map m) {
+        return null;
+    }
+
+    Map.Entry method02(java.util.Map<String, Object> m) {
+        return null;
+    }
+}
+
diff --git a/annotation-file-utilities/tests/InstanceOfMultiple.goal b/annotation-file-utilities/tests/InstanceOfMultiple.goal
new file mode 100644
index 0000000..4be208d
--- /dev/null
+++ b/annotation-file-utilities/tests/InstanceOfMultiple.goal
@@ -0,0 +1,19 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class InstanceOfMultiple {
+  public void foo(Object o) {
+    if (o instanceof @java.lang.Tainted List) {
+      if (o instanceof InstanceOfMultiple) {
+        if (o instanceof @java.lang.UnderInitialization Object) {
+          System.out.println(o);
+        }
+      }
+    }
+
+    if (o instanceof @java.lang.UnderInitialization List<@java.lang.Tainted ?>) {
+      System.out.println(o);
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/InstanceOfMultiple.jaif b/annotation-file-utilities/tests/InstanceOfMultiple.jaif
new file mode 100644
index 0000000..328bbda
--- /dev/null
+++ b/annotation-file-utilities/tests/InstanceOfMultiple.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InstanceOfMultiple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        instanceof #1: @java.lang.Tainted
+        instanceof #15: @java.lang.UnderInitialization
+        instanceof #29: @java.lang.UnderInitialization
+            inner-type 3, 0: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/InstanceOfMultiple.java b/annotation-file-utilities/tests/InstanceOfMultiple.java
new file mode 100644
index 0000000..9995ba7
--- /dev/null
+++ b/annotation-file-utilities/tests/InstanceOfMultiple.java
@@ -0,0 +1,19 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class InstanceOfMultiple {
+  public void foo(Object o) {
+    if (o instanceof List) {
+      if (o instanceof InstanceOfMultiple) {
+        if (o instanceof Object) {
+          System.out.println(o);
+        }
+      }
+    }
+
+    if (o instanceof List<?>) {
+      System.out.println(o);
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/InstanceOfSimple.goal b/annotation-file-utilities/tests/InstanceOfSimple.goal
new file mode 100644
index 0000000..e891d46
--- /dev/null
+++ b/annotation-file-utilities/tests/InstanceOfSimple.goal
@@ -0,0 +1,12 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class InstanceOfSimple {
+  public void foo(Object o) {
+    if (o instanceof @java.lang.Tainted List) {
+      o = new Object();
+    }
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/InstanceOfSimple.jaif b/annotation-file-utilities/tests/InstanceOfSimple.jaif
new file mode 100644
index 0000000..02a2fc9
--- /dev/null
+++ b/annotation-file-utilities/tests/InstanceOfSimple.jaif
@@ -0,0 +1,11 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InstanceOfSimple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        instanceof #1: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/InstanceOfSimple.java b/annotation-file-utilities/tests/InstanceOfSimple.java
new file mode 100644
index 0000000..e55d8fa
--- /dev/null
+++ b/annotation-file-utilities/tests/InstanceOfSimple.java
@@ -0,0 +1,12 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class InstanceOfSimple {
+  public void foo(Object o) {
+    if (o instanceof List) {
+      o = new Object();
+    }
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/IntCell.goal b/annotation-file-utilities/tests/IntCell.goal
new file mode 100644
index 0000000..8231c3f
--- /dev/null
+++ b/annotation-file-utilities/tests/IntCell.goal
@@ -0,0 +1,17 @@
+package annotator.tests;
+
+public class IntCell {
+  private int i;
+
+  public IntCell(int in) {
+    this.i = in;
+  }
+
+  public void set(@java.lang.UnderInitialization IntCell this, int in) {
+    this.i = in;
+  }
+
+  public int get(@java.lang.Tainted IntCell this) {
+    return i;
+  }
+}
diff --git a/annotation-file-utilities/tests/IntCell.jaif b/annotation-file-utilities/tests/IntCell.jaif
new file mode 100644
index 0000000..9b00af7
--- /dev/null
+++ b/annotation-file-utilities/tests/IntCell.jaif
@@ -0,0 +1,19 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class IntCell:
+
+    field i:
+
+    method <init>(I)V:
+
+    method set(I)V:
+        receiver: @java.lang.UnderInitialization
+
+    method get()I:
+        receiver: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/IntCell.java b/annotation-file-utilities/tests/IntCell.java
new file mode 100644
index 0000000..ddc982b
--- /dev/null
+++ b/annotation-file-utilities/tests/IntCell.java
@@ -0,0 +1,17 @@
+package annotator.tests;
+
+public class IntCell {
+  private int i;
+
+  public IntCell(int in) {
+    this.i = in;
+  }
+
+  public void set(int in) {
+    this.i = in;
+  }
+
+  public int get() {
+    return i;
+  }
+}
diff --git a/annotation-file-utilities/tests/LambdaExpression.goal b/annotation-file-utilities/tests/LambdaExpression.goal
new file mode 100644
index 0000000..373fe37
--- /dev/null
+++ b/annotation-file-utilities/tests/LambdaExpression.goal
@@ -0,0 +1,64 @@
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.ToIntBiFunction;
+
+@Target(ElementType.TYPE_USE) @interface A {}
+
+@Target(ElementType.TYPE_USE) @interface B {}
+
+@Target(ElementType.TYPE_USE) @interface C {}
+
+@skip-test
+class LambdaExpression {
+  //Single inferred-type parameter
+  IntFunction<Integer> f0 = (x) -> x+1;
+
+  //Parentheses optional for single inferred-type parameter
+  IntFunction<Integer> f1 = x -> x+1;
+
+  //Single declared-type parameter, expression body
+  IntFunction<Integer> f2 = (@A int x) -> x+1;
+
+  //Single declared-type parameter, block body
+  IntFunction<Integer> f3 = (@A int x) -> { return x+1; };
+
+  //Multiple declared-type parameters
+  IntBinaryOperator f4 = (@A int x, @B int y) -> x+y;
+
+  //Generic argument type
+  static final ToIntBiFunction<String[], List<? extends CharSequence>>
+  selectCommon = (@A String @B [] array,
+                  @A List<@B ? extends @C CharSequence> list) ->
+    {
+      int total = 0;
+      for (int i = 0; i < array.length; i++) {
+        Iterator<? extends CharSequence> iter = list.iterator();
+        String str = array[i];
+        while (iter.hasNext()) {
+          CharSequence seq = iter.next();
+          if (seq.toString().equals(str)) {
+            ++total;
+            iter.remove();
+            break;
+          }
+        }
+      }
+      return total;
+    };
+
+  public static void main(String[] args) {
+    String[] ss = {"a", "b"};
+    System.out.println(selectCommon.applyAsInt(args, Arrays.asList(ss)));
+  }
+}
+
diff --git a/annotation-file-utilities/tests/LambdaExpression.jaif b/annotation-file-utilities/tests/LambdaExpression.jaif
new file mode 100644
index 0000000..28412a7
--- /dev/null
+++ b/annotation-file-utilities/tests/LambdaExpression.jaif
@@ -0,0 +1,34 @@
+package:
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+
+class LambdaExpression:
+
+    field f2:
+        lambda *0:
+            parameter 0:
+                type: @A
+
+    field f3:
+        lambda *0:
+            parameter 0:
+                type: @A
+
+    field f4:
+        lambda *0:
+            parameter 0:
+                type: @A
+            parameter 1:
+                type: @B
+
+    field selectCommon:
+        lambda *0:
+            parameter 0:
+                type: @B
+                inner-type 0, 0: @A
+            parameter 1:
+                type: @A
+                inner-type 3, 0: @B
+                inner-type 3, 0, 2, 0: @C
+
diff --git a/annotation-file-utilities/tests/LambdaExpression.java b/annotation-file-utilities/tests/LambdaExpression.java
new file mode 100644
index 0000000..67e0386
--- /dev/null
+++ b/annotation-file-utilities/tests/LambdaExpression.java
@@ -0,0 +1,63 @@
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.IntBinaryOperator;
+import java.util.function.IntFunction;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.function.ToIntBiFunction;
+
+@Target(ElementType.TYPE_USE) @interface A {}
+
+@Target(ElementType.TYPE_USE) @interface B {}
+
+@Target(ElementType.TYPE_USE) @interface C {}
+
+class LambdaExpression {
+  //Single inferred-type parameter
+  IntFunction<Integer> f0 = (x) -> x+1;
+
+  //Parentheses optional for single inferred-type parameter
+  IntFunction<Integer> f1 = x -> x+1;
+
+  //Single declared-type parameter, expression body
+  IntFunction<Integer> f2 = (int x) -> x+1;
+
+  //Single declared-type parameter, block body
+  IntFunction<Integer> f3 = (int x) -> { return x+1; };
+
+  //Multiple declared-type parameters
+  IntBinaryOperator f4 = (int x, int y) -> x+y;
+
+  //Generic argument type
+  static final ToIntBiFunction<String[], List<? extends CharSequence>>
+  selectCommon = (String[] array,
+                  List<? extends CharSequence> list) ->
+    {
+      int total = 0;
+      for (int i = 0; i < array.length; i++) {
+        Iterator<? extends CharSequence> iter = list.iterator();
+        String str = array[i];
+        while (iter.hasNext()) {
+          CharSequence seq = iter.next();
+          if (seq.toString().equals(str)) {
+            ++total;
+            iter.remove();
+            break;
+          }
+        }
+      }
+      return total;
+    };
+
+  public static void main(String[] args) {
+    String[] ss = {"a", "b"};
+    System.out.println(selectCommon.applyAsInt(args, Arrays.asList(ss)));
+  }
+}
+
diff --git a/annotation-file-utilities/tests/LocalArray.goal b/annotation-file-utilities/tests/LocalArray.goal
new file mode 100644
index 0000000..204346e
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalArray.goal
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+public class LocalArray {
+
+  public void foo() {
+    @java.lang.UnderInitialization Object @java.lang.Tainted [] o = null;
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalArray.jaif b/annotation-file-utilities/tests/LocalArray.jaif
new file mode 100644
index 0000000..93df2c2
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalArray.jaif
@@ -0,0 +1,16 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalArray:
+
+    method <init>()V:
+
+    method foo()V:
+        local 1 #2+8:
+            type: @java.lang.Tainted
+                inner-type 0, 0: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/LocalArray.java b/annotation-file-utilities/tests/LocalArray.java
new file mode 100644
index 0000000..0f23dd9
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalArray.java
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+public class LocalArray {
+
+  public void foo() {
+    Object[] o = null;
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalClass.goal b/annotation-file-utilities/tests/LocalClass.goal
new file mode 100644
index 0000000..083a883
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalClass.goal
@@ -0,0 +1,66 @@
+public class LocalClass {
+
+    @A(1) Object f;
+
+    void m() {
+        class InnerLocalClass {
+            @A(2) Object f;
+        }
+        new Object() {
+            @A(3) Object f;
+
+            class Test{
+                @A(4) Object f;
+
+                void m() {
+                    new Object() {
+                        @A(41) Object f;
+                    };
+                    new Object() {
+                        @A(42) Object f;
+                    };
+                }
+            }
+        };
+        new Object() {
+            @A(31) Object f;
+        };
+    }
+
+    void m2() {
+        class InnerLocalClass {
+            @A(5) Object f;
+
+            class Inner {
+                @A(6) Object f;
+
+                void m() {
+                    new Object() {
+                        @A(7) Object f;
+                    };
+                    new Object() {
+                        @A(71) Object f;
+                    };
+                }
+            }
+
+            void m() {
+                class OuterLocalClass {
+                    @A(10) Object f;
+                }
+            }
+        }
+    }
+
+    void m3() {
+        class OuterLocalClass {
+            @A(8) Object f;
+
+            void m() {
+                class InnerLocalClass {
+                    @A(9) Object f;
+                }
+            }
+        }
+    }
+}
diff --git a/annotation-file-utilities/tests/LocalClass.jaif b/annotation-file-utilities/tests/LocalClass.jaif
new file mode 100644
index 0000000..80b54d9
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalClass.jaif
@@ -0,0 +1,59 @@
+package:
+    annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+
+    class LocalClass:
+        field f:
+            type: @A(1)
+
+    class LocalClass$1InnerLocalClass:
+        field f:
+            type: @A(2)
+
+    class LocalClass$1:
+        field f:
+            type: @A(3)
+
+    class LocalClass$2:
+        field f:
+            type: @A(31)
+
+    class LocalClass$1$Test:
+        field f:
+            type: @A(4)
+
+    class LocalClass$1$Test$1:
+        field f:
+            type: @A(41)
+
+    class LocalClass$1$Test$2:
+        field f:
+            type: @A(42)
+
+    class LocalClass$2InnerLocalClass:
+        field f:
+            type: @A(5)
+
+    class LocalClass$2InnerLocalClass$Inner:
+        field f:
+            type: @A(6)
+
+    class LocalClass$2InnerLocalClass$Inner$1:
+        field f:
+            type: @A(7)
+
+    class LocalClass$2InnerLocalClass$Inner$2:
+        field f:
+            type: @A(71)
+
+    class LocalClass$2InnerLocalClass$1OuterLocalClass:
+        field f:
+            type: @A(10)
+
+    class LocalClass$1OuterLocalClass:
+        field f:
+            type: @A(8)
+
+    class LocalClass$1OuterLocalClass$1InnerLocalClass:
+        field f:
+            type: @A(9)
diff --git a/annotation-file-utilities/tests/LocalClass.java b/annotation-file-utilities/tests/LocalClass.java
new file mode 100644
index 0000000..ccb7483
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalClass.java
@@ -0,0 +1,66 @@
+public class LocalClass {
+
+    Object f;
+
+    void m() {
+        class InnerLocalClass {
+            Object f;
+        }
+        new Object() {
+            Object f;
+
+            class Test{
+                Object f;
+
+                void m() {
+                    new Object() {
+                        Object f;
+                    };
+                    new Object() {
+                        Object f;
+                    };
+                }
+            }
+        };
+        new Object() {
+            Object f;
+        };
+    }
+
+    void m2() {
+        class InnerLocalClass {
+            Object f;
+
+            class Inner {
+                Object f;
+
+                void m() {
+                    new Object() {
+                        Object f;
+                    };
+                    new Object() {
+                        Object f;
+                    };
+                }
+            }
+
+            void m() {
+                class OuterLocalClass {
+                    Object f;
+                }
+            }
+        }
+    }
+
+    void m3() {
+        class OuterLocalClass {
+            Object f;
+
+            void m() {
+                class InnerLocalClass {
+                    Object f;
+                }
+            }
+        }
+    }
+}
diff --git a/annotation-file-utilities/tests/LocalGeneric.goal b/annotation-file-utilities/tests/LocalGeneric.goal
new file mode 100644
index 0000000..a09fe9f
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalGeneric.goal
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class LocalGeneric {
+  public void foo() {
+    @java.lang.UnderInitialization List<@java.lang.Tainted Integer> var = null;
+    System.out.println(var);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalGeneric.jaif b/annotation-file-utilities/tests/LocalGeneric.jaif
new file mode 100644
index 0000000..a09efeb
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalGeneric.jaif
@@ -0,0 +1,16 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalGeneric:
+
+    method <init>()V:
+
+    method foo()V:
+        local 1 #2+8:
+            type: @java.lang.UnderInitialization
+                inner-type 3, 0: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/LocalGeneric.java b/annotation-file-utilities/tests/LocalGeneric.java
new file mode 100644
index 0000000..a6fc891
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalGeneric.java
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class LocalGeneric {
+  public void foo() {
+    List<Integer> var = null;
+    System.out.println(var);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalGenericShadow.goal b/annotation-file-utilities/tests/LocalGenericShadow.goal
new file mode 100644
index 0000000..a039ac5
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalGenericShadow.goal
@@ -0,0 +1,13 @@
+package annotator.tests;
+
+import java.util.List;
+
+
+public class LocalGenericShadow {
+  public List<String> foo = null;
+
+  public void method() {
+    @java.lang.UnderInitialization List<Integer> foo = null;
+    System.out.println(foo);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalGenericShadow.jaif b/annotation-file-utilities/tests/LocalGenericShadow.jaif
new file mode 100644
index 0000000..0ce82b9
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalGenericShadow.jaif
@@ -0,0 +1,14 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalGenericShadow:
+
+    field foo:
+
+    method <init>()V:
+
+    method method()V:
+        local 1 #2+8:
+            type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/LocalGenericShadow.java b/annotation-file-utilities/tests/LocalGenericShadow.java
new file mode 100644
index 0000000..8c6161f
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalGenericShadow.java
@@ -0,0 +1,13 @@
+package annotator.tests;
+
+import java.util.List;
+
+
+public class LocalGenericShadow {
+  public List<String> foo = null;
+
+  public void method() {
+    List<Integer> foo = null;
+    System.out.println(foo);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalMultiple.goal b/annotation-file-utilities/tests/LocalMultiple.goal
new file mode 100644
index 0000000..95e0c65
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultiple.goal
@@ -0,0 +1,20 @@
+package annotator.tests;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class LocalMultiple {
+  public void foo(Object o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      /* @UnderInitialization*/ @java.lang.UnderInitialization Set localVar = null;
+      myList.add(localVar);
+    } else {
+      /* @Tainted*/ @java.lang.Tainted Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalMultiple.jaif b/annotation-file-utilities/tests/LocalMultiple.jaif
new file mode 100644
index 0000000..a94da14
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultiple.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalMultiple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        local 3 #13+8:
+            type: @java.lang.UnderInitialization
+        local 3 #26+8:
+            type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/LocalMultiple.java b/annotation-file-utilities/tests/LocalMultiple.java
new file mode 100644
index 0000000..3b8f205
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultiple.java
@@ -0,0 +1,20 @@
+package annotator.tests;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class LocalMultiple {
+  public void foo(Object o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      /* @UnderInitialization*/ Set localVar = null;
+      myList.add(localVar);
+    } else {
+      /* @Tainted*/ Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethods.goal b/annotation-file-utilities/tests/LocalMultipleManyMethods.goal
new file mode 100644
index 0000000..4689fd9
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultipleManyMethods.goal
@@ -0,0 +1,34 @@
+package annotator.tests;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class LocalMultipleManyMethods {
+  public void foo(Object o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      @java.lang.C Set localVar = null;
+      myList.add(localVar);
+    } else {
+      @java.lang.B Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+
+  public void foo(Object[] o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      @java.lang.A Set localVar = null;
+      myList.add(localVar);
+    } else {
+      @java.lang.D Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+}
+
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethods.jaif b/annotation-file-utilities/tests/LocalMultipleManyMethods.jaif
new file mode 100644
index 0000000..89b45ea
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultipleManyMethods.jaif
@@ -0,0 +1,29 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalMultipleManyMethods:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        local 3 #13+8:
+            type: @java.lang.C
+        local 3 #26+8:
+            type: @java.lang.B
+
+    method foo([Ljava/lang/Object;)V:
+        local 3 #13+8:
+            type: @java.lang.A
+        local 3 #26+8:
+            type: @java.lang.D
+
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethods.java b/annotation-file-utilities/tests/LocalMultipleManyMethods.java
new file mode 100644
index 0000000..d643fa6
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultipleManyMethods.java
@@ -0,0 +1,34 @@
+package annotator.tests;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class LocalMultipleManyMethods {
+  public void foo(Object o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      Set localVar = null;
+      myList.add(localVar);
+    } else {
+      Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+
+  public void foo(Object[] o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      Set localVar = null;
+      myList.add(localVar);
+    } else {
+      Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+}
+
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.goal b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.goal
new file mode 100644
index 0000000..404556a
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.goal
@@ -0,0 +1,38 @@
+package annotator.tests;
+
+import java.util.List;
+import java.util.Set;
+
+public class LocalMultipleManyMethodsShifted {
+
+  public void foo(Object o) {
+    List myList = null;
+
+    myList.add(myList);
+    myList.remove(myList);
+
+    if (myList.size() != 0) {
+      @java.lang.C Set localVar = null;
+      foo(localVar);
+      System.out.println(localVar);
+      myList.add(localVar);
+    } else {
+      Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+
+  public void foo(Object[] o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      @java.lang.D Set localVar = null;
+      myList.add(localVar);
+    } else {
+      @java.lang.A Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.jaif b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.jaif
new file mode 100644
index 0000000..a0d2249
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.jaif
@@ -0,0 +1,28 @@
+package java.lang:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+
+package annotator.tests:
+class LocalMultipleManyMethodsShifted:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        local 3 #29+20:
+            type: @java.lang.C
+        local 3 #54+8:
+
+    method foo([Ljava/lang/Object;)V:
+        local 3 #13+8:
+            type: @java.lang.D
+        local 3 #26+8:
+            type: @java.lang.A
diff --git a/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.java b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.java
new file mode 100644
index 0000000..7b5cb5e
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalMultipleManyMethodsShifted.java
@@ -0,0 +1,38 @@
+package annotator.tests;
+
+import java.util.List;
+import java.util.Set;
+
+public class LocalMultipleManyMethodsShifted {
+
+  public void foo(Object o) {
+    List myList = null;
+
+    myList.add(myList);
+    myList.remove(myList);
+
+    if (myList.size() != 0) {
+      Set localVar = null;
+      foo(localVar);
+      System.out.println(localVar);
+      myList.add(localVar);
+    } else {
+      Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+
+  public void foo(Object[] o) {
+    List myList = null;
+
+    if (myList.size() != 0) {
+      Set localVar = null;
+      myList.add(localVar);
+    } else {
+      Set localVar = null;
+      myList.add(localVar);
+    }
+    foo(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalSimple.goal b/annotation-file-utilities/tests/LocalSimple.goal
new file mode 100644
index 0000000..9260ad3
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalSimple.goal
@@ -0,0 +1,8 @@
+package annotator.tests;
+
+public class LocalSimple {
+  public void foo() {
+    @java.lang.Tainted Object o = null;
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalSimple.jaif b/annotation-file-utilities/tests/LocalSimple.jaif
new file mode 100644
index 0000000..7d112d4
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalSimple.jaif
@@ -0,0 +1,12 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalSimple:
+
+    method <init>()V:
+
+    method foo()V:
+        local 1 #2+8:
+            type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/LocalSimple.java b/annotation-file-utilities/tests/LocalSimple.java
new file mode 100644
index 0000000..90fefe2
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalSimple.java
@@ -0,0 +1,8 @@
+package annotator.tests;
+
+public class LocalSimple {
+  public void foo() {
+    Object o = null;
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalSimpleMultiple.goal b/annotation-file-utilities/tests/LocalSimpleMultiple.goal
new file mode 100644
index 0000000..69208fb
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalSimpleMultiple.goal
@@ -0,0 +1,18 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class LocalSimpleMultiple {
+  public void foo() {
+    @java.lang.UnderInitialization Object o = null;
+    System.out.println(o);
+    @java.lang.Tainted List list = null;
+    bar(list);
+    bar(o);
+  }
+
+  public void bar(Object o) {
+    @java.lang.Tainted LocalSimpleMultiple second = null;
+    bar(second);
+  }
+}
diff --git a/annotation-file-utilities/tests/LocalSimpleMultiple.jaif b/annotation-file-utilities/tests/LocalSimpleMultiple.jaif
new file mode 100644
index 0000000..49d04ce
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalSimpleMultiple.jaif
@@ -0,0 +1,21 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalSimpleMultiple:
+
+    method <init>()V:
+
+    method foo()V:
+        local 1 #2+20:
+            type: @java.lang.UnderInitialization
+        local 2 #11+11:
+            type: @java.lang.Tainted
+
+    method bar(Ljava/lang/Object;)V:
+        local 2 #2+6:
+            type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/LocalSimpleMultiple.java b/annotation-file-utilities/tests/LocalSimpleMultiple.java
new file mode 100644
index 0000000..ffb5fe7
--- /dev/null
+++ b/annotation-file-utilities/tests/LocalSimpleMultiple.java
@@ -0,0 +1,18 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class LocalSimpleMultiple {
+  public void foo() {
+    Object o = null;
+    System.out.println(o);
+    List list = null;
+    bar(list);
+    bar(o);
+  }
+
+  public void bar(Object o) {
+    LocalSimpleMultiple second = null;
+    bar(second);
+  }
+}
diff --git a/annotation-file-utilities/tests/Makefile b/annotation-file-utilities/tests/Makefile
new file mode 100644
index 0000000..80ba9de
--- /dev/null
+++ b/annotation-file-utilities/tests/Makefile
@@ -0,0 +1,104 @@
+# Very rough testing framework for the annotator.  Running 'make all' will
+# look for all myClass.goal files in this directory, run the annotator on the
+# corresponding .jaif and .java files, and then output the difference in a
+# myClass.diff file in this directory.
+#
+# To test just one file, use (for example) 'make myClass.diff'.
+
+# Put user-specific changes in your own Makefile.user.
+# Make will silently continue if that file does not exist.
+-include Makefile.user
+
+# Override these in Makefile.user if the java and javac commands are not on
+# your execution path.  Example from Makefile.user:
+#   JAVA=${JAVA_HOME}/bin/java
+#   JAVAC=${JAVA_HOME}/bin/javac
+export JAVA?=java -ea
+export JAVAC?=javac
+export XJAVAC?=javac
+
+export SHELL=/bin/bash -o pipefail
+
+
+DIFFS := $(wildcard *.goal)
+DISABLED := $(shell grep -le "@skip-test" $(DIFFS))
+FILTERED := $(filter-out $(DISABLED),$(DIFFS))
+DIFFS := $(patsubst %.goal, %.diff, $(FILTERED))
+
+DEBUG :=
+# Use this to enable some debugging.
+# DEBUG := --debug
+
+default : all
+
+.PHONY: all
+all : $(DIFFS) abbreviated ad-hoc system-test source-extension results
+
+.PHONY: abbreviated
+abbreviated:
+	${MAKE} -C abbreviated
+
+.PHONY: ad-hoc
+ad-hoc:
+	${MAKE} -C ad-hoc
+
+.PHONY: source-extension
+source-extension:
+	${MAKE} -C source-extension
+
+.PHONY: system-test
+system-test:
+	${MAKE} -C system-test
+
+# Display results of all .diff files.
+.PHONY: results
+results: bin/VerifyDiffs.class
+	@echo ""
+	@echo "=== RESULTS ==="
+	@echo ""
+	@$(JAVA) -cp bin VerifyDiffs --show_all
+
+# Remakes the little java program that checks and compares diffs
+bin/VerifyDiffs.class : VerifyDiffs.java
+	@$(JAVAC) -g -cp ../bincompile -d bin VerifyDiffs.java
+
+# Compiles all the test cases (be verbose about this).
+compile :
+	mkdir -p bin
+	$(XJAVAC) -g -cp ../bin -d bin *.java
+
+.PRECIOUS : bin/annotator/tests/%.class
+bin/annotator/tests/%.class: %.java
+	mkdir -p bin
+# Added "-Xlint:-options" to see if it permits Jenkins job to succeed, due to
+# problem "target value 1.8 is obsolete and will be removed in a future release"
+	$(XJAVAC) -Xlint:-options -g -cp bin:../annotation-file-utilities.jar -d bin -sourcepath . $*.java
+
+# Actually runs the annotator to create the annotated java file.
+.PRECIOUS: %.output
+%.output: %.jaif %.java bin/annotator/tests/%.class ../lib/plume-core.jar ../bin ../annotation-file-utilities.jar
+	$(JAVA) \
+	-cp ../bin:../annotation-file-utilities.jar:bin \
+	annotator.Main \
+	${DEBUG} \
+	--abbreviate=false \
+	-d $*-output \
+	$*.jaif \
+	$*.java \
+	2>&1 | tee $*.log
+	find "$*-output" -name '*.java' -print | xargs cat > "$*.output"
+	rm -rf $*-output
+
+# Compare the output of the annotator and the goal file.
+%.diff: %.goal %.output
+	-diff -u $*.goal $*.output >& $*.diff
+
+# Remove all .diff, .log files from the tests directory.
+.PHONY: clean
+clean :
+	rm -rf bin
+	rm -f *.diff
+	rm -f *.log
+	rm -f *.output
+	(cd abbreviated && make clean)
+	(cd ad-hoc && make clean)
diff --git a/annotation-file-utilities/tests/MatchReturnValue.goal b/annotation-file-utilities/tests/MatchReturnValue.goal
new file mode 100644
index 0000000..372f553
--- /dev/null
+++ b/annotation-file-utilities/tests/MatchReturnValue.goal
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+public class MatchReturnValue<T> {
+
+  @java.lang.A
+  public MatchReturnValue<T> clone() {
+    return this;
+  }
+
+}
diff --git a/annotation-file-utilities/tests/MatchReturnValue.jaif b/annotation-file-utilities/tests/MatchReturnValue.jaif
new file mode 100644
index 0000000..04df367
--- /dev/null
+++ b/annotation-file-utilities/tests/MatchReturnValue.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @A:
+
+annotation @ShouldNotAppearInOutput:
+
+package annotator.tests:
+class MatchReturnValue:
+
+    method clone()Lannotator.tests.MatchReturnValue;: @A
+    method clone()Ljava.lang.Object;: @ShouldNotAppearInOutput
diff --git a/annotation-file-utilities/tests/MatchReturnValue.java b/annotation-file-utilities/tests/MatchReturnValue.java
new file mode 100644
index 0000000..968de0c
--- /dev/null
+++ b/annotation-file-utilities/tests/MatchReturnValue.java
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+public class MatchReturnValue<T> {
+
+  public MatchReturnValue<T> clone() {
+    return this;
+  }
+
+}
diff --git a/annotation-file-utilities/tests/MemberReference.goal b/annotation-file-utilities/tests/MemberReference.goal
new file mode 100644
index 0000000..a28db8b
--- /dev/null
+++ b/annotation-file-utilities/tests/MemberReference.goal
@@ -0,0 +1,101 @@
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+//import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+import java.util.function.ToIntFunction;
+import java.util.function.UnaryOperator;
+
+@Target(ElementType.TYPE_USE) @interface A {}
+@Target(ElementType.TYPE_USE) @interface B {}
+@Target(ElementType.TYPE_USE) @interface C {}
+@Target(ElementType.TYPE_USE) @interface D {}
+
+@skip-test
+public class MemberReference<T> {
+  class Inner<U> implements Map.Entry<T, U> {
+    @Override
+    public T getKey() { return null; }
+    @Override
+    public U getValue() { return null; }
+    @Override
+    public U setValue(U u) { throw new UnsupportedOperationException(); }
+  }
+
+  boolean test = false;
+  List list = new ArrayList<T>();
+  Collection<T>[] foo;
+
+  <U extends T> MemberReference(U bar) {
+    foo = new ArrayList[1];
+    foo[0] = new ArrayList<T>(1);
+    foo[0].add(bar);
+  }
+
+  // instance method
+  ToIntFunction<String> o1 = @A String::length;
+
+  // static method
+  LongSupplier o2 = @A System::currentTimeMillis;
+
+  // explicit type args
+  ToIntFunction<List<String>> o3 = @A List<@B String>::size;
+
+  // inferred type args
+  ToIntFunction<List<String>> o4 = @A List::size;
+
+  UnaryOperator<Integer[]> o5 = @A Integer @B []::clone;
+
+  Function<T, String> o6 = @A T::toString;
+
+  //IntSupplier o7 = ((String) "abc")::length;
+  //IntSupplier o8 = ((List<String>) foo[0])::size;
+
+  Supplier<Iterator<T>> o9 =
+      (test ? list : Collections.<@A T>emptyList()) :: iterator;
+
+  // constructor for parameterized type
+  Supplier<ArrayList<String>> o10 =
+      @A ArrayList<@B String>::new;
+
+  // inferred type arguments for generic class
+  Supplier<ArrayList<String>> o11 =
+      @A ArrayList::new;
+
+  // explicit type arguments for generic constructor
+  IntFunction<List<Integer>> o12 =
+      @A ArrayList<@B Integer>::<@C Integer>new;
+
+  // generic class + constructor
+  Function<String, MemberReference<CharSequence>> o13 =
+      @A MemberReference<@B CharSequence>::<@C String>new;
+
+  // inner class constructor
+  Supplier<MemberReference<Object>.Inner<Long>> o14 =
+      @A MemberReference<@B Object>.Inner<@C Long>::<@D String>new;
+
+  // array creation
+  Function<Integer, Integer[]> o15 =
+      @A Integer @B []::new;
+
+  //Consumer<String> o16 = System.out::println;
+  //Supplier<String> o17 = ((Object) super)::toString;
+  //Supplier<String> o18 = ((Object) MemberReference.super)::toString;
+
+  public static void main(String[] args) {
+    String[] ss = {"a", "b"};
+    System.out.println(java.util.Arrays.asList(ss).stream()
+        .map(@A String::toUpperCase)
+        .collect(java.util.stream.Collectors.joining(" ")));
+  }
+}
+
diff --git a/annotation-file-utilities/tests/MemberReference.jaif b/annotation-file-utilities/tests/MemberReference.jaif
new file mode 100644
index 0000000..ea9ae4a
--- /dev/null
+++ b/annotation-file-utilities/tests/MemberReference.jaif
@@ -0,0 +1,62 @@
+package :
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+annotation @D: @java.lang.annotation.Target(value={TYPE_USE})
+
+class MemberReference:
+
+    field o1:
+        reference *0: @A
+
+    field o2:
+        reference *0: @A
+
+    field o3:
+        reference *0: @A
+            inner-type 3, 0: @B
+
+    field o4:
+        reference *0: @A
+
+    field o5:
+        reference *0: @B
+            inner-type 0, 0: @A
+
+    field o6:
+        reference *0: @A
+
+    field o9:
+        call *0:
+            typearg 0: @A
+
+    field o10:
+        reference *0: @A
+            inner-type 3, 0: @B
+
+    field o11:
+        reference *0: @A
+
+    field o12:
+        reference *0: @A
+            inner-type 3, 0: @B
+            typearg 0: @C
+
+    field o13:
+        reference *0: @A
+            inner-type 3, 0: @B
+            typearg 0: @C
+
+    field o14:
+        reference *0: @A
+            inner-type 3, 0: @B
+            inner-type 1, 0, 3, 0: @C
+            typearg 0: @D
+
+    field o15:
+        reference *0: @B
+            inner-type 0, 0: @A
+
+    method main([Ljava/lang/String;)V:
+        reference #27: @A
+
diff --git a/annotation-file-utilities/tests/MemberReference.java b/annotation-file-utilities/tests/MemberReference.java
new file mode 100644
index 0000000..23b0f22
--- /dev/null
+++ b/annotation-file-utilities/tests/MemberReference.java
@@ -0,0 +1,98 @@
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.function.IntSupplier;
+import java.util.function.LongSupplier;
+import java.util.function.Supplier;
+import java.util.function.ToIntFunction;
+import java.util.function.UnaryOperator;
+
+@Target(ElementType.TYPE_USE) @interface A {}
+@Target(ElementType.TYPE_USE) @interface B {}
+@Target(ElementType.TYPE_USE) @interface C {}
+@Target(ElementType.TYPE_USE) @interface D {}
+
+public class MemberReference<T> {
+  class Inner<U> implements Map.Entry<T, U> {
+    @Override
+    public T getKey() { return null; }
+    @Override
+    public U getValue() { return null; }
+    @Override
+    public U setValue(U u) { throw new UnsupportedOperationException(); }
+  }
+
+  boolean test = false;
+  List list = new ArrayList<T>();
+  Collection<T>[] foo;
+
+  <U extends T> MemberReference(U bar) {
+    foo = new ArrayList[1];
+    foo[0] = new ArrayList<T>(1);
+    foo[0].add(bar);
+  }
+
+  // instance method
+  ToIntFunction<String> o1 = String::length;
+
+  // static method
+  LongSupplier o2 = System::currentTimeMillis;
+
+  // explicit type args
+  ToIntFunction<List<String>> o3 = List<String>::size;
+
+  // inferred type args
+  ToIntFunction<List<String>> o4 = List::size;
+
+  UnaryOperator<Integer[]> o5 = Integer[]::clone;
+
+  Function<T, String> o6 = T::toString;
+
+  //IntSupplier o7 = ((String) "abc")::length;
+  //IntSupplier o8 = ((List<String>) foo[0])::size;
+
+  Supplier<Iterator<T>> o9 =
+      (test ? list : Collections.<T>emptyList()) :: iterator;
+
+  // constructor for parameterized type
+  Supplier<ArrayList<String>> o10 =
+      ArrayList<String>::new;
+
+  // inferred type arguments for generic class
+  Supplier<ArrayList<String>> o11 =
+      ArrayList::new;
+
+  // explicit type arguments for generic constructor
+  IntFunction<List<Integer>> o12 =
+      ArrayList<Integer>::<Integer>new;
+
+  // generic class + constructor
+  Function<String, MemberReference<CharSequence>> o13 =
+      MemberReference<CharSequence>::<String>new;
+
+  // inner class constructor
+  Supplier<MemberReference<Object>.Inner<Long>> o14 =
+      MemberReference<Object>.Inner<Long>::<String>new;
+
+  // array creation
+  Function<Integer, Integer[]> o15 =
+      Integer[]::new;
+
+  //Consumer<String> o16 = System.out::println;
+  //Supplier<String> o17 = ((Object) super)::toString;
+  //Supplier<String> o18 = ((Object) MemberReference.super)::toString;
+
+  public static void main(String[] args) {
+    String[] ss = {"a", "b"};
+    System.out.println(java.util.Arrays.asList(ss).stream()
+        .map(String::toUpperCase)
+        .collect(java.util.stream.Collectors.joining(" ")));
+  }
+}
diff --git a/annotation-file-utilities/tests/MemberSelectTypes.goal b/annotation-file-utilities/tests/MemberSelectTypes.goal
new file mode 100644
index 0000000..9fd7b6d
--- /dev/null
+++ b/annotation-file-utilities/tests/MemberSelectTypes.goal
@@ -0,0 +1,37 @@
+import java.util.Map;
+
+public class MemberSelectTypes<T extends java.lang.@Anno(0) Object & java.lang.@Anno(1) Comparable>
+        extends java.lang.@Anno(2) Object implements java.io.@Anno(3) Serializable {
+
+    class Inner {
+        void m(@A MemberSelectTypes<T>.Inner this) {}
+    }
+
+    static class StaticInner {
+        void m(MemberSelectTypes.@B StaticInner this) {}
+    }
+
+    java.lang.@C Object o;
+    java.util.Map.@D Entry<java.lang.@E String, java.util.Map.@F Entry<Object, Object>> m1;
+    Map.@G Entry<java.lang.@H String, Map.@I Entry<Object, Object>> m2;
+    Map<@H MemberSelectTypes.Inner, MemberSelectTypes.@I StaticInner> m3;
+    Map.@G Entry<Map.@H Entry<Map.@I Entry<Map.@K Entry<Object, Object>, Object>, Object>, Object> m4;
+    @J MemberSelectTypes.Inner i;
+    MemberSelectTypes.@K StaticInner s;
+
+    java.lang.@L Object m(java.lang.@M Object o, @M MemberSelectTypes.Inner i, MemberSelectTypes.@M StaticInner s) {
+        java.lang.@N Object o2 = (java.lang.@O Object) o;
+        @N MemberSelectTypes.Inner i2 = (@O MemberSelectTypes.Inner) i;
+        MemberSelectTypes.@N StaticInner s2 = (MemberSelectTypes.@O StaticInner) s;
+        o2 = new java.lang.@P Object();
+        i2 = new @P MemberSelectTypes.Inner();
+        s2 = new MemberSelectTypes.@P StaticInner();
+        java.lang.@Q Object[] os = new java.lang.@R Object[1];
+        @Q MemberSelectTypes.Inner[] is = new @R MemberSelectTypes.Inner[1];
+        MemberSelectTypes.@Q StaticInner[] ss = new MemberSelectTypes.@R StaticInner[1];
+        boolean b = o instanceof java.lang.@S Object;
+        b = o instanceof @S MemberSelectTypes.Inner;
+        b = o instanceof MemberSelectTypes.@S StaticInner;
+        return o2;
+    }
+}
diff --git a/annotation-file-utilities/tests/MemberSelectTypes.jaif b/annotation-file-utilities/tests/MemberSelectTypes.jaif
new file mode 100644
index 0000000..9021bba
--- /dev/null
+++ b/annotation-file-utilities/tests/MemberSelectTypes.jaif
@@ -0,0 +1,101 @@
+package:
+    annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @D: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @E: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @F: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @G: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @H: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @I: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @J: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @K: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @L: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @M: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @N: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @O: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @P: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @Q: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @R: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @S: @java.lang.annotation.Target(value={TYPE_USE})
+    annotation @Anno: @java.lang.annotation.Target(value={TYPE_USE})
+        int value
+
+    class MemberSelectTypes$Inner:
+        method m()V:
+            receiver: @A
+
+    class MemberSelectTypes$StaticInner:
+        method m()V:
+            receiver: @B
+
+    class MemberSelectTypes:
+        bound 0 & 0: @Anno(0)
+        bound 0 & 1: @Anno(1)
+        extends: @Anno(2)
+        implements 0: @Anno(3)
+        field o:
+            type: @C
+        field m1:
+            type: @D
+                inner-type 3, 0: @E
+                inner-type 3, 1: @F
+        field m2:
+            type: @G
+                inner-type 3, 0: @H
+                inner-type 3, 1: @I
+
+        field m3:
+            type:
+                inner-type 3, 0: @H
+                inner-type 3, 1: @I
+
+        field m4:
+            type: @G
+                inner-type 3, 0: @H
+                inner-type 3, 0, 3, 0: @I
+                inner-type 3, 0, 3, 0, 3, 0: @K
+
+        field i:
+            type: @J
+        field s:
+            type: @K
+
+        method m(Ljava/lang/Object;LMemberSelectTypes$Inner;LMemberSelectTypes$StaticInner;)Ljava/lang/Object;:
+            return: @L
+            parameter 0:
+                type: @M
+            parameter 1:
+                type: @M
+            parameter 2:
+                type: @M
+            local o2:
+                type: @N
+            local i2:
+                type: @N
+            local s2:
+                type: @N
+            typecast *0: @O
+            typecast *1: @O
+            typecast *2: @O
+            new *0: @P
+            new *1: @P
+            new *2: @P
+            local os:
+                type:
+                    inner-type 0, 0: @Q
+            local is:
+                type:
+                    inner-type 0, 0: @Q
+            local ss:
+                type:
+                    inner-type 0, 0: @Q
+            new *3:
+                inner-type 0, 0: @R
+            new *4:
+                inner-type 0, 0: @R
+            new *5:
+                inner-type 0, 0: @R
+            instanceof *0: @S
+            instanceof *1: @S
+            instanceof *2: @S
diff --git a/annotation-file-utilities/tests/MemberSelectTypes.java b/annotation-file-utilities/tests/MemberSelectTypes.java
new file mode 100644
index 0000000..532dc33
--- /dev/null
+++ b/annotation-file-utilities/tests/MemberSelectTypes.java
@@ -0,0 +1,37 @@
+import java.util.Map;
+
+public class MemberSelectTypes<T extends java.lang.Object & java.lang.Comparable>
+        extends java.lang.Object implements java.io.Serializable {
+
+    class Inner {
+        void m(MemberSelectTypes<T>.Inner this) {}
+    }
+
+    static class StaticInner {
+        void m(MemberSelectTypes.StaticInner this) {}
+    }
+
+    java.lang.Object o;
+    java.util.Map.Entry<java.lang.String, java.util.Map.Entry<Object, Object>> m1;
+    Map.Entry<java.lang.String, Map.Entry<Object, Object>> m2;
+    Map<MemberSelectTypes.Inner, MemberSelectTypes.StaticInner> m3;
+    Map.Entry<Map.Entry<Map.Entry<Map.Entry<Object, Object>, Object>, Object>, Object> m4;
+    MemberSelectTypes.Inner i;
+    MemberSelectTypes.StaticInner s;
+
+    java.lang.Object m(java.lang.Object o, MemberSelectTypes.Inner i, MemberSelectTypes.StaticInner s) {
+        java.lang.Object o2 = (java.lang.Object) o;
+        MemberSelectTypes.Inner i2 = (MemberSelectTypes.Inner) i;
+        MemberSelectTypes.StaticInner s2 = (MemberSelectTypes.StaticInner) s;
+        o2 = new java.lang.Object();
+        i2 = new MemberSelectTypes.Inner();
+        s2 = new MemberSelectTypes.StaticInner();
+        java.lang.Object[] os = new java.lang.Object[1];
+        MemberSelectTypes.Inner[] is = new MemberSelectTypes.Inner[1];
+        MemberSelectTypes.StaticInner[] ss = new MemberSelectTypes.StaticInner[1];
+        boolean b = o instanceof java.lang.Object;
+        b = o instanceof MemberSelectTypes.Inner;
+        b = o instanceof MemberSelectTypes.StaticInner;
+        return o2;
+    }
+}
diff --git a/annotation-file-utilities/tests/MethodCompoundType.goal b/annotation-file-utilities/tests/MethodCompoundType.goal
new file mode 100644
index 0000000..bbb830e
--- /dev/null
+++ b/annotation-file-utilities/tests/MethodCompoundType.goal
@@ -0,0 +1,7 @@
+import java.util.Map;
+
+public class MethodCompoundType {
+    public Map @A [] m() {
+        return null;
+    }
+}
diff --git a/annotation-file-utilities/tests/MethodCompoundType.jaif b/annotation-file-utilities/tests/MethodCompoundType.jaif
new file mode 100644
index 0000000..c8eea69
--- /dev/null
+++ b/annotation-file-utilities/tests/MethodCompoundType.jaif
@@ -0,0 +1,6 @@
+package:
+    annotation @A: @Target(TYPE_USE)
+
+    class MethodCompoundType:
+        method m()[Ljava/util/Map;:
+            return: @A
diff --git a/annotation-file-utilities/tests/MethodCompoundType.java b/annotation-file-utilities/tests/MethodCompoundType.java
new file mode 100644
index 0000000..227d9d4
--- /dev/null
+++ b/annotation-file-utilities/tests/MethodCompoundType.java
@@ -0,0 +1,7 @@
+import java.util.Map;
+
+public class MethodCompoundType {
+    public Map[] m() {
+        return null;
+    }
+}
diff --git a/annotation-file-utilities/tests/MethodMultiple.goal b/annotation-file-utilities/tests/MethodMultiple.goal
new file mode 100644
index 0000000..9832c84
--- /dev/null
+++ b/annotation-file-utilities/tests/MethodMultiple.goal
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+public class MethodMultiple {
+  public String foo() {
+    return null;
+  }
+
+  public @java.lang.UnderInitialization String foo(String s) {
+    return null;
+  }
+}
diff --git a/annotation-file-utilities/tests/MethodMultiple.jaif b/annotation-file-utilities/tests/MethodMultiple.jaif
new file mode 100644
index 0000000..34bbd89
--- /dev/null
+++ b/annotation-file-utilities/tests/MethodMultiple.jaif
@@ -0,0 +1,13 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class MethodMultiple:
+
+    method <init>()V:
+
+    method foo()Ljava/lang/String;:
+
+    method foo(Ljava/lang/String;)Ljava/lang/String;:
+        return: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/MethodMultiple.java b/annotation-file-utilities/tests/MethodMultiple.java
new file mode 100644
index 0000000..c717aea
--- /dev/null
+++ b/annotation-file-utilities/tests/MethodMultiple.java
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+public class MethodMultiple {
+  public String foo() {
+    return null;
+  }
+
+  public String foo(String s) {
+    return null;
+  }
+}
diff --git a/annotation-file-utilities/tests/NewArray.goal b/annotation-file-utilities/tests/NewArray.goal
new file mode 100644
index 0000000..f7ffdf1
--- /dev/null
+++ b/annotation-file-utilities/tests/NewArray.goal
@@ -0,0 +1,33 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class NewArray {
+     private static final int MAX_HOOKS = 10;
+     private static final Runnable[] hooks = new @Nullable Runnable[MAX_HOOKS];
+
+     String[] names01 = new @X String[12];
+
+     String[] names02 = new @X String[] { "Alice", "Bob"};
+
+     String[] names03 = new @X String[] { "Alice", "Bob"};
+
+     static final int[] table1 = new @A int @B [] {0, 1};
+     static final int table2[] = new @A int @B [] {0, 1};
+
+     String[][][][][] names0 = new String @Z [11][12][13][14][15];
+     String[][][][][] names1 = new String[11] @A [12][13][14][15];
+     String[][][][][] names2 = new String[11][12] @B [13][14][15];
+     String[][][][][] names3 = new String[11][12][13] @C [14][15];
+     String[][][][][] names4 = new String[11][12][13][14] @D [15];
+     String[][][][][] names5 = new @E String[11][12][13][14][15];
+
+     Object names10 = new String @Z [][][][][] { { { }}};
+     Object names11 = new String[] @A [][][][] { { { }}};
+     Object names12 = new String[][] @B [][][] { { { }}};
+     Object names13 = new String[][][] @C [][] { { { }}};
+     Object names14 = new String[][][][] @D [] { { { }}};
+     Object names15 = new @E String[][][][][] { { { }}};
+
+     @Z
+     List<@A ?> @B [] lists = new @C ArrayList<@D ?> @E [2];
+}
diff --git a/annotation-file-utilities/tests/NewArray.jaif b/annotation-file-utilities/tests/NewArray.jaif
new file mode 100644
index 0000000..56befcd
--- /dev/null
+++ b/annotation-file-utilities/tests/NewArray.jaif
@@ -0,0 +1,80 @@
+package:
+annotation @Z:
+annotation @A:
+annotation @B:
+annotation @C:
+annotation @D:
+annotation @E:
+annotation @X:
+annotation @Nullable: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME)
+
+class NewArray:
+
+field names01:
+new *0:
+inner-type 0, 0: @X
+
+field names02:
+new *0:
+inner-type 0, 0: @X
+
+field names03:
+new *0:
+inner-type 0, 0: @X
+
+field table1:
+new *0: @B
+inner-type 0, 0: @A
+
+field table2:
+new *0: @B
+inner-type 0, 0: @A
+
+field names0:
+new *0: @Z
+field names1:
+new *0:
+inner-type 0, 0: @A
+field names2:
+new *0:
+inner-type 0, 0, 0, 0: @B
+field names3:
+new *0:
+inner-type 0, 0, 0, 0, 0, 0: @C
+field names4:
+new *0:
+inner-type 0, 0, 0, 0, 0, 0, 0, 0: @D
+field names5:
+new *0:
+inner-type 0, 0, 0, 0, 0, 0, 0, 0, 0, 0: @E
+
+field names10:
+new *0: @Z
+field names11:
+new *0:
+inner-type 0, 0: @A
+field names12:
+new *0:
+inner-type 0, 0, 0, 0: @B
+field names13:
+new *0:
+inner-type 0, 0, 0, 0, 0, 0: @C
+field names14:
+new *0:
+inner-type 0, 0, 0, 0, 0, 0, 0, 0: @D
+field names15:
+new *0:
+inner-type 0, 0, 0, 0, 0, 0, 0, 0, 0, 0: @E
+
+field lists:
+type: @B
+inner-type 0, 0: @Z
+inner-type 0, 0, 3, 0: @A
+new *0: @E
+inner-type 0, 0: @C
+inner-type 0, 0, 3, 0: @D
+
+method <clinit>()V:
+new #0:
+inner-type 0, 0: @Nullable
+
diff --git a/annotation-file-utilities/tests/NewArray.java b/annotation-file-utilities/tests/NewArray.java
new file mode 100644
index 0000000..74b5412
--- /dev/null
+++ b/annotation-file-utilities/tests/NewArray.java
@@ -0,0 +1,32 @@
+import java.util.ArrayList;
+import java.util.List;
+
+public class NewArray {
+     private static final int MAX_HOOKS = 10;
+     private static final Runnable[] hooks = new Runnable[MAX_HOOKS];
+
+     String[] names01 = new String[12];
+
+     String[] names02 = { "Alice", "Bob"};
+
+     String[] names03 = new String[] { "Alice", "Bob"};
+
+     static final int[] table1 = {0, 1};
+     static final int table2[] = {0, 1};
+
+     String[][][][][] names0 = new String[11][12][13][14][15];
+     String[][][][][] names1 = new String[11][12][13][14][15];
+     String[][][][][] names2 = new String[11][12][13][14][15];
+     String[][][][][] names3 = new String[11][12][13][14][15];
+     String[][][][][] names4 = new String[11][12][13][14][15];
+     String[][][][][] names5 = new String[11][12][13][14][15];
+
+     Object names10 = new String[][][][][] { { { }}};
+     Object names11 = new String[][][][][] { { { }}};
+     Object names12 = new String[][][][][] { { { }}};
+     Object names13 = new String[][][][][] { { { }}};
+     Object names14 = new String[][][][][] { { { }}};
+     Object names15 = new String[][][][][] { { { }}};
+
+     List<?>[] lists = new ArrayList<?>[2];
+}
diff --git a/annotation-file-utilities/tests/NewGeneric.goal b/annotation-file-utilities/tests/NewGeneric.goal
new file mode 100644
index 0000000..2b42309
--- /dev/null
+++ b/annotation-file-utilities/tests/NewGeneric.goal
@@ -0,0 +1,13 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class NewGeneric {
+  public void foo(Object o) {
+    List<NewGeneric> varOne = (@java.lang.Tainted List<@java.lang.UnderInitialization NewGeneric>) o;
+
+    NewGeneric varTwo = (@java.lang.UnderInitialization NewGeneric) varOne;
+
+    varTwo.foo(varOne);
+  }
+}
diff --git a/annotation-file-utilities/tests/NewGeneric.jaif b/annotation-file-utilities/tests/NewGeneric.jaif
new file mode 100644
index 0000000..3b4e91a
--- /dev/null
+++ b/annotation-file-utilities/tests/NewGeneric.jaif
@@ -0,0 +1,16 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class NewGeneric:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        typecast #1: @java.lang.Tainted
+            inner-type 3, 0: @java.lang.UnderInitialization
+        typecast #6: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/NewGeneric.java b/annotation-file-utilities/tests/NewGeneric.java
new file mode 100644
index 0000000..10155e9
--- /dev/null
+++ b/annotation-file-utilities/tests/NewGeneric.java
@@ -0,0 +1,13 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class NewGeneric {
+  public void foo(Object o) {
+    List<NewGeneric> varOne = (List<NewGeneric>) o;
+
+    NewGeneric varTwo = (NewGeneric) varOne;
+
+    varTwo.foo(varOne);
+  }
+}
diff --git a/annotation-file-utilities/tests/NewInAnnotatedVariable.goal b/annotation-file-utilities/tests/NewInAnnotatedVariable.goal
new file mode 100644
index 0000000..53fc344
--- /dev/null
+++ b/annotation-file-utilities/tests/NewInAnnotatedVariable.goal
@@ -0,0 +1,16 @@
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE_USE)
+@interface Nullable {}
+@Target(ElementType.TYPE_USE)
+@interface NonNull {}
+
+@skip-test
+public class NewInAnnotatedVariable {
+  Number b1 = new @NonNull Integer(0);
+  @NonNull Object b2 = new /*@Nullable*/ @NonNull Double(1);
+  @NonNull Runnable b3 = new /*@NonNull*/ Thread();
+  @NonNull
+  ThreadLocal @NonNull [] b4 = new @NonNull InheritableThreadLocal @NonNull [3];
+}
+
diff --git a/annotation-file-utilities/tests/NewInAnnotatedVariable.jaif b/annotation-file-utilities/tests/NewInAnnotatedVariable.jaif
new file mode 100644
index 0000000..49ae8de
--- /dev/null
+++ b/annotation-file-utilities/tests/NewInAnnotatedVariable.jaif
@@ -0,0 +1,39 @@
+package:
+annotation @NonNull:
+
+package:
+class NewInAnnotatedVariable:
+field b1:
+new *0: @NonNull
+
+package:
+class NewInAnnotatedVariable:
+field b2:
+new *0: @NonNull
+
+package:
+class NewInAnnotatedVariable:
+field b3:
+new *0: @NonNull
+
+package :
+class NewInAnnotatedVariable:
+field b4:
+type: @NonNull
+
+package :
+class NewInAnnotatedVariable:
+field b4:
+type:
+inner-type 0, 0: @NonNull
+
+package :
+class NewInAnnotatedVariable:
+field b4:
+new *0: @NonNull
+
+package :
+class NewInAnnotatedVariable:
+field b4:
+new *0:
+inner-type 0, 0: @NonNull
diff --git a/annotation-file-utilities/tests/NewInAnnotatedVariable.java b/annotation-file-utilities/tests/NewInAnnotatedVariable.java
new file mode 100644
index 0000000..971f5b0
--- /dev/null
+++ b/annotation-file-utilities/tests/NewInAnnotatedVariable.java
@@ -0,0 +1,14 @@
+import java.lang.annotation.*;
+
+@Target(ElementType.TYPE_USE)
+@interface Nullable {}
+@Target(ElementType.TYPE_USE)
+@interface NonNull {}
+
+public class NewInAnnotatedVariable {
+  Number b1 = new Integer(0);
+  @NonNull Object b2 = new /*@Nullable*/ Double(1);
+  @NonNull Runnable b3 = new /*@NonNull*/ Thread();
+  ThreadLocal[] b4 = new InheritableThreadLocal[3];
+}
+
diff --git a/annotation-file-utilities/tests/NewMultiple.goal b/annotation-file-utilities/tests/NewMultiple.goal
new file mode 100644
index 0000000..45f9cba
--- /dev/null
+++ b/annotation-file-utilities/tests/NewMultiple.goal
@@ -0,0 +1,15 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class NewMultiple {
+  void foo(Object o) {
+    List var = (/* @Tainted*/ @java.lang.Tainted List) o;
+    System.out.println(var);
+  }
+
+  void bar(Object o) {
+    List var = (/* @UnderInitialization*/ @java.lang.UnderInitialization List) o;
+    System.out.println(var);
+  }
+}
diff --git a/annotation-file-utilities/tests/NewMultiple.jaif b/annotation-file-utilities/tests/NewMultiple.jaif
new file mode 100644
index 0000000..524c8b5
--- /dev/null
+++ b/annotation-file-utilities/tests/NewMultiple.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class NewMultiple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        typecast #1: @java.lang.Tainted
+
+    method bar(Ljava/lang/Object;)V:
+        typecast #1: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/NewMultiple.java b/annotation-file-utilities/tests/NewMultiple.java
new file mode 100644
index 0000000..437fe4e
--- /dev/null
+++ b/annotation-file-utilities/tests/NewMultiple.java
@@ -0,0 +1,15 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class NewMultiple {
+  void foo(Object o) {
+    List var = (/* @Tainted*/ List) o;
+    System.out.println(var);
+  }
+
+  void bar(Object o) {
+    List var = (/* @UnderInitialization*/ List) o;
+    System.out.println(var);
+  }
+}
diff --git a/annotation-file-utilities/tests/NewPackage.goal b/annotation-file-utilities/tests/NewPackage.goal
new file mode 100644
index 0000000..ea69dd9
--- /dev/null
+++ b/annotation-file-utilities/tests/NewPackage.goal
@@ -0,0 +1,5 @@
+public class NewPackage {
+  public NewPackage() {
+    Object o = new java.util.@X LinkedList();
+  }
+}
diff --git a/annotation-file-utilities/tests/NewPackage.jaif b/annotation-file-utilities/tests/NewPackage.jaif
new file mode 100644
index 0000000..0414db0
--- /dev/null
+++ b/annotation-file-utilities/tests/NewPackage.jaif
@@ -0,0 +1,6 @@
+package:
+annotation @X:
+
+class NewPackage:
+method <init>()V:
+new *0: @X
diff --git a/annotation-file-utilities/tests/NewPackage.java b/annotation-file-utilities/tests/NewPackage.java
new file mode 100644
index 0000000..d976789
--- /dev/null
+++ b/annotation-file-utilities/tests/NewPackage.java
@@ -0,0 +1,5 @@
+public class NewPackage {
+  public NewPackage() {
+    Object o = new java.util.LinkedList();
+  }
+}
diff --git a/annotation-file-utilities/tests/NewSimple.goal b/annotation-file-utilities/tests/NewSimple.goal
new file mode 100644
index 0000000..8f3379c
--- /dev/null
+++ b/annotation-file-utilities/tests/NewSimple.goal
@@ -0,0 +1,8 @@
+package annotator.tests;
+
+public class NewSimple {
+  public void foo() {
+    Object o = new @java.lang.Tainted Object();
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/NewSimple.jaif b/annotation-file-utilities/tests/NewSimple.jaif
new file mode 100644
index 0000000..4e0af52
--- /dev/null
+++ b/annotation-file-utilities/tests/NewSimple.jaif
@@ -0,0 +1,11 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class NewSimple:
+
+    method <init>()V:
+
+    method foo()V:
+        new #0: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/NewSimple.java b/annotation-file-utilities/tests/NewSimple.java
new file mode 100644
index 0000000..a079934
--- /dev/null
+++ b/annotation-file-utilities/tests/NewSimple.java
@@ -0,0 +1,8 @@
+package annotator.tests;
+
+public class NewSimple {
+  public void foo() {
+    Object o = new Object();
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/NonClass.goal b/annotation-file-utilities/tests/NonClass.goal
new file mode 100644
index 0000000..a849c77
--- /dev/null
+++ b/annotation-file-utilities/tests/NonClass.goal
@@ -0,0 +1,13 @@
+public class NonClass {
+  @X
+  interface I extends @X Comparable<@X I> {}
+  interface A { void m(@X Object p); }
+  enum B { ONE; void m() { @X Object l; } }
+  @interface C { @X String value() default "Ha!"; }
+  enum E {
+    @V(3)
+    @V(2) D((@V(1) String) new String());
+    E(String s) { a = new @V(0) A() { @Override public void m(Object p) {} }; }
+    final @X A a;
+  }
+}
diff --git a/annotation-file-utilities/tests/NonClass.jaif b/annotation-file-utilities/tests/NonClass.jaif
new file mode 100644
index 0000000..aaefc7c
--- /dev/null
+++ b/annotation-file-utilities/tests/NonClass.jaif
@@ -0,0 +1,50 @@
+package:
+
+annotation @X:
+
+class NonClass$A:
+method m(Ljava/lang/Object;)V:
+parameter 0:
+type: @X
+
+class NonClass$B:
+method m()V:
+local l:
+type: @X
+
+class NonClass$C:
+method value()Ljava/lang/String;:
+return: @X
+
+annotation @V:
+  int value
+
+class NonClass$E:
+field D:
+typecast *0: @V(1)
+
+package:
+class NonClass$E:
+field D:
+new *0: @V(2)
+
+package:
+class NonClass$E:
+field D:
+type: @V(3)
+
+package:
+class NonClass$E:
+field a:
+type: @X
+
+package:
+class NonClass$E:
+method <init>(Ljava/lang/String;)V:
+new *0: @V(0)
+
+package:
+class NonClass$I: @X
+implements 0: @X
+inner-type 3, 0: @X
+
diff --git a/annotation-file-utilities/tests/NonClass.java b/annotation-file-utilities/tests/NonClass.java
new file mode 100644
index 0000000..8129bbe
--- /dev/null
+++ b/annotation-file-utilities/tests/NonClass.java
@@ -0,0 +1,11 @@
+public class NonClass {
+  interface I extends Comparable<I> {}
+  interface A { void m(Object p); }
+  enum B { ONE; void m() { Object l; } }
+  @interface C { String value() default "Ha!"; }
+  enum E {
+    D((String) new String());
+    E(String s) { a = new A() { @Override public void m(Object p) {} }; }
+    final A a;
+  }
+}
diff --git a/annotation-file-utilities/tests/NonStandardSpacing.goal b/annotation-file-utilities/tests/NonStandardSpacing.goal
new file mode 100644
index 0000000..648d0de
--- /dev/null
+++ b/annotation-file-utilities/tests/NonStandardSpacing.goal
@@ -0,0 +1,45 @@
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+public abstract class NonStandardSpacing {
+
+    public void m1() {
+        int a = ((@Anno int) (1  +  2)) + 3;
+        int b = ((@Anno int) (1 + /* comment */ 2)) + 3;
+        int c = ((@Anno int) (1 +
+                2)) + 3;
+        int d = ((@Anno int) (1 + // comment
+            2)) + 3;
+        int e = ((@Anno int) (1 + /* comment
+            end */ 2)) + 3;
+    }
+
+    public void m2(@Anno NonStandardSpacing this) // comment()
+    {
+
+    }
+
+    public void m3(@Anno NonStandardSpacing this) throws @AnnoField(1) Exception {
+
+    }
+
+    // ()
+    public  abstract  void  m4  (@Anno NonStandardSpacing this);
+
+    // ()
+    public abstract void //comment
+        m5(@Anno NonStandardSpacing this);
+
+    // ()
+    public abstract void /* comment
+        end */ m6(@Anno NonStandardSpacing this);
+
+    // ()
+    public abstract void m7
+                           (@Anno NonStandardSpacing this);
+}
+
+@Target(ElementType.TYPE_USE)
+@interface AnnoField {
+    int value();
+}
diff --git a/annotation-file-utilities/tests/NonStandardSpacing.jaif b/annotation-file-utilities/tests/NonStandardSpacing.jaif
new file mode 100644
index 0000000..ef48761
--- /dev/null
+++ b/annotation-file-utilities/tests/NonStandardSpacing.jaif
@@ -0,0 +1,28 @@
+package:
+    annotation @Anno:
+
+    class NonStandardSpacing:
+        method m1()V:
+            insert-typecast Block.statement 0, Variable.initializer, Binary.leftOperand: @Anno int
+            insert-typecast Block.statement 1, Variable.initializer, Binary.leftOperand: @Anno int
+            insert-typecast Block.statement 2, Variable.initializer, Binary.leftOperand: @Anno int
+            insert-typecast Block.statement 3, Variable.initializer, Binary.leftOperand: @Anno int
+            insert-typecast Block.statement 4, Variable.initializer, Binary.leftOperand: @Anno int
+
+        method m2()V:
+            receiver: @Anno
+
+        method m3()V:
+            receiver: @Anno
+
+        method m4()V:
+            receiver: @Anno
+
+        method m5()V:
+            receiver: @Anno
+
+        method m6()V:
+            receiver: @Anno
+
+        method m7()V:
+            receiver: @Anno
diff --git a/annotation-file-utilities/tests/NonStandardSpacing.java b/annotation-file-utilities/tests/NonStandardSpacing.java
new file mode 100644
index 0000000..896b8c4
--- /dev/null
+++ b/annotation-file-utilities/tests/NonStandardSpacing.java
@@ -0,0 +1,45 @@
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+public abstract class NonStandardSpacing {
+
+    public void m1() {
+        int a = 1  +  2 + 3;
+        int b = 1 + /* comment */ 2 + 3;
+        int c = 1 +
+                2 + 3;
+        int d = 1 + // comment
+            2 + 3;
+        int e = 1 + /* comment
+            end */ 2 + 3;
+    }
+
+    public void m2() // comment()
+    {
+
+    }
+
+    public void m3() throws @AnnoField(1) Exception {
+
+    }
+
+    // ()
+    public  abstract  void  m4  ();
+
+    // ()
+    public abstract void //comment
+        m5();
+
+    // ()
+    public abstract void /* comment
+        end */ m6();
+
+    // ()
+    public abstract void m7
+                           ();
+}
+
+@Target(ElementType.TYPE_USE)
+@interface AnnoField {
+    int value();
+}
diff --git a/annotation-file-utilities/tests/Package.goal b/annotation-file-utilities/tests/Package.goal
new file mode 100644
index 0000000..8cd06d7
--- /dev/null
+++ b/annotation-file-utilities/tests/Package.goal
@@ -0,0 +1,5 @@
+package pkg.name.here;
+
+class Package {
+    @A Object o;
+}
diff --git a/annotation-file-utilities/tests/Package.jaif b/annotation-file-utilities/tests/Package.jaif
new file mode 100644
index 0000000..b82af4f
--- /dev/null
+++ b/annotation-file-utilities/tests/Package.jaif
@@ -0,0 +1,7 @@
+package:
+    annotation @A: @Target(TYPE_USE)
+
+package pkg.name.here:
+    class Package:
+        field o:
+            type: @A
diff --git a/annotation-file-utilities/tests/Package.java b/annotation-file-utilities/tests/Package.java
new file mode 100644
index 0000000..14fc757
--- /dev/null
+++ b/annotation-file-utilities/tests/Package.java
@@ -0,0 +1,5 @@
+package pkg.name.here;
+
+class Package {
+    Object o;
+}
diff --git a/annotation-file-utilities/tests/ParseType.goal b/annotation-file-utilities/tests/ParseType.goal
new file mode 100644
index 0000000..4ced733
--- /dev/null
+++ b/annotation-file-utilities/tests/ParseType.goal
@@ -0,0 +1,20 @@
+import java.util.Map;
+
+public class ParseType<E> {
+    void m(Object o) {
+        Object o0 = ((@A String) (o));
+        Object o1 = ((@A Map<String, Integer>) (o));
+        Object o2 = ((String @A []) (o));
+        Object o3 = ((String[] @A []) (o));
+        Object o4 = ((@A Map<String[], Integer>) (o));
+        Object o5 = ((@A Map<String[][], Integer>) (o));
+        Object o6 = ((@A Map<?, ?>) (o));
+        Object o7 = ((@A Map<? extends String, ? super List<Integer>>) (o));
+        Object o8 = ((@A Map<List<String>, List<String>[]>) (o));
+        Object o9 = ((@A Map.Entry<String, Integer>) (o));
+        Object o10 = ((Map.Entry<String, Integer> @A []) (o));
+        Object o11 = ((ParseType<String>.Inner<Integer> @A []) (o));
+    }
+
+    public class Inner<K> {}
+}
diff --git a/annotation-file-utilities/tests/ParseType.jaif b/annotation-file-utilities/tests/ParseType.jaif
new file mode 100644
index 0000000..9c6a0d2
--- /dev/null
+++ b/annotation-file-utilities/tests/ParseType.jaif
@@ -0,0 +1,19 @@
+package:
+
+annotation @A:
+
+class ParseType:
+
+    method m(Ljava/lang/Object;)V:
+        insert-typecast Block.statement 0, Variable.initializer: @A String
+        insert-typecast Block.statement 1, Variable.initializer: @A Map<String, Integer>
+        insert-typecast Block.statement 2, Variable.initializer: @A String[]
+        insert-typecast Block.statement 3, Variable.initializer: @A String[][]
+        insert-typecast Block.statement 4, Variable.initializer: @A Map<String[], Integer>
+        insert-typecast Block.statement 5, Variable.initializer: @A Map<String[][], Integer>
+        insert-typecast Block.statement 6, Variable.initializer: @A Map<?, ?>
+        insert-typecast Block.statement 7, Variable.initializer: @A Map<? extends String, ? super List<Integer>>
+        insert-typecast Block.statement 8, Variable.initializer: @A Map<List<String>, List<String>[]>
+        insert-typecast Block.statement 9, Variable.initializer: @A Map.Entry<String, Integer>
+        insert-typecast Block.statement 10, Variable.initializer: @A Map.Entry<String, Integer>[]
+        insert-typecast Block.statement 11, Variable.initializer: @A ParseType<String>.Inner<Integer>[]
diff --git a/annotation-file-utilities/tests/ParseType.java b/annotation-file-utilities/tests/ParseType.java
new file mode 100644
index 0000000..8e969b3
--- /dev/null
+++ b/annotation-file-utilities/tests/ParseType.java
@@ -0,0 +1,20 @@
+import java.util.Map;
+
+public class ParseType<E> {
+    void m(Object o) {
+        Object o0 = o;
+        Object o1 = o;
+        Object o2 = o;
+        Object o3 = o;
+        Object o4 = o;
+        Object o5 = o;
+        Object o6 = o;
+        Object o7 = o;
+        Object o8 = o;
+        Object o9 = o;
+        Object o10 = o;
+        Object o11 = o;
+    }
+
+    public class Inner<K> {}
+}
diff --git a/annotation-file-utilities/tests/ReceiverWithThrows.goal b/annotation-file-utilities/tests/ReceiverWithThrows.goal
new file mode 100644
index 0000000..07d5da8
--- /dev/null
+++ b/annotation-file-utilities/tests/ReceiverWithThrows.goal
@@ -0,0 +1,13 @@
+package annotator.tests;
+
+public class ReceiverWithThrows {
+  /* @UnderInitialization ReceiverWithThrows this */
+  public void foo(@java.lang.UnderInitialization ReceiverWithThrows this) {
+
+  }
+
+  /* @Tainted ReceiverWithThrows this */
+  public void bar(@java.lang.Tainted ReceiverWithThrows this) throws Exception {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/ReceiverWithThrows.jaif b/annotation-file-utilities/tests/ReceiverWithThrows.jaif
new file mode 100644
index 0000000..11e79d6
--- /dev/null
+++ b/annotation-file-utilities/tests/ReceiverWithThrows.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ReceiverWithThrows:
+
+    method <init>()V:
+
+    method foo()V:
+        receiver: @java.lang.UnderInitialization
+
+    method bar()V:
+        receiver: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/ReceiverWithThrows.java b/annotation-file-utilities/tests/ReceiverWithThrows.java
new file mode 100644
index 0000000..79fddc7
--- /dev/null
+++ b/annotation-file-utilities/tests/ReceiverWithThrows.java
@@ -0,0 +1,13 @@
+package annotator.tests;
+
+public class ReceiverWithThrows {
+  /* @UnderInitialization ReceiverWithThrows this */
+  public void foo() {
+
+  }
+
+  /* @Tainted ReceiverWithThrows this */
+  public void bar() throws Exception {
+
+  }
+}
diff --git a/annotation-file-utilities/tests/Receivers.goal b/annotation-file-utilities/tests/Receivers.goal
new file mode 100644
index 0000000..f3be74c
--- /dev/null
+++ b/annotation-file-utilities/tests/Receivers.goal
@@ -0,0 +1,123 @@
+package annotator.tests;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public class Receivers {
+    public void m(@checkers.tainting.quals.Tainted Receivers this) {}
+
+    public void           spaces(@checkers.tainting.quals.Tainted Receivers this) {}
+
+    public void m(@checkers.tainting.quals.Tainted Receivers this, int i) {}
+
+    public void           spaces(@checkers.tainting.quals.Tainted Receivers this, int i) {}
+
+    public void m(@checkers.tainting.quals.Tainted Receivers this, @Anno() String s) {}
+}
+
+class Receivers2 {
+    public void m(@checkers.tainting.quals.Tainted Receivers2 this) {}
+
+    public void           spaces(@checkers.tainting.quals.Tainted Receivers2 this) {}
+
+    public void m(@checkers.tainting.quals.Tainted Receivers2 this, int i) {}
+
+    public void           spaces(@checkers.tainting.quals.Tainted Receivers2 this, int i) {}
+}
+
+class Receivers3<K, V> {
+    public void m(@checkers.tainting.quals.Tainted Receivers3<@annotation.Inner(0) K, @annotation.Inner(1) V> this) {}
+
+    public void m(@checkers.tainting.quals.Tainted Receivers3<@annotation.Inner(0) K, @annotation.Inner(1) V> this, int i) {}
+}
+
+class Receivers4<K, V> {
+    public void m(@checkers.tainting.quals.Tainted Receivers4<K, V> this) {}
+
+    public void m(@checkers.tainting.quals.Tainted Receivers4<K, V> this, int i) {}
+}
+
+interface Receivers5 {
+    public void m(@checkers.tainting.quals.Tainted Receivers5 this);
+}
+
+enum Receivers6 {
+    TEST;
+    public void m(@checkers.tainting.quals.Tainted Receivers6 this) {}
+}
+
+class Receivers7<K extends Object, V> {
+    public void m(@checkers.tainting.quals.Tainted Receivers7<@annotation.Inner(0) K, @annotation.Inner(1) V> this) {}
+}
+
+class Receivers8<K extends Object> {
+    public void m(@checkers.tainting.quals.Tainted Receivers8<K> this) {}
+}
+
+class Receivers9 {
+    public void m(@checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C Receivers9 this) {}
+}
+
+class Receivers10<K, V> {
+    public void m(@checkers.tainting.quals.Tainted Receivers10<@annotation.A K, @annotation.B V> this) {}
+
+    public void m(@checkers.tainting.quals.Tainted Receivers10<@annotation.A K, @annotation.B V> this, Receivers10<K, V> other) {}
+}
+
+@interface Anno {}
+
+// Test receiver insertion on inner class's default constructor.
+final class ScriptBasedMapping  {
+  private final class RawScriptBasedMapping {
+  public RawScriptBasedMapping(@checkers.inference.quals.VarAnnot(0) ScriptBasedMapping ScriptBasedMapping.this) { super(); }
+  }
+}
+
+// Test receiver insertion before first parameter annotation.
+interface GenericInterface<T extends Object> {
+  public T map(T toMap);
+}
+class GenericArray<Z extends Object> implements GenericInterface<String []> {
+  private Z z;
+  public void setZ(Z z) {
+    this.z = z;
+  }
+  public String [] map(@trusted.quals.Untrusted GenericArray<Z> this, @trusted.quals.Untrusted String @trusted.quals.Untrusted [] toMap) {
+    return toMap;
+  }
+}
+class GenericFields {
+  private GenericArray<String> genArray;
+}
+
+// Test inner receiver insertion before first parameter annotation.
+class Outer<T, S> {
+  class Inner<T2 extends T> {
+    private S s;
+    private T t;
+
+    protected void initialize(@checkers.inference.quals.VarAnnot(21) Outer<T, S>.Inner<@checkers.inference.quals.VarAnnot(20) T2> this, S s, T t) {
+      this.s = s;
+      this.t = t;
+    }
+
+    public Inner(S s, T t) {
+      initialize(s, t);
+    }
+  }
+}
+
+// Test that parameters inside an anonymous class get annotated.
+interface Interface {
+    String get(@trusted.quals.Untrusted Interface this, @trusted.quals.Untrusted String param);
+}
+
+// Test for infinite loop bug.
+class Closer<T> implements Closeable {
+  private final Closeable proxyProvider = System.out;
+
+  @Override
+  public void close(@checkers.inference.quals.VarAnnot(5) Closer<@checkers.inference.quals.VarAnnot(6) T> this) throws IOException {
+    proxyProvider.close();
+  }
+}
diff --git a/annotation-file-utilities/tests/Receivers.jaif b/annotation-file-utilities/tests/Receivers.jaif
new file mode 100644
index 0000000..354277e
--- /dev/null
+++ b/annotation-file-utilities/tests/Receivers.jaif
@@ -0,0 +1,140 @@
+package annotation:
+annotation @A:
+annotation @B:
+annotation @C:
+annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package checkers.tainting.quals:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package checkers.inference.quals:
+  annotation @VarAnnot:
+    int value
+
+package trusted.quals:
+  annotation @Untrusted:
+  annotation @Trusted:
+  annotation @PolyTrusted:
+
+package annotator.tests:
+class Receivers:
+
+    method m()V:
+        receiver: @Tainted
+
+    method spaces()V:
+        receiver: @Tainted
+
+    method m(I)V:
+        receiver: @Tainted
+
+    method spaces(I)V:
+        receiver: @Tainted
+
+    method m(Ljava/lang/String;)V:
+        receiver: @Tainted
+
+class Receivers2:
+
+    method m()V:
+        receiver: @Tainted
+
+    method spaces()V:
+        receiver: @Tainted
+
+    method m(I)V:
+        receiver: @Tainted
+
+    method spaces(I)V:
+        receiver: @Tainted
+
+class Receivers3:
+
+    method m()V:
+        receiver: @Tainted
+            inner-type 3, 0: @Inner(0)
+            inner-type 3, 1: @Inner(1)
+
+    method m(I)V:
+        receiver: @Tainted
+            inner-type 3, 0: @Inner(0)
+            inner-type 3, 1: @Inner(1)
+
+class Receivers4:
+
+    method m()V:
+        receiver: @Tainted
+
+    method m(I)V:
+        receiver: @Tainted
+
+class Receivers5:
+
+    method m()V:
+        receiver: @Tainted
+
+class Receivers6:
+
+    method m()V:
+        receiver: @Tainted
+
+class Receivers7:
+
+    method m()V:
+        receiver: @Tainted
+            inner-type 3, 0: @Inner(0)
+            inner-type 3, 1: @Inner(1)
+
+class Receivers8:
+
+    method m()V:
+        receiver: @Tainted
+
+class Receivers9:
+
+    method m()V:
+        receiver: @Tainted @A @B @C
+
+class Receivers10:
+
+    method m()V:
+        receiver: @Tainted
+            inner-type 3, 0: @annotation.A
+            inner-type 3, 1: @annotation.B
+
+    method m(Lannotator/tests/Receivers10;)V:
+        receiver: @Tainted
+            inner-type 3, 0: @annotation.A
+            inner-type 3, 1: @annotation.B
+
+class ScriptBasedMapping$RawScriptBasedMapping:
+
+method <init>()V:
+insert-annotation Method.parameter -1: @checkers.inference.quals.VarAnnot(0)
+
+class GenericArray:
+
+method map([Ljava/lang/String;)[Ljava/lang/String;:
+insert-annotation Method.parameter 0, Variable.type: @trusted.quals.Untrusted
+insert-annotation Method.parameter -1: @trusted.quals.Untrusted
+insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @trusted.quals.Untrusted
+
+class Outer$Inner:
+
+method initialize(Ljava/lang/Object;Ljava/lang/Object;)V:
+insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @checkers.inference.quals.VarAnnot(20)
+insert-annotation Method.parameter -1: @checkers.inference.quals.VarAnnot(21)
+
+class Interface:
+
+method get(Ljava/lang/String;)Ljava/lang/String;:
+insert-annotation Method.parameter -1: @trusted.quals.Untrusted
+insert-annotation Method.parameter 0, Variable.type: @trusted.quals.Untrusted
+
+class Closer:
+
+method close()V:
+insert-annotation Method.parameter -1: @checkers.inference.quals.VarAnnot(5)
+insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @checkers.inference.quals.VarAnnot(6)
+
diff --git a/annotation-file-utilities/tests/Receivers.java b/annotation-file-utilities/tests/Receivers.java
new file mode 100644
index 0000000..adc89ab
--- /dev/null
+++ b/annotation-file-utilities/tests/Receivers.java
@@ -0,0 +1,122 @@
+package annotator.tests;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+public class Receivers {
+    public void m() {}
+
+    public void           spaces() {}
+
+    public void m(int i) {}
+
+    public void           spaces(int i) {}
+
+    public void m(@Anno() String s) {}
+}
+
+class Receivers2 {
+    public void m(Receivers2 this) {}
+
+    public void           spaces(Receivers2 this) {}
+
+    public void m(Receivers2 this, int i) {}
+
+    public void           spaces(Receivers2 this, int i) {}
+}
+
+class Receivers3<K, V> {
+    public void m() {}
+
+    public void m(int i) {}
+}
+
+class Receivers4<K, V> {
+    public void m(Receivers4<K, V> this) {}
+
+    public void m(Receivers4<K, V> this, int i) {}
+}
+
+interface Receivers5 {
+    public void m();
+}
+
+enum Receivers6 {
+    TEST;
+    public void m() {}
+}
+
+class Receivers7<K extends Object, V> {
+    public void m() {}
+}
+
+class Receivers8<K extends Object> {
+    public void m(Receivers8<K> this) {}
+}
+
+class Receivers9 {
+    public void m() {}
+}
+
+class Receivers10<K, V> {
+    public void m(Receivers10<K, V> this) {}
+
+    public void m(Receivers10<K, V> this, Receivers10<K, V> other) {}
+}
+
+@interface Anno {}
+
+// Test receiver insertion on inner class's default constructor.
+final class ScriptBasedMapping  {
+  private final class RawScriptBasedMapping {
+  }
+}
+
+// Test receiver insertion before first parameter annotation.
+interface GenericInterface<T extends Object> {
+  public T map(T toMap);
+}
+class GenericArray<Z extends Object> implements GenericInterface<String []> {
+  private Z z;
+  public void setZ(Z z) {
+    this.z = z;
+  }
+  public String [] map(String [] toMap) {
+    return toMap;
+  }
+}
+class GenericFields {
+  private GenericArray<String> genArray;
+}
+
+// Test inner receiver insertion before first parameter annotation.
+class Outer<T, S> {
+  class Inner<T2 extends T> {
+    private S s;
+    private T t;
+
+    protected void initialize(S s, T t) {
+      this.s = s;
+      this.t = t;
+    }
+
+    public Inner(S s, T t) {
+      initialize(s, t);
+    }
+  }
+}
+
+// Test that parameters inside an anonymous class get annotated.
+interface Interface {
+    String get(String param);
+}
+
+// Test for infinite loop bug.
+class Closer<T> implements Closeable {
+  private final Closeable proxyProvider = System.out;
+
+  @Override
+  public void close() throws IOException {
+    proxyProvider.close();
+  }
+}
diff --git a/annotation-file-utilities/tests/StringEscape.goal b/annotation-file-utilities/tests/StringEscape.goal
new file mode 100644
index 0000000..3d2776f
--- /dev/null
+++ b/annotation-file-utilities/tests/StringEscape.goal
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class StringEscape {
+  public void foo(@Key(desc="PartialRegex(\"\\\")") String orig) {
+  }
+}
+
diff --git a/annotation-file-utilities/tests/StringEscape.jaif b/annotation-file-utilities/tests/StringEscape.jaif
new file mode 100644
index 0000000..f66e4a5
--- /dev/null
+++ b/annotation-file-utilities/tests/StringEscape.jaif
@@ -0,0 +1,11 @@
+package :
+annotation @Key:
+    String desc
+
+package annotator.tests:
+class StringEscape:
+    method foo(Ljava/lang/String;)V:
+        return:
+        parameter #0:
+            type: @Key(desc="PartialRegex(\"\\\")")
+
diff --git a/annotation-file-utilities/tests/StringEscape.java b/annotation-file-utilities/tests/StringEscape.java
new file mode 100644
index 0000000..d8dee21
--- /dev/null
+++ b/annotation-file-utilities/tests/StringEscape.java
@@ -0,0 +1,7 @@
+package annotator.tests;
+
+public class StringEscape {
+  public void foo(String orig) {
+  }
+}
+
diff --git a/annotation-file-utilities/tests/TypeCastGeneric.goal b/annotation-file-utilities/tests/TypeCastGeneric.goal
new file mode 100644
index 0000000..f027421
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastGeneric.goal
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class TypeCastGeneric {
+  public void foo(Object o) {
+    List<Integer> i = (List<@java.lang.UnderInitialization Integer>) o;
+    System.out.println(i);
+  }
+}
diff --git a/annotation-file-utilities/tests/TypeCastGeneric.jaif b/annotation-file-utilities/tests/TypeCastGeneric.jaif
new file mode 100644
index 0000000..3e23b10
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastGeneric.jaif
@@ -0,0 +1,12 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeCastGeneric:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        typecast #1:
+            inner-type 3, 0: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/TypeCastGeneric.java b/annotation-file-utilities/tests/TypeCastGeneric.java
new file mode 100644
index 0000000..e88cd87
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastGeneric.java
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class TypeCastGeneric {
+  public void foo(Object o) {
+    List<Integer> i = (List<Integer>) o;
+    System.out.println(i);
+  }
+}
diff --git a/annotation-file-utilities/tests/TypeCastMultiple.goal b/annotation-file-utilities/tests/TypeCastMultiple.goal
new file mode 100644
index 0000000..3517807
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastMultiple.goal
@@ -0,0 +1,15 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class TypeCastMultiple {
+  public void foo(Object o) {
+    List myList = (@java.lang.UnderInitialization List) o;
+    Integer i = (@java.lang.Tainted Integer) o;
+    String s = (String) ((@java.lang.Tainted CharSequence) o);
+    Object n = (@A String & @B Comparable<@C String> & @D CharSequence) null;
+    System.out.println(myList);
+    System.out.println(i);
+  }
+}
+
diff --git a/annotation-file-utilities/tests/TypeCastMultiple.jaif b/annotation-file-utilities/tests/TypeCastMultiple.jaif
new file mode 100644
index 0000000..185d67b
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastMultiple.jaif
@@ -0,0 +1,25 @@
+package :
+annotation @A: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+annotation @B: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+annotation @C: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+annotation @D: @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeCastMultiple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        typecast #1: @java.lang.UnderInitialization
+        typecast #6: @java.lang.Tainted
+        //typecast #11: @java.lang.Tainted  // inserts in wrong place!
+        typecast *3: @java.lang.Tainted
+        typecast *4, 0: @A
+        typecast *4, 1: @B
+            inner-type 3, 0: @C
+        typecast *4, 2: @D
+
diff --git a/annotation-file-utilities/tests/TypeCastMultiple.java b/annotation-file-utilities/tests/TypeCastMultiple.java
new file mode 100644
index 0000000..b2ecdad
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastMultiple.java
@@ -0,0 +1,15 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class TypeCastMultiple {
+  public void foo(Object o) {
+    List myList = (List) o;
+    Integer i = (Integer) o;
+    String s = (String) ((CharSequence) o);
+    Object n = (String & Comparable<String> & CharSequence) null;
+    System.out.println(myList);
+    System.out.println(i);
+  }
+}
+
diff --git a/annotation-file-utilities/tests/TypeCastSimple.goal b/annotation-file-utilities/tests/TypeCastSimple.goal
new file mode 100644
index 0000000..0db4599
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastSimple.goal
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class TypeCastSimple {
+  public void foo(Object o) {
+    List myList = (/* @UnderInitialization*/ @java.lang.UnderInitialization List) o;
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/TypeCastSimple.jaif b/annotation-file-utilities/tests/TypeCastSimple.jaif
new file mode 100644
index 0000000..76be40b
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastSimple.jaif
@@ -0,0 +1,11 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeCastSimple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        typecast #1: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/TypeCastSimple.java b/annotation-file-utilities/tests/TypeCastSimple.java
new file mode 100644
index 0000000..89db0fb
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeCastSimple.java
@@ -0,0 +1,10 @@
+package annotator.tests;
+
+import java.util.List;
+
+public class TypeCastSimple {
+  public void foo(Object o) {
+    List myList = (/* @UnderInitialization*/ List) o;
+    System.out.println(o);
+  }
+}
diff --git a/annotation-file-utilities/tests/TypeParamMethod.goal b/annotation-file-utilities/tests/TypeParamMethod.goal
new file mode 100644
index 0000000..f838e16
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeParamMethod.goal
@@ -0,0 +1,24 @@
+package annotator.tests;
+
+import java.util.Date;
+
+public class TypeParamMethod {
+
+  public <@java.lang.Tainted T> void foo(T t) {
+    System.out.println(t);
+  }
+
+  public <@java.lang.Tainted T extends Date> void foo2(T t) {
+    System.out.println(t);
+  }
+
+  public <T, @java.lang.Tainted U> void foo(T t, U u) {
+    System.out.println(t);
+    System.out.println(u);
+  }
+
+  public <T extends Date, @java.lang.Tainted U extends Date> void foo2(T t, U u) {
+    System.out.println(t);
+    System.out.println(u);
+  }
+}
diff --git a/annotation-file-utilities/tests/TypeParamMethod.jaif b/annotation-file-utilities/tests/TypeParamMethod.jaif
new file mode 100644
index 0000000..70e10d5
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeParamMethod.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeParamMethod:
+
+    method foo(Ljava/lang/Object;)V:
+        typeparam 0: @java.lang.Tainted
+
+    method foo2(Lannotator/tests/Date;)V:
+        typeparam 0: @java.lang.Tainted
+
+    method foo(Ljava/lang/Object;Ljava/lang/Object;)V:
+        typeparam 1: @java.lang.Tainted
+
+    method foo2(Ljava/util/Date;Ljava/util/Date;)V:
+        typeparam 1: @java.lang.Tainted
diff --git a/annotation-file-utilities/tests/TypeParamMethod.java b/annotation-file-utilities/tests/TypeParamMethod.java
new file mode 100644
index 0000000..30a3fe2
--- /dev/null
+++ b/annotation-file-utilities/tests/TypeParamMethod.java
@@ -0,0 +1,24 @@
+package annotator.tests;
+
+import java.util.Date;
+
+public class TypeParamMethod {
+
+  public <T> void foo(T t) {
+    System.out.println(t);
+  }
+
+  public <T extends Date> void foo2(T t) {
+    System.out.println(t);
+  }
+
+  public <T, U> void foo(T t, U u) {
+    System.out.println(t);
+    System.out.println(u);
+  }
+
+  public <T extends Date, U extends Date> void foo2(T t, U u) {
+    System.out.println(t);
+    System.out.println(u);
+  }
+}
diff --git a/annotation-file-utilities/tests/Varargs.goal b/annotation-file-utilities/tests/Varargs.goal
new file mode 100644
index 0000000..c6bf60f
--- /dev/null
+++ b/annotation-file-utilities/tests/Varargs.goal
@@ -0,0 +1,16 @@
+package annotator.tests;
+
+public class Varargs {
+
+    public void m1(@java.lang.Nullable String ... params) {
+    }
+
+    public void m2(String @java.lang.Nullable ... params) {
+    }
+
+    public void m3(@java.lang.Nullable String @java.lang.Nullable ... params) {
+    }
+
+    public void m4(@java.lang.Nullable Comparable<@java.lang.Nullable String> @java.lang.Nullable ... params) {
+    }
+}
diff --git a/annotation-file-utilities/tests/Varargs.jaif b/annotation-file-utilities/tests/Varargs.jaif
new file mode 100644
index 0000000..46e5f07
--- /dev/null
+++ b/annotation-file-utilities/tests/Varargs.jaif
@@ -0,0 +1,26 @@
+package java.lang:
+
+// Note no @Target meta-annotation
+annotation @Nullable:
+
+
+package annotator.tests:
+
+class Varargs:
+    method m1([Ljava.lang.String;)V:
+      parameter #0:
+       type:
+       inner-type 0, 0: @Nullable
+    method m2([Ljava.lang.String;)V:
+      parameter #0:
+        type: @Nullable
+    method m3([Ljava.lang.String;)V:
+      parameter #0:
+        type: @Nullable
+        inner-type 0, 0: @Nullable
+    method m4([Ljava.lang.Comparable;)V:
+      parameter #0:
+        type: @Nullable
+        inner-type 0, 0: @Nullable
+        inner-type 0, 0, 3, 0: @Nullable
+
diff --git a/annotation-file-utilities/tests/Varargs.java b/annotation-file-utilities/tests/Varargs.java
new file mode 100644
index 0000000..b1ff4a4
--- /dev/null
+++ b/annotation-file-utilities/tests/Varargs.java
@@ -0,0 +1,16 @@
+package annotator.tests;
+
+public class Varargs {
+
+    public void m1(String ... params) {
+    }
+
+    public void m2(String ... params) {
+    }
+
+    public void m3(String ... params) {
+    }
+
+    public void m4(Comparable<String> ... params) {
+    }
+}
diff --git a/annotation-file-utilities/tests/VerifyDiffs.java b/annotation-file-utilities/tests/VerifyDiffs.java
new file mode 100644
index 0000000..5d584e2
--- /dev/null
+++ b/annotation-file-utilities/tests/VerifyDiffs.java
@@ -0,0 +1,92 @@
+import java.io.*;
+import java.util.*;
+
+/**
+ * Little program that checks to see if every file ending in .diff in
+ * the current directory is empty.  For each file, displays whether or not
+ * it is empty.  If any file is non-empty, exits with an error exit status,
+ * else exits with a 0 exit status.
+ *
+ * Usage: java VerifyDiffs [--show_all]
+ *
+ * If --show_all option is used, all tests that pass will also be displayed.
+ * If --show_all is not used, and all tests pass, then there will be no
+ * output.
+ */
+public class VerifyDiffs {
+    private static boolean show_all = false;
+
+    private static void parseArgs(String[] args) {
+        for (String s : args) {
+            if (s.equals("--show_all")) {
+                VerifyDiffs.show_all = true;
+            }
+        }
+    }
+
+    public static void main(String[] args) {
+        parseArgs(args);
+
+        int passCount = 0;
+        int failCount = 0;
+        boolean pass = true;
+        try {
+
+            File dir = new File(".");
+
+            List<File> allDiffs = new ArrayList<File>();
+            gatherDiffs(allDiffs, dir);
+            Collections.sort(allDiffs);
+            for (File f : allDiffs) {
+                String fileName = f.toString();
+                if (fileName.startsWith("./")) {
+                    fileName = fileName.substring(2);
+                }
+                FileReader fr = new FileReader(f);
+                if (fr.read() != -1) { // if not empty, output error message
+                    System.out.println(fileName + " ...FAILED");
+                    pass = false;
+                    failCount++;
+                } else {
+                    if (VerifyDiffs.show_all) {
+                        System.out.println(fileName + " ...OK");
+                    }
+                    passCount++;
+                }
+                fr.close();
+            }
+        } catch (Exception e) {
+            System.out.println("verify diffs failed due to exception: "
+                               + e.getMessage());
+            pass = false;
+        }
+
+        System.out.println("Passed: " + passCount + "    Failed: " + failCount);
+        if (pass) {
+            if (VerifyDiffs.show_all) {
+                System.out.println("All tests succeeded.");
+            }
+        } else {
+            System.out.println("Tests failed.");
+            System.exit(1);
+        }
+    }
+
+    /**
+     * Recursively adds all files in directory dir ending in .diff to
+     * the list diffs.
+     *
+     * @param diffs the array to place all diff files in
+     * @param dir the directory to start gathering diffs
+     */
+    private static void gatherDiffs(List<File> diffs, File dir) {
+      for (File f : dir.listFiles()) {
+        if (f.toString().endsWith(".diff")) {
+          diffs.add(f);
+        }
+        if (f.isDirectory()) {
+          gatherDiffs(diffs, f);
+        }
+      }
+    }
+}
diff --git a/annotation-file-utilities/tests/WildcardAnnoBound.goal b/annotation-file-utilities/tests/WildcardAnnoBound.goal
new file mode 100644
index 0000000..d4c12a8
--- /dev/null
+++ b/annotation-file-utilities/tests/WildcardAnnoBound.goal
@@ -0,0 +1,13 @@
+import java.util.List;
+import java.util.Map;
+
+@interface Bla {}
+
+public class WildcardAnnoBound<X extends List<? extends @Bla Object>> {
+  WildcardAnnoBound(@Bla WildcardAnnoBound<X> n, X p) {
+  }
+}
+
+class NoBound<X extends @A Object> {}
+
+class Bounds<@A X extends @B Object & @C Comparable<@D int @E []> & @F Map<@G ? extends @H Object, @I ?>, @J Y> {}
diff --git a/annotation-file-utilities/tests/WildcardAnnoBound.jaif b/annotation-file-utilities/tests/WildcardAnnoBound.jaif
new file mode 100644
index 0000000..0517f2d
--- /dev/null
+++ b/annotation-file-utilities/tests/WildcardAnnoBound.jaif
@@ -0,0 +1,36 @@
+package:
+annotation @Bla:
+annotation @A:
+annotation @B:
+annotation @C:
+annotation @D:
+annotation @E:
+annotation @F:
+annotation @G:
+annotation @H:
+annotation @I:
+annotation @J:
+
+class WildcardAnnoBound:
+bound 0 & 0:
+inner-type 3, 0, 2, 0: @Bla
+
+class WildcardAnnoBound:
+method <init>(LWildcardAnnoBound;Ljava/util/List;)V:
+parameter 0:
+type: @Bla
+
+class NoBound:
+bound 0 & 0: @A
+
+class Bounds:
+	typeparam 0: @A
+	typeparam 1: @J
+	bound 0 & 0: @B
+	bound 0 & 1: @C
+		inner-type 3, 0: @E
+		inner-type 3, 0, 0, 0: @D
+	bound 0 & 2: @F
+		inner-type 3, 0: @G
+		inner-type 3, 0, 2, 0: @H
+		inner-type 3, 1: @I
diff --git a/annotation-file-utilities/tests/WildcardAnnoBound.java b/annotation-file-utilities/tests/WildcardAnnoBound.java
new file mode 100644
index 0000000..d4ce3d4
--- /dev/null
+++ b/annotation-file-utilities/tests/WildcardAnnoBound.java
@@ -0,0 +1,13 @@
+import java.util.List;
+import java.util.Map;
+
+@interface Bla {}
+
+public class WildcardAnnoBound<X extends List<? extends Object>> {
+  WildcardAnnoBound(WildcardAnnoBound<X> n, X p) {
+  }
+}
+
+class NoBound<X> {}
+
+class Bounds<X extends Object & Comparable<int[]> & Map<? extends Object, ?>, Y> {}
diff --git a/annotation-file-utilities/tests/abbreviated/C.jaif b/annotation-file-utilities/tests/abbreviated/C.jaif
new file mode 100644
index 0000000..47c586f
--- /dev/null
+++ b/annotation-file-utilities/tests/abbreviated/C.jaif
@@ -0,0 +1,17 @@
+package javax.annotation:
+annotation @Resource: @Target(value={TYPE,FIELD,METHOD})
+
+package org.checkerframework.checker.nullness.qual:
+annotation @Nullable:
+
+package :
+class C1:
+
+    field values:
+        type: @javax.annotation.Resource
+
+    method foo(Ljava/lang/Object;)Lplume/C1;:
+        return:
+        parameter #0:
+            type: @org.checkerframework.checker.nullness.qual.Nullable
+
diff --git a/annotation-file-utilities/tests/abbreviated/C0.goal b/annotation-file-utilities/tests/abbreviated/C0.goal
new file mode 100644
index 0000000..5055295
--- /dev/null
+++ b/annotation-file-utilities/tests/abbreviated/C0.goal
@@ -0,0 +1 @@
+public class C0 {}
diff --git a/annotation-file-utilities/tests/abbreviated/C0.java b/annotation-file-utilities/tests/abbreviated/C0.java
new file mode 100644
index 0000000..5055295
--- /dev/null
+++ b/annotation-file-utilities/tests/abbreviated/C0.java
@@ -0,0 +1 @@
+public class C0 {}
diff --git a/annotation-file-utilities/tests/abbreviated/C1.goal b/annotation-file-utilities/tests/abbreviated/C1.goal
new file mode 100644
index 0000000..95aeffe
--- /dev/null
+++ b/annotation-file-utilities/tests/abbreviated/C1.goal
@@ -0,0 +1,9 @@
+import org.checkerframework.checker.nullness.qual.Nullable;
+import javax.annotation.Resource;
+public class C1<T extends Object> {
+  @Resource
+  int values = 0;
+  public <A extends Object> C1<A> foo(@Nullable A a) {
+      return null;
+  }
+}
diff --git a/annotation-file-utilities/tests/abbreviated/C1.java b/annotation-file-utilities/tests/abbreviated/C1.java
new file mode 100644
index 0000000..4df3603
--- /dev/null
+++ b/annotation-file-utilities/tests/abbreviated/C1.java
@@ -0,0 +1,7 @@
+import javax.annotation.Resource;
+public class C1<T extends Object> {
+  int values = 0;
+  public <A extends Object> C1<A> foo(A a) {
+      return null;
+  }
+}
diff --git a/annotation-file-utilities/tests/abbreviated/Makefile b/annotation-file-utilities/tests/abbreviated/Makefile
new file mode 100644
index 0000000..368c0a2
--- /dev/null
+++ b/annotation-file-utilities/tests/abbreviated/Makefile
@@ -0,0 +1,85 @@
+# Very rough testing framework for the annotator.  Running 'make all' will
+# look for all myClass.goal files in this directory, run the annotator on the
+# corresponding .jaif and .java files, and then output the difference in a
+# myClass.diff file in this directory.
+#
+# To test just one file, use (for example) 'make myClass.diff'.
+
+# Put user-specific changes in your own Makefile.user.
+# Make will silently continue if that file does not exist.
+-include ../Makefile.user
+
+# Override these in Makefile.user if the java and javac commands are not on
+# your execution path.  Example from Makefile.user:
+#   JAVA=${JAVA_HOME}/bin/java
+#   JAVAC=${JAVA_HOME}/bin/javac
+JAVA?=java
+JAVAC?=javac
+
+export SHELL=/bin/bash -o pipefail
+
+
+DIFFS := $(wildcard *.goal)
+DISABLED := $(shell grep -le "@skip-test" $(DIFFS))
+FILTERED := $(filter-out $(DISABLED),$(DIFFS))
+DIFFS := $(patsubst %.goal, %.diff, $(FILTERED))
+AFU_JARS := ../../lib/plume-core.jar ../../annotation-file-utilities.jar
+JAIF := C.jaif
+SRC := $(patsubst %.goal, %.java, $(FILTERED))
+OUT := $(patsubst %.goal, %.output, $(FILTERED))
+
+DEBUG :=
+# Use this to enable some debugging.
+# DEBUG := --debug
+
+default : all
+
+.PHONY: all
+all : $(DIFFS) results
+
+# Display results of all .diff files.
+.PHONY: results
+results: ../bin/VerifyDiffs.class
+	@rm -rf output
+	@echo ""
+	@echo "=== RESULTS ==="
+	@echo ""
+	@$(JAVA) -cp bin:../bin VerifyDiffs --show_all
+
+# Remakes the little java program that checks and compares diffs
+../bin/VerifyDiffs.class : ../VerifyDiffs.java
+	@$(JAVAC) -g -cp ../../bincompile -d ../bin ../VerifyDiffs.java
+
+# Compiles all the test cases (be verbose about this).
+.PHONY: compile
+compile : $(SRC)
+	mkdir -p bin
+	$(JAVAC) -g -cp bin:../../bin -d bin -sourcepath . $(SRC)
+
+# Actually runs the annotator to create the annotated java file.
+output: compile $(JAIF) ../../bin $(AFU_JARS)
+	$(JAVA) \
+	-cp ../../bin:../../annotation-file-utilities.jar:bin \
+	annotator.Main \
+	${DEBUG} \
+	--abbreviate=true \
+	-d output \
+	$(JAIF) \
+	$(SRC) \
+	2>&1 | tee C.log
+
+.PRECIOUS: %.output
+%.output: output
+	find output -name "$*.java" -print | xargs cat > "$*.output"
+
+# Compare the output of the annotator and the goal file.
+%.diff: %.goal %.output
+	-diff -u $*.goal $*.output >& $*.diff
+
+# Remove all .diff, .log files from the tests directory.
+.PHONY: clean
+clean :
+	rm -rf bin
+	rm -f *.diff
+	rm -f *.log
+	rm -f *.output
diff --git a/annotation-file-utilities/tests/abbreviated/README.md b/annotation-file-utilities/tests/abbreviated/README.md
new file mode 100644
index 0000000..f870261
--- /dev/null
+++ b/annotation-file-utilities/tests/abbreviated/README.md
@@ -0,0 +1,6 @@
+For motivation/explanation, see
+[Issue 111](https://github.com/typetools/annotation-tools/issues/111).
+(Note that `C2` in the linked issue became `C0` here because the outcome
+depends on the order of the arguments, and `Makefile` passes the files
+to the annotator in reverse lexicographical order.)
+
diff --git a/annotation-file-utilities/tests/ad-hoc/Makefile b/annotation-file-utilities/tests/ad-hoc/Makefile
new file mode 100644
index 0000000..e372c78
--- /dev/null
+++ b/annotation-file-utilities/tests/ad-hoc/Makefile
@@ -0,0 +1,10 @@
+.PHONY: all
+all: bridge
+
+.PHONY: bridge
+bridge:
+	${MAKE} -C bridge
+
+.PHONY: clean
+clean:
+	${MAKE} -C bridge clean
diff --git a/annotation-file-utilities/tests/ad-hoc/README b/annotation-file-utilities/tests/ad-hoc/README
new file mode 100644
index 0000000..6ba5d8e
--- /dev/null
+++ b/annotation-file-utilities/tests/ad-hoc/README
@@ -0,0 +1 @@
+This directory is a place to put tests that don't fit into the harness.
diff --git a/annotation-file-utilities/tests/ad-hoc/bridge/C.java b/annotation-file-utilities/tests/ad-hoc/bridge/C.java
new file mode 100644
index 0000000..3bf6155
--- /dev/null
+++ b/annotation-file-utilities/tests/ad-hoc/bridge/C.java
@@ -0,0 +1,11 @@
+@interface A {}
+
+class C implements Comparable<C> {
+  String value = "";
+
+  @A
+  public int compareTo(C other) {
+    return value.compareTo(other.value);
+  }
+}
+
diff --git a/annotation-file-utilities/tests/ad-hoc/bridge/Makefile b/annotation-file-utilities/tests/ad-hoc/bridge/Makefile
new file mode 100644
index 0000000..e85d0f7
--- /dev/null
+++ b/annotation-file-utilities/tests/ad-hoc/bridge/Makefile
@@ -0,0 +1,31 @@
+.PHONY: all
+all: bridge no-bridge
+
+JAVAC?=javac
+
+JAVACVERSION = $(shell $(JAVAC) -version 2>&1)
+ifeq (1.7.,$(findstring 1.7.,$(JAVACVERSION)))
+  JAVACTARGET = -source 7 -target 7
+endif
+
+.PHONY: bridge
+# Counting bridge methods, there should be 3 @A annotations.
+bridge: C.class
+	CLASSPATH=.:${CLASSPATH} ../../../scripts/extract-annotations C.class
+# skip test if using javac 1.7, which doesn't copy annotations to bridge methods
+	test 3 -eq `grep -c -w '@A' C.jaif` || (echo "Didn't find 3 '@A' in C.jaif:" && cat C.jaif && false)
+
+.PHONY: no-bridge
+# Not counting bridge methods, there should be 2 @A annotations.
+# -b ignores annotations on bridge methods
+no-bridge: C.class
+	CLASSPATH=.:${CLASSPATH} ../../../scripts/extract-annotations -b C.class
+	test 2 -eq `grep -c -w '@A' C.jaif` || (echo "Didn't find 2 '@A' in C.jaif:" && cat C.jaif && false)
+
+C.class: C.java
+	$(XJAVAC) -g $(JAVACTARGET) C.java
+
+.PHONY: clean
+clean:
+	rm -rf *.class C.jaif annotated out
+
diff --git a/annotation-file-utilities/tests/ad-hoc/bridge/README b/annotation-file-utilities/tests/ad-hoc/bridge/README
new file mode 100644
index 0000000..3854ea1
--- /dev/null
+++ b/annotation-file-utilities/tests/ad-hoc/bridge/README
@@ -0,0 +1,5 @@
+Test of bridge method inclusion/exclusion.  For the default target
+(created with "make all"), the extracted JAIF should contain entries for
+both "compareTo(LC;)I" and "compareTo(Ljava/lang/Object;)I"; for the
+alternate target ("make alt"), the JAIF should omit the latter.
+
diff --git a/annotation-file-utilities/tests/converted/ASTInsert.jaif b/annotation-file-utilities/tests/converted/ASTInsert.jaif
new file mode 100644
index 0000000..7489db6
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ASTInsert.jaif
@@ -0,0 +1,127 @@
+package checkers.nullness.quals:
+annotation @Nullable: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME)
+
+package :
+annotation @A:
+
+package :
+annotation @B:
+
+package :
+annotation @C:
+
+package checkers.nullness.quals:
+annotation @NonNull: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME)
+
+package :
+annotation @Bla:
+
+package :
+annotation @D:
+
+package :
+annotation @H:
+
+package :
+annotation @E:
+
+package :
+annotation @G:
+
+package :
+annotation @I:
+
+package :
+annotation @F:
+
+package :
+annotation @J:
+
+package :
+class ASTInsert:
+
+    field c:
+        insert-annotation Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Variable.initializer, Binary.rightOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Variable.initializer: @checkers.nullness.quals.Nullable
+
+    field str:
+        insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable
+
+    field sa:
+        insert-annotation Variable.type, ArrayType.type: @Bla
+        insert-annotation Variable.initializer, NewArray.type 0: @Bla
+    inner-type 0, 0: @Bla
+        insert-annotation Variable.type: @Bla
+
+    method m(Ljava/lang/String;[Ljava/lang/String;I)I:
+        return:
+        insert-annotation Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 2, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 16, Variable.initializer, MemberSelect.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 14, Variable.initializer, InstanceOf.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 18, Variable.initializer, NewArray.dimension 1: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 7, Switch.expression, Parenthesized.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 21, If.thenStatement, Return.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 23, If.thenStatement, Throw.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 12, ForLoop.initializer 1, Variable.initializer: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 12, ForLoop.condition, Binary.rightOperand: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable
+        insert-annotation Block.statement 30, Variable.initializer: @checkers.nullness.quals.Nullable @A @B @C
+
+    method context()V:
+        return:
+        insert-annotation Block.statement 0, ExpressionStatement.expression, MethodInvocation.typeArgument 0: @checkers.nullness.quals.NonNull
+
+package :
+class Wild:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0, ParameterizedType.typeArgument 0, TypeParameter.bound 0: @Bla
+
+    method <init>(LWild;Ljava/util/List;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @Bla
+
+package :
+class Unbound:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @A
+
+package :
+class Bound:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0, ArrayType.type: @D
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0, Wildcard.bound: @H
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0: @E
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0: @G
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 1: @I
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @B
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 1: @C
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2: @F
+    insert-annotation Class.typeParameter 0: @A
+    insert-annotation Class.typeParameter 1: @J
+
diff --git a/annotation-file-utilities/tests/converted/AnonInner.jaif b/annotation-file-utilities/tests/converted/AnonInner.jaif
new file mode 100644
index 0000000..bfbbe89
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/AnonInner.jaif
@@ -0,0 +1,45 @@
+package :
+annotation @A:
+
+package :
+annotation @D:
+
+package :
+annotation @C:
+
+package :
+annotation @B:
+
+package :
+annotation @F:
+
+package :
+annotation @E:
+
+package :
+class AnonInner$InnerOne:
+
+    method m1(Ljava/lang/String;)Ljava/lang/Object;:
+        return:
+        insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @A
+
+    method m2(Ljava/lang/String;)Ljava/lang/Object;:
+        return:
+        insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @D
+
+package :
+class AnonInner$InnerOne$1:
+
+    method e1(Ljava/lang/Object;)Ljava/lang/Object;:
+        return:
+        insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @C
+        insert-annotation Method.type: @B
+
+package :
+class AnonInner$InnerOne$2:
+
+    method e2(Ljava/lang/Object;)Ljava/lang/Object;:
+        return:
+        insert-annotation Method.body, Block.statement 0, Return.expression, NewClass.identifier: @F
+        insert-annotation Method.type: @E
+
diff --git a/annotation-file-utilities/tests/converted/ArrayMultiDim.jaif b/annotation-file-utilities/tests/converted/ArrayMultiDim.jaif
new file mode 100644
index 0000000..6279f96
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ArrayMultiDim.jaif
@@ -0,0 +1,37 @@
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @F: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ArrayMultiDim:
+
+    field field:
+        insert-annotation Variable.type, ArrayType.type, ArrayType.type, ArrayType.type: @java.lang.C
+        insert-annotation Variable.type, ArrayType.type, ArrayType.type: @java.lang.F
+        insert-annotation Variable.type, ArrayType.type: @java.lang.E
+        insert-annotation Variable.type: @java.lang.D
+
+    field field2:
+        insert-annotation Variable.initializer, NewArray.type 0: @java.lang.D
+        insert-annotation Variable.initializer, NewArray.type 1: @java.lang.E
+        insert-annotation Variable.initializer, NewArray.type 2: @java.lang.F
+        insert-annotation Variable.initializer, NewArray.type 3: @java.lang.C
+
+    field field3:
+        insert-annotation Variable.type, ArrayType.type, ArrayType.type, ArrayType.type: @java.lang.C
+        insert-annotation Variable.type, ArrayType.type, ArrayType.type: @java.lang.F
+        insert-annotation Variable.type, ArrayType.type: @java.lang.E
+        insert-annotation Variable.initializer, NewArray.type 0: @java.lang.D
+        insert-annotation Variable.initializer, NewArray.type 1: @java.lang.E
+        insert-annotation Variable.initializer, NewArray.type 2: @java.lang.F
+        insert-annotation Variable.initializer, NewArray.type 3: @java.lang.C
+        insert-annotation Variable.type: @java.lang.D
+
diff --git a/annotation-file-utilities/tests/converted/ArrayParamSimple.jaif b/annotation-file-utilities/tests/converted/ArrayParamSimple.jaif
new file mode 100644
index 0000000..4c6a4aa
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ArrayParamSimple.jaif
@@ -0,0 +1,25 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Nullable:
+
+package annotator.tests:
+class ArrayParamSimple:
+
+    method m1([Ljava/lang/Integer;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Tainted
+
+    method m2([Ljava/lang/Integer;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.Tainted
+
+    method m3([Ljava/lang/Integer;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable
+
+    method m4([Ljava/lang/Integer;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable
+
diff --git a/annotation-file-utilities/tests/converted/ArrayReturnTypeSimple.jaif b/annotation-file-utilities/tests/converted/ArrayReturnTypeSimple.jaif
new file mode 100644
index 0000000..0ec9a39
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ArrayReturnTypeSimple.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ArrayReturnTypeSimple:
+
+    method foo()[Ljava/lang/Object;:
+        return:
+        insert-annotation Method.type, ArrayType.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/BoundClassMultiple.jaif b/annotation-file-utilities/tests/converted/BoundClassMultiple.jaif
new file mode 100644
index 0000000..9be7354
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/BoundClassMultiple.jaif
@@ -0,0 +1,23 @@
+package java.lang:
+annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundClassMultiple:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @java.lang.A
+    insert-annotation Class.typeParameter 1, TypeParameter.bound 0: @java.lang.B
+    insert-annotation Class.typeParameter 2, TypeParameter.bound 0: @java.lang.D
+    insert-annotation Class.typeParameter 1, TypeParameter.bound 1: @java.lang.C
+    insert-annotation Class.typeParameter 2, TypeParameter.bound 0, ParameterizedType.typeArgument 0: @java.lang.E
+
diff --git a/annotation-file-utilities/tests/converted/BoundClassSimple.jaif b/annotation-file-utilities/tests/converted/BoundClassSimple.jaif
new file mode 100644
index 0000000..5bc0c73
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/BoundClassSimple.jaif
@@ -0,0 +1,7 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundClassSimple:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/BoundMethodMultiple.jaif b/annotation-file-utilities/tests/converted/BoundMethodMultiple.jaif
new file mode 100644
index 0000000..26e0281
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/BoundMethodMultiple.jaif
@@ -0,0 +1,39 @@
+package java.lang:
+annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @F: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundMethodMultiple:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.A
+
+    method foo(Ljava/util/Date;)V:
+        return:
+        insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.B
+
+    method foo(Ljava/util/List;)V:
+        return:
+        insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.C
+        insert-annotation Method.typeParameter 0, TypeParameter.bound 1: @java.lang.D
+
+    method foo(Ljava/util/Date;Ljava/util/Map;)V:
+        return:
+        insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.E
+        insert-annotation Method.typeParameter 1, TypeParameter.bound 0: @java.lang.F
+
diff --git a/annotation-file-utilities/tests/converted/BoundMethodSimple.jaif b/annotation-file-utilities/tests/converted/BoundMethodSimple.jaif
new file mode 100644
index 0000000..fb2a8ca
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/BoundMethodSimple.jaif
@@ -0,0 +1,14 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class BoundMethodSimple:
+
+    method foo(Ljava/util/Date;)V:
+        return:
+        insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.Tainted
+
+    method foo2(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.typeParameter 0, TypeParameter.bound 0: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/CastInsert.jaif b/annotation-file-utilities/tests/converted/CastInsert.jaif
new file mode 100644
index 0000000..2863e2a
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/CastInsert.jaif
@@ -0,0 +1,84 @@
+package checkers.nullness.quals:
+annotation @Nullable: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME)
+
+package annotation:
+annotation @A:
+
+package annotation:
+annotation @B:
+
+package annotation:
+annotation @C:
+
+package :
+class CastInsert:
+
+    field c:
+        insert-typecast Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Variable.initializer, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Variable.initializer: @checkers.nullness.quals.Nullable Integer
+
+    field str:
+        insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable String
+        insert-typecast Variable.initializer, Binary.rightOperand, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable String
+
+    method m(Ljava/lang/String;[Ljava/lang/String;I)I:
+        return:
+        insert-typecast Block.statement 24, Try.catch 1, Catch.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 24, Try.finallyBlock, Block.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 7, Switch.case 0, Case.statement 0, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 25, ExpressionStatement.expression, Assignment.expression, TypeCast.expression, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 11, EnhancedForLoop.statement, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 22, Synchronized.block, Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 28, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression, MemberSelect.expression: @checkers.nullness.quals.Nullable CastInsert
+        insert-typecast Block.statement 7, Switch.case 1, Case.statement 1, ExpressionStatement.expression, MethodInvocation.argument 0: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 27, WhileLoop.statement, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 10, DoWhileLoop.statement, Block.statement 0, Variable.initializer, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression, ArrayAccess.index: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 12, ForLoop.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 12, ForLoop.update 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 13, If.elseStatement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 15, LabeledStatement.statement, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Boolean
+        insert-typecast Block.statement 9, ExpressionStatement.expression, Assignment.expression, ConditionalExpression.trueExpression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 29, ExpressionStatement.expression, MethodInvocation.methodSelect, MemberSelect.expression: @checkers.nullness.quals.Nullable PrintStream
+        insert-typecast Block.statement 19, Variable.initializer, NewArray.initializer 1, NewArray.initializer 2: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 24, Try.block, Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 26, ExpressionStatement.expression, Assignment.expression, Unary.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable String
+        insert-typecast Block.statement 13, If.condition, Parenthesized.expression, Binary.leftOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable String
+        insert-typecast Block.statement 27, WhileLoop.condition, Parenthesized.expression, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable String
+        insert-typecast Block.statement 16, Variable.initializer, MemberSelect.expression: @checkers.nullness.quals.Nullable CastInsert
+        insert-typecast Block.statement 14, Variable.initializer, InstanceOf.expression: @checkers.nullness.quals.Nullable String
+        insert-typecast Block.statement 17, ExpressionStatement.expression, MethodInvocation.argument 2: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 18, Variable.initializer, NewArray.dimension 1: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 20, ExpressionStatement.expression, NewClass.argument 0: @checkers.nullness.quals.Nullable String
+        insert-typecast Block.statement 7, Switch.expression, Parenthesized.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 21, If.thenStatement, Return.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 23, If.thenStatement, Throw.expression: @checkers.nullness.quals.Nullable RuntimeException
+        insert-typecast Block.statement 12, ForLoop.initializer 1, Variable.initializer: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 12, ForLoop.condition, Binary.rightOperand: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 8, ExpressionStatement.expression, CompoundAssignment.expression: @checkers.nullness.quals.Nullable Integer
+        insert-typecast Block.statement 0, Variable.initializer: @checkers.nullness.quals.Nullable String
+        insert-typecast Block.statement 30, Variable.initializer: @checkers.nullness.quals.Nullable @annotation.A @annotation.B @annotation.C String
+
+    method m2(Ljava/lang/Object;)V:
+        return:
+        insert-typecast Block.statement 0, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Map<String, Integer>
+    inner-type 3, 0: @annotation.A
+    inner-type 3, 1: @annotation.B
+        insert-typecast Block.statement 1, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable Object[][]
+    inner-type 0, 0: @annotation.A
+    inner-type 0, 0,0, 0: @annotation.B
+        insert-typecast Block.statement 2, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable int[][]
+    inner-type 0, 0: @annotation.A
+    inner-type 0, 0,0, 0: @annotation.B
+        insert-typecast Block.statement 3, ExpressionStatement.expression, Assignment.expression: @checkers.nullness.quals.Nullable List<? extends Object>
+    inner-type 3, 0: @annotation.A
+    inner-type 3, 0,2, 0: @annotation.B
+        insert-typecast Block.statement 4, ExpressionStatement.expression, Assignment.expression: int
+        insert-typecast Block.statement 5, ExpressionStatement.expression, Assignment.expression: Map<String, Integer>
+    inner-type 3, 0: @annotation.A
+    inner-type 3, 1: @annotation.B
+
diff --git a/annotation-file-utilities/tests/converted/ClassAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/ClassAnnotationParameter.jaif
new file mode 100644
index 0000000..f85e429
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ClassAnnotationParameter.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @TestAnnotation:
+    Class value
+
+package annotator.tests:
+class ClassAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value=java.lang.Object.class)
+        return:
+
diff --git a/annotation-file-utilities/tests/converted/ClassListAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/ClassListAnnotationParameter.jaif
new file mode 100644
index 0000000..e74fcba
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ClassListAnnotationParameter.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @TestAnnotation:
+    Class[] value
+
+package annotator.tests:
+class ClassListAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value={java.lang.Object.class,java.lang.String.class})
+        return:
+
diff --git a/annotation-file-utilities/tests/converted/ClassSimple.jaif b/annotation-file-utilities/tests/converted/ClassSimple.jaif
new file mode 100644
index 0000000..b657991
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ClassSimple.jaif
@@ -0,0 +1,6 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ClassSimple: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/ComplexLocationOne.jaif b/annotation-file-utilities/tests/converted/ComplexLocationOne.jaif
new file mode 100644
index 0000000..96e299d
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ComplexLocationOne.jaif
@@ -0,0 +1,32 @@
+package java.lang:
+annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ComplexLocationOne:
+
+    field field:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 1, ArrayType.type: @java.lang.D
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.C
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 1: @java.lang.E
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.B
+        insert-annotation Variable.type: @java.lang.A
+
+    field entries:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type, MemberSelect.expression, ParameterizedType.typeArgument 1, ArrayType.type: @java.lang.B
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type, MemberSelect.expression, ParameterizedType.typeArgument 0: @java.lang.A
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 1, ArrayType.type: @java.lang.E
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type: @java.lang.C
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.D
+
diff --git a/annotation-file-utilities/tests/converted/ComplexLocationTwo.jaif b/annotation-file-utilities/tests/converted/ComplexLocationTwo.jaif
new file mode 100644
index 0000000..0ab8111
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ComplexLocationTwo.jaif
@@ -0,0 +1,37 @@
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @F: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @E: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @H: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @G: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ComplexLocationTwo:
+
+    field field:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ArrayType.type, ArrayType.type, ArrayType.type: @java.lang.C
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ArrayType.type, ArrayType.type: @java.lang.F
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ArrayType.type: @java.lang.E
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.D
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @java.lang.H
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.B
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1: @java.lang.G
+        insert-annotation Variable.type: @java.lang.A
+
diff --git a/annotation-file-utilities/tests/converted/ConstructorParam.jaif b/annotation-file-utilities/tests/converted/ConstructorParam.jaif
new file mode 100644
index 0000000..172bcee
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ConstructorParam.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ConstructorParam:
+
+    method <init>(I)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/ConstructorParamMultiple.jaif b/annotation-file-utilities/tests/converted/ConstructorParamMultiple.jaif
new file mode 100644
index 0000000..c4729b3
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ConstructorParamMultiple.jaif
@@ -0,0 +1,16 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ConstructorParamMultiple:
+
+    method <init>(Ljava/lang/Object;Ljava/util/List;I)V:
+        return:
+        insert-annotation Method.parameter 1, Variable.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.Tainted
+        insert-annotation Method.parameter 1, Variable.type: @java.lang.Tainted
+        insert-annotation Method.parameter 2, Variable.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/ConstructorReceivers.jaif b/annotation-file-utilities/tests/converted/ConstructorReceivers.jaif
new file mode 100644
index 0000000..21a8b82
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ConstructorReceivers.jaif
@@ -0,0 +1,122 @@
+package checkers.taiting.quals:
+annotation @Tainted: @java.lang.annotation.Target(value={TYPE_USE,TYPE_PARAMETER}) @java.lang.annotation.Retention(value=RUNTIME)
+
+package annotation:
+annotation @A:
+
+package annotation:
+annotation @B:
+
+package annotation:
+annotation @C:
+
+package annotation:
+annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package annotator.tests:
+class ConstructorReceivers$C0:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method <init>(I)V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class ConstructorReceivers$C1:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+    method <init>(I)V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class ConstructorReceivers$C2:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C
+
+package annotator.tests:
+class ConstructorReceivers$C3:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C
+
+package annotator.tests:
+class ConstructorReceivers$P0$C4:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method <init>(I)V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class ConstructorReceivers$P0$C5:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted
+
+    method <init>(I)V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class ConstructorReceivers$P0$C6:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method <init>(Lannotator/tests/ConstructorReceivers$P0;)V:
+        return:
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class ConstructorReceivers$P0$C7:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted
+
+    method <init>(Lannotator/tests/ConstructorReceivers$P0;)V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+
+package annotator.tests:
+class ConstructorReceivers$P1$C8:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class ConstructorReceivers$P1$C9:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression, MemberSelect.expression, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @checkers.tainting.quals.Tainted
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+
diff --git a/annotation-file-utilities/tests/converted/ConstructorReturn.jaif b/annotation-file-utilities/tests/converted/ConstructorReturn.jaif
new file mode 100644
index 0000000..3d8f45d
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ConstructorReturn.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ConstructorReturn:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/Date.jaif b/annotation-file-utilities/tests/converted/Date.jaif
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/Date.jaif
diff --git a/annotation-file-utilities/tests/converted/DeclarationAnnotation.jaif b/annotation-file-utilities/tests/converted/DeclarationAnnotation.jaif
new file mode 100644
index 0000000..1f84e70
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/DeclarationAnnotation.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @SuppressWarnings:
+    String value
+
+package annotator.tests:
+class DeclarationAnnotation:
+
+    method foo()V: @java.lang.SuppressWarnings(value="string argument")
+        return:
+
diff --git a/annotation-file-utilities/tests/converted/DuplicateAnnotation.jaif b/annotation-file-utilities/tests/converted/DuplicateAnnotation.jaif
new file mode 100644
index 0000000..b3f7141
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/DuplicateAnnotation.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @SuppressWarnings:
+    String value
+
+package java.lang:
+annotation @Deprecated:
+
+package annotator.tests:
+class DuplicateAnnotation:
+
+    method m5()V: @java.lang.SuppressWarnings(value="E")
+        return:
+
+    method m6()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Deprecated
+
diff --git a/annotation-file-utilities/tests/converted/EnumAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/EnumAnnotationParameter.jaif
new file mode 100644
index 0000000..e1daf89
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/EnumAnnotationParameter.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @TestAnnotation:
+    enum java.lang.TestEnum value
+
+package annotator.tests:
+class EnumAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value=A)
+        return:
+
diff --git a/annotation-file-utilities/tests/converted/EnumListAnnotationParameter.jaif b/annotation-file-utilities/tests/converted/EnumListAnnotationParameter.jaif
new file mode 100644
index 0000000..1192c40
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/EnumListAnnotationParameter.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @TestAnnotation:
+    enum java.lang.TestEnum[] value
+
+package annotator.tests:
+class EnumListAnnotationParameter:
+
+    method foo()V: @java.lang.TestAnnotation(value={A,B})
+        return:
+
diff --git a/annotation-file-utilities/tests/converted/ExtImpl.jaif b/annotation-file-utilities/tests/converted/ExtImpl.jaif
new file mode 100644
index 0000000..c0dd1ca
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ExtImpl.jaif
@@ -0,0 +1,36 @@
+package :
+annotation @B:
+
+package :
+annotation @E:
+
+package :
+annotation @C:
+
+package :
+annotation @F:
+
+package :
+annotation @A:
+
+package :
+annotation @D:
+
+package :
+class ExtImpl$C1:
+    insert-annotation Class.bound -1, ParameterizedType.typeArgument 0: @B
+    insert-annotation Class.bound 0, ParameterizedType.typeArgument 0: @E
+    insert-annotation Class.bound -1, ParameterizedType.typeArgument 1: @C
+    insert-annotation Class.bound 0, ParameterizedType.typeArgument 1: @F
+    insert-annotation Class.bound -1: @A
+    insert-annotation Class.bound 0: @D
+
+package :
+class ExtImpl$C2:
+    insert-annotation Class.bound 0, ParameterizedType.typeArgument 0: @B
+    insert-annotation Class.bound 1, ParameterizedType.typeArgument 0: @E
+    insert-annotation Class.bound 0, ParameterizedType.typeArgument 1: @C
+    insert-annotation Class.bound 1, ParameterizedType.typeArgument 1: @F
+    insert-annotation Class.bound 0: @A
+    insert-annotation Class.bound 1: @D
+
diff --git a/annotation-file-utilities/tests/converted/FieldGenericArray.jaif b/annotation-file-utilities/tests/converted/FieldGenericArray.jaif
new file mode 100644
index 0000000..2e6d510
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/FieldGenericArray.jaif
@@ -0,0 +1,17 @@
+package :
+annotation @GenericType: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @InsideArray: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @OuterMostType: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldGenericArray:
+
+    field field:
+        insert-annotation Variable.type, ArrayType.type, ParameterizedType.typeArgument 0: @GenericType
+        insert-annotation Variable.type, ArrayType.type: @InsideArray
+        insert-annotation Variable.type: @OuterMostType
+
diff --git a/annotation-file-utilities/tests/converted/FieldMultiple.jaif b/annotation-file-utilities/tests/converted/FieldMultiple.jaif
new file mode 100644
index 0000000..48b3f89
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/FieldMultiple.jaif
@@ -0,0 +1,8 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldMultiple:
+
+    field d: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/FieldSimple.jaif b/annotation-file-utilities/tests/converted/FieldSimple.jaif
new file mode 100644
index 0000000..a4539f9
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/FieldSimple.jaif
@@ -0,0 +1,9 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldSimple:
+
+    field field:
+        insert-annotation Variable.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/FieldSimpleArray.jaif b/annotation-file-utilities/tests/converted/FieldSimpleArray.jaif
new file mode 100644
index 0000000..6bed71e
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/FieldSimpleArray.jaif
@@ -0,0 +1,13 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldSimpleArray:
+
+    field field:
+        insert-annotation Variable.type, ArrayType.type: @java.lang.UnderInitialization
+        insert-annotation Variable.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/FieldSimpleGeneric.jaif b/annotation-file-utilities/tests/converted/FieldSimpleGeneric.jaif
new file mode 100644
index 0000000..6b1f877
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/FieldSimpleGeneric.jaif
@@ -0,0 +1,13 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class FieldSimpleGeneric:
+
+    field field:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.Tainted
+        insert-annotation Variable.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/GenericAnnoBound.jaif b/annotation-file-utilities/tests/converted/GenericAnnoBound.jaif
new file mode 100644
index 0000000..b0d113f
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/GenericAnnoBound.jaif
@@ -0,0 +1,10 @@
+package :
+annotation @Bla:
+
+package :
+class GenericAnnoBound:
+
+    method <init>(LGenericAnnoBound;Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @Bla
+
diff --git a/annotation-file-utilities/tests/converted/GenericArg.jaif b/annotation-file-utilities/tests/converted/GenericArg.jaif
new file mode 100644
index 0000000..b607f78
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/GenericArg.jaif
@@ -0,0 +1,29 @@
+package :
+annotation @X:
+
+package :
+class GenericArg:
+
+    method mp(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @X
+
+    method mr()LX;:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @X
+
+    method foo(Ljava/lang/Number;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @X
+
+    method bar()LZ;:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @X
+
+package :
+class GenericArg$Tricky:
+
+    method argh(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @X
+
diff --git a/annotation-file-utilities/tests/converted/GenericCell.jaif b/annotation-file-utilities/tests/converted/GenericCell.jaif
new file mode 100644
index 0000000..40761c3
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/GenericCell.jaif
@@ -0,0 +1,27 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @PolyRaw: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @ThisUnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class GenericCell:
+
+    field internalList:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.ThisUnderInitialization
+        insert-annotation Variable.type: @java.lang.ThisUnderInitialization
+
+    method <init>(Ljava/util/List;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.UnderInitialization
+
+    method getList()Ljava/util/List;:
+        return:
+        insert-annotation Method.type, ParameterizedType.typeArgument 0: @java.lang.PolyRaw
+        insert-annotation Method.parameter -1: @java.lang.PolyRaw
+        insert-annotation Method.type: @java.lang.PolyRaw
+
diff --git a/annotation-file-utilities/tests/converted/GenericCellDoubled.jaif b/annotation-file-utilities/tests/converted/GenericCellDoubled.jaif
new file mode 100644
index 0000000..c08daec
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/GenericCellDoubled.jaif
@@ -0,0 +1,34 @@
+package :
+annotation @X:
+
+package :
+annotation @Y:
+
+package :
+class GenericCellDoubled:
+
+    field f:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @X
+
+    field g:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @X
+
+    field h:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @X
+
+    field i:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @X
+
+    field j1:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @X
+
+    field j2:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @X
+
+    field j3:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @X
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1: @Y @X
+
+    field k:
+        insert-annotation Variable.type: @Y
+
diff --git a/annotation-file-utilities/tests/converted/GenericMultiLevel.jaif b/annotation-file-utilities/tests/converted/GenericMultiLevel.jaif
new file mode 100644
index 0000000..1d6eebd
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/GenericMultiLevel.jaif
@@ -0,0 +1,25 @@
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @H: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @G: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class GenericMultiLevel:
+
+    field field:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0: @java.lang.C
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.typeArgument 0: @java.lang.H
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @java.lang.B
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1: @java.lang.G
+        insert-annotation Variable.type: @java.lang.A
+
diff --git a/annotation-file-utilities/tests/converted/ImplicitUpper.jaif b/annotation-file-utilities/tests/converted/ImplicitUpper.jaif
new file mode 100644
index 0000000..f6edd90
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ImplicitUpper.jaif
@@ -0,0 +1,14 @@
+package :
+annotation @A:
+
+package :
+annotation @B:
+
+package :
+class ExplicitUpper:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @A
+
+package :
+class ImplicitUpper:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @B
+
diff --git a/annotation-file-utilities/tests/converted/Initializers.jaif b/annotation-file-utilities/tests/converted/Initializers.jaif
new file mode 100644
index 0000000..ca158c1
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/Initializers.jaif
@@ -0,0 +1,27 @@
+package :
+annotation @X:
+
+package annotator.tests:
+class Initializers:
+
+    field s1:
+        insert-annotation Class.initializer 0, Block.statement 0, Variable.initializer, NewClass.identifier: @X
+
+    field s2:
+        insert-annotation Class.initializer 1, Block.statement 0, Variable.initializer, NewClass.identifier: @X
+
+    field o1:
+        insert-annotation Class.initializer 2, Block.statement 0, Variable.initializer, NewClass.identifier: @X
+
+    field o2:
+        insert-annotation Class.initializer 3, Block.statement 0, Variable.initializer, NewClass.identifier: @X
+
+package annotator.tests:
+class Initializers$MyEnum:
+
+    field s:
+        insert-annotation Class.initializer 0, Block.statement 0, Variable.initializer, NewClass.identifier: @X
+
+    field o:
+        insert-annotation Class.initializer 1, Block.statement 0, Variable.initializer, NewClass.identifier: @X
+
diff --git a/annotation-file-utilities/tests/converted/InnerClass.jaif b/annotation-file-utilities/tests/converted/InnerClass.jaif
new file mode 100644
index 0000000..e4c2856
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/InnerClass.jaif
@@ -0,0 +1,53 @@
+package :
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+annotation @D: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+class InnerClass:
+
+    method m()V:
+        return:
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @A(value=5)
+        insert-annotation Method.body, Block.statement 5, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @D(value=5)
+        insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @A(value=3)
+        insert-annotation Method.body, Block.statement 5, If.condition, Parenthesized.expression, InstanceOf.type: @D(value=3)
+        insert-annotation Method.body, Block.statement 4, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @D(value=2)
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @A(value=4)
+        insert-annotation Method.body, Block.statement 5, If.thenStatement, Block.statement 0, Variable.type: @D(value=4)
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @A(value=2)
+        insert-annotation Method.body, Block.statement 0, Variable.type: @A(value=1)
+
+package :
+class InnerClass$1Inner:
+
+    method m()V:
+        return:
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @B(value=5)
+        insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @B(value=3)
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @B(value=4)
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @B(value=2)
+        insert-annotation Method.body, Block.statement 0, Variable.type: @B(value=1)
+
+package :
+class InnerClass$1:
+
+    method m()V:
+        return:
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.initializer, TypeCast.type: @C(value=5)
+        insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @C(value=3)
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @C(value=4)
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @C(value=2)
+        insert-annotation Method.body, Block.statement 0, Variable.type: @C(value=1)
+
diff --git a/annotation-file-utilities/tests/converted/InnerClassAnonymous.jaif b/annotation-file-utilities/tests/converted/InnerClassAnonymous.jaif
new file mode 100644
index 0000000..12eda14
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/InnerClassAnonymous.jaif
@@ -0,0 +1,27 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @NonNegative: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @InnerlyAnnotated: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InnerClassAnonymous$1:
+
+    field serialVersionUID:
+        insert-annotation Variable.type: @java.lang.Tainted
+
+package annotator.tests:
+class InnerClassAnonymous$2:
+
+    field serialVersionUID:
+        insert-annotation Variable.type: @NonNegative
+
+package annotator.tests:
+class InnerClassAnonymous$3:
+
+    field serialVersionUID:
+        insert-annotation Variable.type: @InnerlyAnnotated
+
diff --git a/annotation-file-utilities/tests/converted/InnerClassSimple.jaif b/annotation-file-utilities/tests/converted/InnerClassSimple.jaif
new file mode 100644
index 0000000..cf14df0
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/InnerClassSimple.jaif
@@ -0,0 +1,12 @@
+package java.lang:
+annotation @DoesNotExist: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InnerClassSimple$ActualInnerClass: @java.lang.DoesNotExist @java.lang.Tainted
+
+    field d:
+        insert-annotation Variable.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/InnerReceivers.jaif b/annotation-file-utilities/tests/converted/InnerReceivers.jaif
new file mode 100644
index 0000000..1e84d83
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/InnerReceivers.jaif
@@ -0,0 +1,170 @@
+package :
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @D: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @D1: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @E: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+annotation @F: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @F1: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @G: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+annotation @H: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @I: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @J: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @K: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @I1: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+annotation @L: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @M: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @N: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @O: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InnerReceivers$1$Inner:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @A
+
+    method m1()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @B
+
+package annotator.tests:
+class InnerReceivers:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @C
+
+    method m1()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @D
+
+    method m2()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @D1
+
+package annotator.tests:
+class InnerReceivers$Inner1:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @E(value=2)
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @E(value=3)
+        insert-annotation Method.parameter -1, ParameterizedType.type, MemberSelect.expression: @E(value=1)
+        insert-annotation Method.parameter -1: @E(value=0)
+
+    method m1()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @F
+
+    method m2()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type, MemberSelect.expression: @F1
+
+package annotator.tests:
+class InnerReceivers$Inner1$Inner2:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.type, MemberSelect.expression: @G(value=4)
+        insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.typeArgument 0: @G(value=2)
+        insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.typeArgument 1: @G(value=3)
+        insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression: @G(value=1)
+        insert-annotation Method.parameter -1: @G(value=0)
+
+    method m1()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression, ParameterizedType.type, MemberSelect.expression: @H
+
+package annotator.tests:
+class InnerReceivers$StaticInner1:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @I
+
+    method m1()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @J
+
+    method m2()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @K
+
+package annotator.tests:
+class InnerReceivers$StaticInner3:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @I1(value=1)
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @I1(value=2)
+        insert-annotation Method.parameter -1: @I1(value=0)
+
+    method m1()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type: @J
+
+    method m2()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.type: @K
+
+package annotator.tests:
+class Outer$StaticInner2:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @L
+
+    method m1()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @M
+
+    method m2()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @N
+
+package annotator.tests:
+class Outer$StaticInner2$StaticInner3:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @O
+
diff --git a/annotation-file-utilities/tests/converted/InnerTypeResolution.jaif b/annotation-file-utilities/tests/converted/InnerTypeResolution.jaif
new file mode 100644
index 0000000..8359ecd
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/InnerTypeResolution.jaif
@@ -0,0 +1,13 @@
+package java.lang:
+annotation @Deprecated:
+
+package java.lang:
+annotation @Tainted:
+
+package annotator.tests:
+class InnerTypeResolution:
+
+    method method01(Ljava/util/Map;)Ljava/util/Map$Entry;: @java.lang.Deprecated
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/InstanceOfMultiple.jaif b/annotation-file-utilities/tests/converted/InstanceOfMultiple.jaif
new file mode 100644
index 0000000..1a737ca
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/InstanceOfMultiple.jaif
@@ -0,0 +1,16 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InstanceOfMultiple:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, If.thenStatement, Block.statement 0, If.thenStatement, Block.statement 0, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.UnderInitialization
+        insert-annotation Method.body, Block.statement 0, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.Tainted
+        insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.UnderInitialization
+        insert-annotation Method.body, Block.statement 1, If.condition, Parenthesized.expression, InstanceOf.type, ParameterizedType.typeArgument 0: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/InstanceOfSimple.jaif b/annotation-file-utilities/tests/converted/InstanceOfSimple.jaif
new file mode 100644
index 0000000..921560b
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/InstanceOfSimple.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class InstanceOfSimple:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, If.condition, Parenthesized.expression, InstanceOf.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/IntCell.jaif b/annotation-file-utilities/tests/converted/IntCell.jaif
new file mode 100644
index 0000000..f9c17f3
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/IntCell.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class IntCell:
+
+    method set(I)V:
+        return:
+        insert-annotation Method.parameter -1: @java.lang.UnderInitialization
+
+    method get()I:
+        return:
+        insert-annotation Method.parameter -1: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/LocalArray.jaif b/annotation-file-utilities/tests/converted/LocalArray.jaif
new file mode 100644
index 0000000..e333462
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalArray.jaif
@@ -0,0 +1,14 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalArray:
+
+    method foo()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type, ArrayType.type: @java.lang.UnderInitialization
+        insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/LocalClass.jaif b/annotation-file-utilities/tests/converted/LocalClass.jaif
new file mode 100644
index 0000000..313cf84
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalClass.jaif
@@ -0,0 +1,88 @@
+package :
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+class LocalClass:
+
+    field f:
+        insert-annotation Variable.type: @A(value=1)
+
+package :
+class LocalClass$1InnerLocalClass:
+
+    field f:
+        insert-annotation Variable.type: @A(value=2)
+
+package :
+class LocalClass$1:
+
+    field f:
+        insert-annotation Variable.type: @A(value=3)
+
+package :
+class LocalClass$1$Test:
+
+    field f:
+        insert-annotation Variable.type: @A(value=4)
+
+package :
+class LocalClass$1$Test$1:
+
+    field f:
+        insert-annotation Variable.type: @A(value=41)
+
+package :
+class LocalClass$1$Test$2:
+
+    field f:
+        insert-annotation Variable.type: @A(value=42)
+
+package :
+class LocalClass$2:
+
+    field f:
+        insert-annotation Variable.type: @A(value=31)
+
+package :
+class LocalClass$2InnerLocalClass:
+
+    field f:
+        insert-annotation Variable.type: @A(value=5)
+
+package :
+class LocalClass$2InnerLocalClass$Inner:
+
+    field f:
+        insert-annotation Variable.type: @A(value=6)
+
+package :
+class LocalClass$2InnerLocalClass$Inner$1:
+
+    field f:
+        insert-annotation Variable.type: @A(value=7)
+
+package :
+class LocalClass$2InnerLocalClass$Inner$2:
+
+    field f:
+        insert-annotation Variable.type: @A(value=71)
+
+package :
+class LocalClass$2InnerLocalClass$1OuterLocalClass:
+
+    field f:
+        insert-annotation Variable.type: @A(value=10)
+
+package :
+class LocalClass$1OuterLocalClass:
+
+    field f:
+        insert-annotation Variable.type: @A(value=8)
+
+package :
+class LocalClass$1OuterLocalClass$1InnerLocalClass:
+
+    field f:
+        insert-annotation Variable.type: @A(value=9)
+
diff --git a/annotation-file-utilities/tests/converted/LocalGeneric.jaif b/annotation-file-utilities/tests/converted/LocalGeneric.jaif
new file mode 100644
index 0000000..aa53ec4
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalGeneric.jaif
@@ -0,0 +1,14 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalGeneric:
+
+    method foo()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type, ParameterizedType.typeArgument 0: @java.lang.Tainted
+        insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/LocalGenericShadow.jaif b/annotation-file-utilities/tests/converted/LocalGenericShadow.jaif
new file mode 100644
index 0000000..4ecd93d
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalGenericShadow.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalGenericShadow:
+
+    method method()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/LocalMultiple.jaif b/annotation-file-utilities/tests/converted/LocalMultiple.jaif
new file mode 100644
index 0000000..2f16436
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalMultiple.jaif
@@ -0,0 +1,14 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalMultiple:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.Tainted
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/LocalMultipleManyMethods.jaif b/annotation-file-utilities/tests/converted/LocalMultipleManyMethods.jaif
new file mode 100644
index 0000000..46fb454
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalMultipleManyMethods.jaif
@@ -0,0 +1,25 @@
+package java.lang:
+annotation @B: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalMultipleManyMethods:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.B
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.C
+
+    method foo([Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.D
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.A
+
diff --git a/annotation-file-utilities/tests/converted/LocalMultipleManyMethodsShifted.jaif b/annotation-file-utilities/tests/converted/LocalMultipleManyMethodsShifted.jaif
new file mode 100644
index 0000000..3d1ce21
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalMultipleManyMethodsShifted.jaif
@@ -0,0 +1,21 @@
+package java.lang:
+annotation @C: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @A: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @D: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalMultipleManyMethodsShifted:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 3, If.thenStatement, Block.statement 0, Variable.type: @java.lang.C
+
+    method foo([Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 1, If.elseStatement, Block.statement 0, Variable.type: @java.lang.A
+        insert-annotation Method.body, Block.statement 1, If.thenStatement, Block.statement 0, Variable.type: @java.lang.D
+
diff --git a/annotation-file-utilities/tests/converted/LocalSimple.jaif b/annotation-file-utilities/tests/converted/LocalSimple.jaif
new file mode 100644
index 0000000..a329cf0
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalSimple.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalSimple:
+
+    method foo()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/LocalSimpleMultiple.jaif b/annotation-file-utilities/tests/converted/LocalSimpleMultiple.jaif
new file mode 100644
index 0000000..391bc2f
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/LocalSimpleMultiple.jaif
@@ -0,0 +1,18 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalSimpleMultiple:
+
+    method foo()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.UnderInitialization
+        insert-annotation Method.body, Block.statement 2, Variable.type: @java.lang.Tainted
+
+    method bar(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/MatchReturnValue.jaif b/annotation-file-utilities/tests/converted/MatchReturnValue.jaif
new file mode 100644
index 0000000..39faa3a
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/MatchReturnValue.jaif
@@ -0,0 +1,9 @@
+package java.lang:
+annotation @A:
+
+package annotator.tests:
+class MatchReturnValue:
+
+    method clone()Lannotator/tests/MatchReturnValue;: @java.lang.A
+        return:
+
diff --git a/annotation-file-utilities/tests/converted/MemberSelectTypes.jaif b/annotation-file-utilities/tests/converted/MemberSelectTypes.jaif
new file mode 100644
index 0000000..55e9972
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/MemberSelectTypes.jaif
@@ -0,0 +1,136 @@
+package :
+annotation @Anno: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package :
+annotation @S: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @P: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @Q: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @R: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @O: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @N: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @M: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @L: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @C: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @F: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @D: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @E: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @I: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @G: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @H: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @K: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @J: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @B: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+class MemberSelectTypes:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @Anno(value=0)
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 1: @Anno(value=1)
+    insert-annotation Class.bound -1: @Anno(value=2)
+    insert-annotation Class.bound 0: @Anno(value=3)
+
+    field o:
+        insert-annotation Variable.type: @C
+
+    field m1:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.type: @F
+        insert-annotation Variable.type, ParameterizedType.type: @D
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @E
+
+    field m2:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1, ParameterizedType.type: @I
+        insert-annotation Variable.type, ParameterizedType.type: @G
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0: @H
+
+    field m3:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, MemberSelect.expression: @H
+        insert-annotation Variable.type, ParameterizedType.typeArgument 1: @I
+
+    field m4:
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ParameterizedType.type: @K
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.typeArgument 0, ParameterizedType.type: @I
+        insert-annotation Variable.type, ParameterizedType.typeArgument 0, ParameterizedType.type: @H
+        insert-annotation Variable.type, ParameterizedType.type: @G
+
+    field i:
+        insert-annotation Variable.type, MemberSelect.expression: @J
+
+    field s:
+        insert-annotation Variable.type: @K
+
+    method m(Ljava/lang/Object;LMemberSelectTypes$Inner;LMemberSelectTypes$StaticInner;)Ljava/lang/Object;:
+        return:
+        insert-annotation Method.body, Block.statement 10, ExpressionStatement.expression, Assignment.expression, InstanceOf.type, MemberSelect.expression: @S
+        insert-annotation Method.body, Block.statement 4, ExpressionStatement.expression, Assignment.expression, NewClass.identifier, MemberSelect.expression: @P
+        insert-annotation Method.body, Block.statement 7, Variable.type, ArrayType.type, MemberSelect.expression: @Q
+        insert-annotation Method.body, Block.statement 7, Variable.initializer, NewArray.type 1: @R
+        insert-annotation Method.body, Block.statement 1, Variable.initializer, TypeCast.type, MemberSelect.expression: @O
+        insert-annotation Method.body, Block.statement 11, ExpressionStatement.expression, Assignment.expression, InstanceOf.type: @S
+        insert-annotation Method.body, Block.statement 3, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @P
+        insert-annotation Method.body, Block.statement 5, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @P
+        insert-annotation Method.body, Block.statement 6, Variable.type, ArrayType.type: @Q
+        insert-annotation Method.body, Block.statement 8, Variable.type, ArrayType.type: @Q
+        insert-annotation Method.body, Block.statement 1, Variable.type, MemberSelect.expression: @N
+        insert-annotation Method.parameter 1, Variable.type, MemberSelect.expression: @M
+        insert-annotation Method.body, Block.statement 9, Variable.initializer, InstanceOf.type: @S
+        insert-annotation Method.body, Block.statement 6, Variable.initializer, NewArray.type 1: @R
+        insert-annotation Method.body, Block.statement 8, Variable.initializer, NewArray.type 1: @R
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @O
+        insert-annotation Method.body, Block.statement 2, Variable.initializer, TypeCast.type: @O
+        insert-annotation Method.body, Block.statement 0, Variable.type: @N
+        insert-annotation Method.body, Block.statement 2, Variable.type: @N
+        insert-annotation Method.parameter 0, Variable.type: @M
+        insert-annotation Method.parameter 2, Variable.type: @M
+        insert-annotation Method.type: @L
+
+package :
+class MemberSelectTypes$Inner:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, MemberSelect.expression: @A
+
+package :
+class MemberSelectTypes$StaticInner:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @B
+
diff --git a/annotation-file-utilities/tests/converted/MethodCompoundType.jaif b/annotation-file-utilities/tests/converted/MethodCompoundType.jaif
new file mode 100644
index 0000000..4de003b
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/MethodCompoundType.jaif
@@ -0,0 +1,10 @@
+package :
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+class MethodCompoundType:
+
+    method m()[Ljava/util/Map;:
+        return:
+        insert-annotation Method.type: @A
+
diff --git a/annotation-file-utilities/tests/converted/MethodMultiple.jaif b/annotation-file-utilities/tests/converted/MethodMultiple.jaif
new file mode 100644
index 0000000..a12644d
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/MethodMultiple.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class MethodMultiple:
+
+    method foo(Ljava/lang/String;)Ljava/lang/String;:
+        return:
+        insert-annotation Method.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/NewArray.jaif b/annotation-file-utilities/tests/converted/NewArray.jaif
new file mode 100644
index 0000000..962975d
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NewArray.jaif
@@ -0,0 +1,85 @@
+package :
+annotation @X:
+
+package :
+annotation @B:
+
+package :
+annotation @A:
+
+package :
+annotation @Z:
+
+package :
+annotation @C:
+
+package :
+annotation @D:
+
+package :
+annotation @E:
+
+package :
+class NewArray:
+
+    field names01:
+        insert-annotation Variable.initializer, NewArray.type 1: @X
+
+    field names02:
+        insert-annotation Variable.initializer, NewArray.type 1: @X
+
+    field names03:
+        insert-annotation Variable.initializer, NewArray.type 1: @X
+
+    field table1:
+        insert-annotation Variable.initializer, NewArray.type 0: @B
+        insert-annotation Variable.initializer, NewArray.type 1: @A
+
+    field table2:
+        insert-annotation Variable.initializer, NewArray.type 0: @B
+        insert-annotation Variable.initializer, NewArray.type 1: @A
+
+    field names0:
+        insert-annotation Variable.initializer, NewArray.type 0: @Z
+
+    field names1:
+        insert-annotation Variable.initializer, NewArray.type 1: @A
+
+    field names2:
+        insert-annotation Variable.initializer, NewArray.type 2: @B
+
+    field names3:
+        insert-annotation Variable.initializer, NewArray.type 3: @C
+
+    field names4:
+        insert-annotation Variable.initializer, NewArray.type 4: @D
+
+    field names5:
+        insert-annotation Variable.initializer, NewArray.type 5: @E
+
+    field names10:
+        insert-annotation Variable.initializer, NewArray.type 0: @Z
+
+    field names11:
+        insert-annotation Variable.initializer, NewArray.type 1: @A
+
+    field names12:
+        insert-annotation Variable.initializer, NewArray.type 2: @B
+
+    field names13:
+        insert-annotation Variable.initializer, NewArray.type 3: @C
+
+    field names14:
+        insert-annotation Variable.initializer, NewArray.type 4: @D
+
+    field names15:
+        insert-annotation Variable.initializer, NewArray.type 5: @E
+
+    field lists:
+        insert-annotation Variable.type: @B
+        insert-annotation Variable.type, ArrayType.type: @Z
+        insert-annotation Variable.type, ArrayType.type, ParameterizedType.typeArgument 0: @A
+        insert-annotation Variable.initializer, NewArray.type 0: @E
+        insert-annotation Variable.initializer, NewArray.type 1: @C
+        insert-annotation Variable.initializer, NewArray.type 1, ParameterizedType.typeArgument 0: @D
+
diff --git a/annotation-file-utilities/tests/converted/NewGeneric.jaif b/annotation-file-utilities/tests/converted/NewGeneric.jaif
new file mode 100644
index 0000000..211ed3a
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NewGeneric.jaif
@@ -0,0 +1,15 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class NewGeneric:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.Tainted
+        insert-annotation Method.body, Block.statement 1, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/NewInAnnotatedVariable.jaif b/annotation-file-utilities/tests/converted/NewInAnnotatedVariable.jaif
new file mode 100644
index 0000000..9eacec7
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NewInAnnotatedVariable.jaif
@@ -0,0 +1,18 @@
+package :
+annotation @NonNull:
+
+package :
+class NewInAnnotatedVariable:
+
+    field b1:
+        insert-annotation Variable.initializer, NewClass.identifier: @NonNull
+
+    field b2:
+        insert-annotation Variable.initializer, NewClass.identifier, AnnotatedType.underlyingType: @NonNull
+
+    field b4:
+        insert-annotation Variable.type, ArrayType.type: @NonNull
+        insert-annotation Variable.initializer, NewArray.type 0: @NonNull
+        insert-annotation Variable.initializer, NewArray.type 1: @NonNull
+        insert-annotation Variable.type: @NonNull
+
diff --git a/annotation-file-utilities/tests/converted/NewMultiple.jaif b/annotation-file-utilities/tests/converted/NewMultiple.jaif
new file mode 100644
index 0000000..7109c41
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NewMultiple.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class NewMultiple:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.Tainted
+
+    method bar(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/NewPackage.jaif b/annotation-file-utilities/tests/converted/NewPackage.jaif
new file mode 100644
index 0000000..fd0697a
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NewPackage.jaif
@@ -0,0 +1,10 @@
+package :
+annotation @X:
+
+package :
+class NewPackage:
+
+    method <init>()V:
+        return:
+        insert-annotation Method.body, Block.statement 1, Variable.initializer, NewClass.identifier: @X
+
diff --git a/annotation-file-utilities/tests/converted/NewSimple.jaif b/annotation-file-utilities/tests/converted/NewSimple.jaif
new file mode 100644
index 0000000..ee97c85
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NewSimple.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class NewSimple:
+
+    method foo()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, NewClass.identifier: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/NonClass.jaif b/annotation-file-utilities/tests/converted/NonClass.jaif
new file mode 100644
index 0000000..fc2d2d4
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NonClass.jaif
@@ -0,0 +1,53 @@
+package :
+annotation @X:
+
+package :
+annotation @V:
+    int value
+
+package :
+class NonClass$A:
+
+    method m(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @X
+
+package :
+class NonClass$B:
+
+    method m()V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.type: @X
+
+package :
+class NonClass$C:
+
+    method value()Ljava/lang/String;:
+        return:
+        insert-annotation Method.type: @X
+
+package :
+class NonClass$E:
+
+    field D:
+        insert-annotation Variable.initializer, NewClass.argument 0, TypeCast.type: @V(value=1)
+        insert-annotation Variable.initializer, NewClass.identifier: @V(value=2)
+        insert-annotation Variable.type: @V(value=3)
+
+package :
+class NonClass$E:
+
+    field a:
+        insert-annotation Variable.type: @X
+
+package :
+class NonClass$E:
+
+    method <init>(Ljava/lang/String;)V:
+        insert-annotation Method.body, Block.statement 1, ExpressionStatement.expression, Assignment.expression, NewClass.identifier: @V(value=0)
+
+package :
+class NonClass$I:
+    insert-annotation Class.bound 0: @X
+    insert-annotation Class.bound 0, ParameterizedType.typeArgument 0: @X
+
diff --git a/annotation-file-utilities/tests/converted/NonStandardSpacing.jaif b/annotation-file-utilities/tests/converted/NonStandardSpacing.jaif
new file mode 100644
index 0000000..805bf7f
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/NonStandardSpacing.jaif
@@ -0,0 +1,34 @@
+package :
+annotation @Anno:
+
+package :
+class NonStandardSpacing:
+
+    method m1()V:
+        return:
+        insert-typecast Block.statement 0, Variable.initializer, Binary.leftOperand: @Anno int
+        insert-typecast Block.statement 1, Variable.initializer, Binary.leftOperand: @Anno int
+        insert-typecast Block.statement 2, Variable.initializer, Binary.leftOperand: @Anno int
+        insert-typecast Block.statement 3, Variable.initializer, Binary.leftOperand: @Anno int
+        insert-typecast Block.statement 4, Variable.initializer, Binary.leftOperand: @Anno int
+
+    method m2()V:
+        return:
+        insert-annotation Method.parameter -1: @Anno
+
+    method m3()V:
+        return:
+        insert-annotation Method.parameter -1: @Anno
+
+    method m4()V:
+        return:
+        insert-annotation Method.parameter -1: @Anno
+
+    method m5()V:
+        return:
+        insert-annotation Method.parameter -1: @Anno
+
+    method m7()V:
+        return:
+        insert-annotation Method.parameter -1: @Anno
+
diff --git a/annotation-file-utilities/tests/converted/Package.jaif b/annotation-file-utilities/tests/converted/Package.jaif
new file mode 100644
index 0000000..8691233
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/Package.jaif
@@ -0,0 +1,9 @@
+package :
+annotation @A: @java.lang.annotation.Target(value={TYPE_USE})
+
+package pkg.name.here:
+class Package:
+
+    field o:
+        insert-annotation Variable.type: @A
+
diff --git a/annotation-file-utilities/tests/converted/ParseType.jaif b/annotation-file-utilities/tests/converted/ParseType.jaif
new file mode 100644
index 0000000..00d37cf
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ParseType.jaif
@@ -0,0 +1,21 @@
+package :
+annotation @A:
+
+package :
+class ParseType:
+
+    method m(Ljava/lang/Object;)V:
+        return:
+        insert-typecast Block.statement 0, Variable.initializer: @A String
+        insert-typecast Block.statement 1, Variable.initializer: @A Map<String, Integer>
+        insert-typecast Block.statement 2, Variable.initializer: @A String[]
+        insert-typecast Block.statement 3, Variable.initializer: @A String[][]
+        insert-typecast Block.statement 4, Variable.initializer: @A Map<String[], Integer>
+        insert-typecast Block.statement 5, Variable.initializer: @A Map<String[][], Integer>
+        insert-typecast Block.statement 6, Variable.initializer: @A Map<?, ?>
+        insert-typecast Block.statement 7, Variable.initializer: @A Map<? extends String, ? super List<Integer>>
+        insert-typecast Block.statement 8, Variable.initializer: @A Map<List<String>, List<String>[]>
+        insert-typecast Block.statement 9, Variable.initializer: @A Map.Entry<String, Integer>
+        insert-typecast Block.statement 10, Variable.initializer: @A Map.Entry<String, Integer>[]
+        insert-typecast Block.statement 11, Variable.initializer: @A ParseType<String>.Inner<Integer>[]
+
diff --git a/annotation-file-utilities/tests/converted/ReceiverWithThrows.jaif b/annotation-file-utilities/tests/converted/ReceiverWithThrows.jaif
new file mode 100644
index 0000000..b56c968
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/ReceiverWithThrows.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class ReceiverWithThrows:
+
+    method foo()V:
+        return:
+        insert-annotation Method.parameter -1: @java.lang.UnderInitialization
+
+    method bar()V:
+        return:
+        insert-annotation Method.parameter -1: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/Receivers.jaif b/annotation-file-utilities/tests/converted/Receivers.jaif
new file mode 100644
index 0000000..754d6cb
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/Receivers.jaif
@@ -0,0 +1,136 @@
+package checkers.tainting.quals:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotation:
+annotation @Inner: @java.lang.annotation.Target(value={TYPE_USE})
+    int value
+
+package annotation:
+annotation @A:
+
+package annotation:
+annotation @B:
+
+package annotation:
+annotation @C:
+
+package annotator.tests:
+class Receivers:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method spaces()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method m(I)V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method spaces(I)V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method m(Ljava/lang/String;)V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers2:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+    method spaces()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+    method m(I)V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+    method spaces(I)V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers3:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+    method m(I)V:
+        return:
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers4:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+    method m(I)V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers5:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers6:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers7:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 0: @annotation.Inner(value=0)
+        insert-annotation Method.parameter -1, ParameterizedType.typeArgument 1: @annotation.Inner(value=1)
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers8:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+package annotator.tests:
+class Receivers9:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1: @checkers.tainting.quals.Tainted @annotation.A @annotation.B @annotation.C
+
+package annotator.tests:
+class Receivers10:
+
+    method m()V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.A
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.B
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
+    method m(Lannotator/tests/Receivers10;)V:
+        return:
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 0: @annotation.A
+        insert-annotation Method.parameter -1, Variable.type, ParameterizedType.typeArgument 1: @annotation.B
+        insert-annotation Method.parameter -1, Variable.type: @checkers.tainting.quals.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/TypeCastGeneric.jaif b/annotation-file-utilities/tests/converted/TypeCastGeneric.jaif
new file mode 100644
index 0000000..14468ea
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/TypeCastGeneric.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeCastGeneric:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type, ParameterizedType.typeArgument 0: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/TypeCastMultiple.jaif b/annotation-file-utilities/tests/converted/TypeCastMultiple.jaif
new file mode 100644
index 0000000..fffaf2e
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/TypeCastMultiple.jaif
@@ -0,0 +1,15 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeCastMultiple:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 2, Variable.initializer, TypeCast.expression, Parenthesized.expression, TypeCast.type: @java.lang.Tainted
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization
+        insert-annotation Method.body, Block.statement 1, Variable.initializer, TypeCast.type: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/TypeCastSimple.jaif b/annotation-file-utilities/tests/converted/TypeCastSimple.jaif
new file mode 100644
index 0000000..d040dd6
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/TypeCastSimple.jaif
@@ -0,0 +1,10 @@
+package java.lang:
+annotation @UnderInitialization: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeCastSimple:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.body, Block.statement 0, Variable.initializer, TypeCast.type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/converted/TypeParamMethod.jaif b/annotation-file-utilities/tests/converted/TypeParamMethod.jaif
new file mode 100644
index 0000000..655d6e3
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/TypeParamMethod.jaif
@@ -0,0 +1,22 @@
+package java.lang:
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeParamMethod:
+
+    method foo(Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.typeParameter 0: @java.lang.Tainted
+
+    method foo2(Ljava/util/Date;)V:
+        return:
+        insert-annotation Method.typeParameter 0: @java.lang.Tainted
+
+    method foo(Ljava/lang/Object;Ljava/lang/Object;)V:
+        return:
+        insert-annotation Method.typeParameter 1: @java.lang.Tainted
+
+    method foo2(Ljava/util/Date;Ljava/util/Date;)V:
+        return:
+        insert-annotation Method.typeParameter 1: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/converted/Varargs.jaif b/annotation-file-utilities/tests/converted/Varargs.jaif
new file mode 100644
index 0000000..a1a3cbc
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/Varargs.jaif
@@ -0,0 +1,25 @@
+package java.lang:
+annotation @Nullable:
+
+package annotator.tests:
+class Varargs:
+
+    method m1([Ljava/lang/String;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable
+
+    method m2([Ljava/lang/String;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable
+
+    method m3([Ljava/lang/String;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable
+
+    method m4([Ljava/lang/Comparable;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type, ArrayType.type, ParameterizedType.typeArgument 0: @java.lang.Nullable
+        insert-annotation Method.parameter 0, Variable.type, ArrayType.type: @java.lang.Nullable
+        insert-annotation Method.parameter 0, Variable.type: @java.lang.Nullable
+
diff --git a/annotation-file-utilities/tests/converted/WildcardAnnoBound.jaif b/annotation-file-utilities/tests/converted/WildcardAnnoBound.jaif
new file mode 100644
index 0000000..85be198
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/WildcardAnnoBound.jaif
@@ -0,0 +1,58 @@
+package :
+annotation @Bla:
+
+package :
+annotation @A:
+
+package :
+annotation @D:
+
+package :
+annotation @H:
+
+package :
+annotation @E:
+
+package :
+annotation @G:
+
+package :
+annotation @I:
+
+package :
+annotation @B:
+
+package :
+annotation @C:
+
+package :
+annotation @F:
+
+package :
+annotation @J:
+
+package :
+class WildcardAnnoBound:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0, ParameterizedType.typeArgument 0, Wildcard.bound: @Bla
+
+    method <init>(LWildcardAnnoBound;Ljava/util/List;)V:
+        return:
+        insert-annotation Method.parameter 0, Variable.type: @Bla
+
+package :
+class NoBound:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @A
+
+package :
+class Bounds:
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0, ArrayType.type: @D
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0, Wildcard.bound: @H
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 1, ParameterizedType.typeArgument 0: @E
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 0: @G
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2, ParameterizedType.typeArgument 1: @I
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 0: @B
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 1: @C
+    insert-annotation Class.typeParameter 0, TypeParameter.bound 2: @F
+    insert-annotation Class.typeParameter 0: @A
+    insert-annotation Class.typeParameter 1: @J
+
diff --git a/annotation-file-utilities/tests/converted/package-info.jaif b/annotation-file-utilities/tests/converted/package-info.jaif
new file mode 100644
index 0000000..d8d107c
--- /dev/null
+++ b/annotation-file-utilities/tests/converted/package-info.jaif
@@ -0,0 +1,4 @@
+package java.lang:
+annotation @OnThePackage: @java.lang.annotation.Retention(value=RUNTIME)
+
+package annotator.tests: @java.lang.OnThePackage
diff --git a/annotation-file-utilities/tests/package-info.goal b/annotation-file-utilities/tests/package-info.goal
new file mode 100644
index 0000000..b016058
--- /dev/null
+++ b/annotation-file-utilities/tests/package-info.goal
@@ -0,0 +1,2 @@
+@java.lang.OnThePackage
+package annotator.tests;
diff --git a/annotation-file-utilities/tests/package-info.jaif b/annotation-file-utilities/tests/package-info.jaif
new file mode 100644
index 0000000..e649e6c
--- /dev/null
+++ b/annotation-file-utilities/tests/package-info.jaif
@@ -0,0 +1,4 @@
+package java.lang:
+annotation @OnThePackage: @Retention(value=RUNTIME)
+
+package annotator.tests: @java.lang.OnThePackage
diff --git a/annotation-file-utilities/tests/package-info.java b/annotation-file-utilities/tests/package-info.java
new file mode 100644
index 0000000..4efcdd1
--- /dev/null
+++ b/annotation-file-utilities/tests/package-info.java
@@ -0,0 +1 @@
+package annotator.tests;
diff --git a/annotation-file-utilities/tests/source-extension/ClassTV3.goal b/annotation-file-utilities/tests/source-extension/ClassTV3.goal
new file mode 100644
index 0000000..837414a
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/ClassTV3.goal
@@ -0,0 +1,5 @@
+public class ClassTV3<X extends @GUT.quals.Peer Object, Y extends @GUT.quals.Rep Object, Z extends @GUT.quals.Any Object> {
+  X f;
+  Y g;
+  Z h;
+}
diff --git a/annotation-file-utilities/tests/source-extension/ClassTV3.jaif b/annotation-file-utilities/tests/source-extension/ClassTV3.jaif
new file mode 100644
index 0000000..01ad5eb
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/ClassTV3.jaif
@@ -0,0 +1,16 @@
+package GUT.quals:
+annotation @Peer:
+annotation @Rep:
+annotation @Any:
+
+package:
+class ClassTV3:
+bound 0 & 0: @GUT.quals.Peer
+
+package:
+class ClassTV3:
+bound 1 & 0: @GUT.quals.Rep
+
+package:
+class ClassTV3:
+bound 2 & 0: @GUT.quals.Any
\ No newline at end of file
diff --git a/annotation-file-utilities/tests/source-extension/ClassTV3.java b/annotation-file-utilities/tests/source-extension/ClassTV3.java
new file mode 100644
index 0000000..0e25137
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/ClassTV3.java
@@ -0,0 +1,5 @@
+public class ClassTV3<X, Y, Z> {
+  X f;
+  Y g;
+  Z h;
+}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNew.goal b/annotation-file-utilities/tests/source-extension/FieldNew.goal
new file mode 100644
index 0000000..79ff204
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/FieldNew.goal
@@ -0,0 +1,6 @@
+package annotator.tests;
+
+public class FieldNew {
+  @GUT.quals.Peer
+  Object f = new @GUT.quals.Rep FieldNew();
+}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNew.jaif b/annotation-file-utilities/tests/source-extension/FieldNew.jaif
new file mode 100644
index 0000000..61f676c
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/FieldNew.jaif
@@ -0,0 +1,14 @@
+package GUT.quals:
+annotation @Peer:
+annotation @Rep:
+
+package annotator.tests:
+class FieldNew:
+field f:
+type: @GUT.quals.Peer
+
+package annotator.tests:
+class FieldNew:
+field f:
+new *0: @GUT.quals.Rep
+
diff --git a/annotation-file-utilities/tests/source-extension/FieldNew.java b/annotation-file-utilities/tests/source-extension/FieldNew.java
new file mode 100644
index 0000000..3859777
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/FieldNew.java
@@ -0,0 +1,5 @@
+package annotator.tests;
+
+public class FieldNew {
+  Object f = new FieldNew();
+}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNewComplex.goal b/annotation-file-utilities/tests/source-extension/FieldNewComplex.goal
new file mode 100644
index 0000000..e1fe8f4
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/FieldNewComplex.goal
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+public class FieldNewComplex {
+  @GUT.quals.Rep
+  FieldNewComplex m(@GUT.quals.Peer FieldNewComplex a, @GUT.quals.Peer FieldNewComplex b, @GUT.quals.Rep FieldNewComplex c) {
+    return null;
+  }
+
+  @GUT.quals.Rep
+  FieldNewComplex f = m(new @GUT.quals.Peer FieldNewComplex(), new @GUT.quals.Peer FieldNewComplex(), new @GUT.quals.Rep FieldNewComplex());
+}
diff --git a/annotation-file-utilities/tests/source-extension/FieldNewComplex.jaif b/annotation-file-utilities/tests/source-extension/FieldNewComplex.jaif
new file mode 100644
index 0000000..1c0694f
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/FieldNewComplex.jaif
@@ -0,0 +1,47 @@
+package GUT.quals:
+annotation @Rep:
+annotation @Peer:
+annotation @Any:
+
+package annotator.tests:
+class FieldNewComplex:
+field f:
+new *1: @GUT.quals.Peer
+
+package annotator.tests:
+class FieldNewComplex:
+field f:
+new *2: @GUT.quals.Rep
+
+package annotator.tests:
+class FieldNewComplex:
+method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;:
+parameter 1:
+type: @GUT.quals.Peer
+
+package annotator.tests:
+class FieldNewComplex:
+field f:
+new *0: @GUT.quals.Peer
+
+package annotator.tests:
+class FieldNewComplex:
+field f:
+type: @GUT.quals.Rep
+
+package annotator.tests:
+class FieldNewComplex:
+method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;:
+return: @GUT.quals.Rep
+
+package annotator.tests:
+class FieldNewComplex:
+method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;:
+parameter 2:
+type: @GUT.quals.Rep
+
+package annotator.tests:
+class FieldNewComplex:
+method m(Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;Lannotator/tests/FieldNewComplex;)Lannotator/tests/FieldNewComplex;:
+parameter 0:
+type: @GUT.quals.Peer
diff --git a/annotation-file-utilities/tests/source-extension/FieldNewComplex.java b/annotation-file-utilities/tests/source-extension/FieldNewComplex.java
new file mode 100644
index 0000000..3c1bb3a
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/FieldNewComplex.java
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+public class FieldNewComplex {
+  FieldNewComplex m(FieldNewComplex a, FieldNewComplex b, FieldNewComplex c) {
+    return null;
+  }
+
+  FieldNewComplex f = m(new FieldNewComplex(), new FieldNewComplex(), new FieldNewComplex());
+}
diff --git a/annotation-file-utilities/tests/source-extension/LocalVariables.goal b/annotation-file-utilities/tests/source-extension/LocalVariables.goal
new file mode 100644
index 0000000..ef7dca6
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/LocalVariables.goal
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+public class LocalVariables {
+  public void foo() {
+    /*Mut*/ @java.lang.UnderInitialization Object a = null;
+    Object b = null;
+    Object c = null;
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/LocalVariables.jaif b/annotation-file-utilities/tests/source-extension/LocalVariables.jaif
new file mode 100644
index 0000000..7a5e000
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/LocalVariables.jaif
@@ -0,0 +1,11 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class LocalVariables:
+
+    method <init>()V:
+
+    method foo()V:
+        local a: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/source-extension/LocalVariables.java b/annotation-file-utilities/tests/source-extension/LocalVariables.java
new file mode 100644
index 0000000..a742952
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/LocalVariables.java
@@ -0,0 +1,9 @@
+package annotator.tests;
+
+public class LocalVariables {
+  public void foo() {
+    /*Mut*/ Object a = null;
+    Object b = null;
+    Object c = null;
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/Makefile b/annotation-file-utilities/tests/source-extension/Makefile
new file mode 100644
index 0000000..27693af
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/Makefile
@@ -0,0 +1,87 @@
+# Very rough testing framework for the annotator.  Running 'make all' will
+# look for all myClass.goal files in this directory, run the annotator on the
+# corresponding .jaif and .java files, and then output the difference in a
+# myClass.diff file in this directory.
+#
+# To test just one file, use (for example) 'make myClass.diff'.
+
+# Put user-specific changes in your own Makefile.user.
+# Make will silently continue if that file does not exist.
+-include ../Makefile.user
+
+# Override these in Makefile.user if the java and javac commands are not on
+# your execution path.  Example from Makefile.user:
+#   JAVA=${JAVA_HOME}/bin/java
+#   JAVAC=${JAVA_HOME}/bin/javac
+JAVA?=java
+JAVAC?=javac
+
+export SHELL=/bin/bash -o pipefail
+
+
+DIFFS := $(wildcard *.goal)
+DISABLED := $(shell grep -le "@skip-test" $(DIFFS))
+FILTERED := $(filter-out $(DISABLED),$(DIFFS))
+DIFFS := $(patsubst %.goal, %.diff, $(FILTERED))
+SRC := $(patsubst %.goal, %.java, $(FILTERED))
+OUT := $(patsubst %.goal, %.output, $(FILTERED))
+
+DEBUG :=
+# Use this to enable some debugging.
+# DEBUG := --debug
+
+default : all
+
+.PHONY: all
+all : $(DIFFS) results
+
+# Display results of all .diff files.
+.PHONY: results
+results: ../bin/VerifyDiffs.class
+	@echo ""
+	@echo "=== RESULTS ==="
+	@echo ""
+	@$(JAVA) -cp bin:../bin VerifyDiffs --show_all
+
+# Remakes the little java program that checks and compares diffs
+../bin/VerifyDiffs.class : ../VerifyDiffs.java
+	@$(JAVAC) -g -cp ../../bincompile -d ../bin ../VerifyDiffs.java
+
+# Compiles all the test cases (be verbose about this).
+.PHONY: compile
+compile : $(SRC)
+	mkdir -p bin
+	$(JAVAC) -g -cp ../../bin -d bin $(SRC)
+
+# Compiles just one test case
+.PRECIOUS : bin/annotator/tests/%.class
+bin/annotator/tests/%.class: %.java
+	mkdir -p bin
+	$(JAVAC) -g -cp bin:../../bin -d bin -sourcepath . $<
+
+# Actually runs the annotator to create the annotated java file.
+.PRECIOUS: %.output
+%.output: %.jaif %.java bin/annotator/tests/%.class ../../lib/plume-core.jar ../../bin ../../annotation-file-utilities.jar
+	$(JAVA) \
+	-cp ../../bin:../../annotation-file-utilities.jar:bin \
+	annotator.Main \
+	${DEBUG} \
+	--abbreviate=false \
+	-d $*-output \
+	$*.jaif \
+	$*.java \
+	2>&1 | tee $*.log
+	find "$*-output" -name '*.java' -print | xargs cat > "$*.output"
+	rm -rf $*-output
+
+# Compare the output of the annotator and the goal file.
+%.diff: %.goal %.output
+	-diff -u $*.goal $*.output >& $*.diff
+
+# Remove all .diff, .log files from the tests directory.
+.PHONY: clean
+clean :
+	rm -rf bin
+	rm -f *.diff
+	rm -f *.log
+	rm -f *.output
diff --git a/annotation-file-utilities/tests/source-extension/MethodNew.goal b/annotation-file-utilities/tests/source-extension/MethodNew.goal
new file mode 100644
index 0000000..d9bf380
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/MethodNew.goal
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+public class MethodNew {
+  void m() {
+    Object l = new @GUT.quals.Rep MethodNew();
+  }
+
+  void m(Object p) {
+    Object x = new @GUT.quals.Peer MethodNew();
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/MethodNew.jaif b/annotation-file-utilities/tests/source-extension/MethodNew.jaif
new file mode 100644
index 0000000..93a6a62
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/MethodNew.jaif
@@ -0,0 +1,15 @@
+package GUT.quals:
+annotation @Peer:
+annotation @Rep:
+
+package annotator.tests:
+class MethodNew:
+method m(Ljava/lang/Object;)V:
+new *0: @GUT.quals.Peer
+
+package annotator.tests:
+class MethodNew:
+method m()V:
+new *0: @GUT.quals.Rep
+
+
diff --git a/annotation-file-utilities/tests/source-extension/MethodNew.java b/annotation-file-utilities/tests/source-extension/MethodNew.java
new file mode 100644
index 0000000..a41ad90
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/MethodNew.java
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+public class MethodNew {
+  void m() {
+    Object l = new MethodNew();
+  }
+
+  void m(Object p) {
+    Object x = new MethodNew();
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/SameName.goal b/annotation-file-utilities/tests/source-extension/SameName.goal
new file mode 100644
index 0000000..670fb63
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/SameName.goal
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+public class SameName {
+  void m() {
+    if (5==6) {
+      Object a = null;
+    } else {
+      @java.lang.UnderInitialization Object a = null;
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/SameName.jaif b/annotation-file-utilities/tests/source-extension/SameName.jaif
new file mode 100644
index 0000000..f362460
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/SameName.jaif
@@ -0,0 +1,12 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class SameName:
+
+    method <init>()V:
+
+    method m()V:
+        local a *1:
+        type: @java.lang.UnderInitialization
+
diff --git a/annotation-file-utilities/tests/source-extension/SameName.java b/annotation-file-utilities/tests/source-extension/SameName.java
new file mode 100644
index 0000000..0e1d885
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/SameName.java
@@ -0,0 +1,11 @@
+package annotator.tests;
+
+public class SameName {
+  void m() {
+    if (5==6) {
+      Object a = null;
+    } else {
+      Object a = null;
+    }
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/StaticInit.goal b/annotation-file-utilities/tests/source-extension/StaticInit.goal
new file mode 100644
index 0000000..7ec6528
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/StaticInit.goal
@@ -0,0 +1,21 @@
+package annotator.tests;
+
+public class StaticInit {
+  static void blabla() {}
+
+  static {
+    @GUT.quals.Peer
+    Object o = new @GUT.quals.Peer Integer(5);
+    if (o instanceof Integer) {
+      @GUT.quals.Peer
+      Object o2 = new @GUT.quals.Peer Object();
+    }
+  }
+
+  void m() { if (true) {} else {} }
+
+  static {
+    @GUT.quals.Rep
+    StaticInit si = new @GUT.quals.Rep StaticInit();
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/StaticInit.jaif b/annotation-file-utilities/tests/source-extension/StaticInit.jaif
new file mode 100644
index 0000000..3e98587
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/StaticInit.jaif
@@ -0,0 +1,37 @@
+package GUT.quals:
+annotation @Peer:
+annotation @Rep:
+
+
+package annotator.tests:
+class StaticInit:
+staticinit *0:
+new *1: @GUT.quals.Peer
+
+package annotator.tests:
+class StaticInit:
+staticinit *1:
+new *0: @GUT.quals.Rep
+
+package annotator.tests:
+class StaticInit:
+staticinit *0:
+new *0: @GUT.quals.Peer
+
+package annotator.tests:
+class StaticInit:
+staticinit *0:
+local o2:
+type: @GUT.quals.Peer
+
+package annotator.tests:
+class StaticInit:
+staticinit *1:
+local si:
+type: @GUT.quals.Rep
+
+package annotator.tests:
+class StaticInit:
+staticinit *0:
+local o:
+type: @GUT.quals.Peer
diff --git a/annotation-file-utilities/tests/source-extension/StaticInit.java b/annotation-file-utilities/tests/source-extension/StaticInit.java
new file mode 100644
index 0000000..fcf24da
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/StaticInit.java
@@ -0,0 +1,18 @@
+package annotator.tests;
+
+public class StaticInit {
+  static void blabla() {}
+
+  static {
+    Object o = new Integer(5);
+    if (o instanceof Integer) {
+      Object o2 = new Object();
+    }
+  }
+
+  void m() { if (true) {} else {} }
+
+  static {
+    StaticInit si = new StaticInit();
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/TypeCastMultiple.goal b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.goal
new file mode 100644
index 0000000..aabb5dd
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.goal
@@ -0,0 +1,16 @@
+package annotator.tests;
+
+import java.util.List;
+import java.util.LinkedList;
+
+public class TypeCastMultiple {
+  public void foo(Object o) {
+    List myList = (@java.lang.UnderInitialization List) o;
+    myList = new @java.lang.UnderInitialization LinkedList();
+    if (myList instanceof @java.lang.UnderInitialization List) {
+    }
+    Integer i = (@java.lang.Tainted Integer) o;
+    System.out.println(myList);
+    System.out.println(i);
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/TypeCastMultiple.jaif b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.jaif
new file mode 100644
index 0000000..25b743b
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.jaif
@@ -0,0 +1,17 @@
+package java.lang:
+annotation @UnderInitialization: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package java.lang:
+annotation @Tainted: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotator.tests:
+class TypeCastMultiple:
+
+    method <init>()V:
+
+    method foo(Ljava/lang/Object;)V:
+        typecast *0: @java.lang.UnderInitialization
+        new *0: @java.lang.UnderInitialization
+        instanceof *0: @java.lang.UnderInitialization
+        typecast *1: @java.lang.Tainted
+
diff --git a/annotation-file-utilities/tests/source-extension/TypeCastMultiple.java b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.java
new file mode 100644
index 0000000..baf4874
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/TypeCastMultiple.java
@@ -0,0 +1,16 @@
+package annotator.tests;
+
+import java.util.List;
+import java.util.LinkedList;
+
+public class TypeCastMultiple {
+  public void foo(Object o) {
+    List myList = (List) o;
+    myList = new LinkedList();
+    if (myList instanceof List) {
+    }
+    Integer i = (Integer) o;
+    System.out.println(myList);
+    System.out.println(i);
+  }
+}
diff --git a/annotation-file-utilities/tests/source-extension/Wildcards.goal b/annotation-file-utilities/tests/source-extension/Wildcards.goal
new file mode 100644
index 0000000..69696f6
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/Wildcards.goal
@@ -0,0 +1,12 @@
+import java.util.List;
+import java.util.Map;
+
+public class Wildcards {
+  @GUT.quals.Rep
+  List<? extends @GUT.quals.Rep Object> l1;
+  @GUT.quals.Rep
+  List<? extends @GUT.quals.Rep Object> l2;
+  Map<? extends @GUT.quals.Peer Object, ? extends @GUT.quals.Rep Object> l3;
+  Map<@GUT.quals.Rep ? extends @GUT.quals.Peer Map<@GUT.quals.Rep ? extends @GUT.quals.Peer Object, @GUT.quals.Rep String>, @GUT.quals.Peer Object> l4;
+}
+
diff --git a/annotation-file-utilities/tests/source-extension/Wildcards.jaif b/annotation-file-utilities/tests/source-extension/Wildcards.jaif
new file mode 100644
index 0000000..ae318ea
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/Wildcards.jaif
@@ -0,0 +1,74 @@
+package GUT.quals:
+annotation @Peer:
+annotation @Rep:
+
+package:
+class Wildcards:
+field l1:
+type:
+inner-type 3, 0, 2, 0: @GUT.quals.Rep
+
+package:
+class Wildcards:
+field l1:
+type: @GUT.quals.Rep
+
+package:
+class Wildcards:
+field l2:
+type: @GUT.quals.Rep
+
+package:
+class Wildcards:
+field l2:
+type:
+inner-type 3, 0, 2, 0: @GUT.quals.Rep
+
+package:
+class Wildcards:
+field l3:
+type:
+inner-type 3, 0, 2, 0: @GUT.quals.Peer
+
+package:
+class Wildcards:
+field l3:
+type:
+inner-type 3, 1, 2, 0: @GUT.quals.Rep
+
+
+package:
+class Wildcards:
+field l4:
+type:
+inner-type 3, 0: @GUT.quals.Rep
+
+package:
+class Wildcards:
+field l4:
+type:
+inner-type 3, 0, 2, 0: @GUT.quals.Peer
+
+package:
+class Wildcards:
+field l4:
+type:
+inner-type 3, 0, 2, 0, 3, 0: @GUT.quals.Rep
+
+package:
+class Wildcards:
+field l4:
+type:
+inner-type 3, 0, 2, 0, 3, 0, 2, 0: @GUT.quals.Peer
+
+package:
+class Wildcards:
+field l4:
+type:
+inner-type 3, 0, 2, 0, 3, 1: @GUT.quals.Rep
+
+package:
+class Wildcards:
+field l4:
+type:
+inner-type 3, 1: @GUT.quals.Peer
diff --git a/annotation-file-utilities/tests/source-extension/Wildcards.java b/annotation-file-utilities/tests/source-extension/Wildcards.java
new file mode 100644
index 0000000..eda12f5
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/Wildcards.java
@@ -0,0 +1,10 @@
+import java.util.List;
+import java.util.Map;
+
+public class Wildcards {
+  List<?> l1;
+  List<? extends Object> l2;
+  Map<?, ?> l3;
+  Map<? extends Map<?, String>, Object> l4;
+}
+
diff --git a/annotation-file-utilities/tests/source-extension/WildcardsSuper.goal b/annotation-file-utilities/tests/source-extension/WildcardsSuper.goal
new file mode 100644
index 0000000..86e06c0
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/WildcardsSuper.goal
@@ -0,0 +1,6 @@
+import java.util.*;
+
+public class WildcardsSuper {
+  @GUT.quals.Rep
+  Enumeration<? super @GUT.quals.Peer WildcardsSuper> f;
+}
diff --git a/annotation-file-utilities/tests/source-extension/WildcardsSuper.jaif b/annotation-file-utilities/tests/source-extension/WildcardsSuper.jaif
new file mode 100644
index 0000000..c333c13
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/WildcardsSuper.jaif
@@ -0,0 +1,15 @@
+package GUT.quals:
+annotation @Rep:
+package GUT.quals:
+annotation @Peer:
+
+package:
+class WildcardsSuper:
+field f:
+type:
+inner-type 3, 0, 2, 0: @GUT.quals.Peer
+
+package:
+class WildcardsSuper:
+field f:
+type: @GUT.quals.Rep
diff --git a/annotation-file-utilities/tests/source-extension/WildcardsSuper.java b/annotation-file-utilities/tests/source-extension/WildcardsSuper.java
new file mode 100644
index 0000000..7bb4ff0
--- /dev/null
+++ b/annotation-file-utilities/tests/source-extension/WildcardsSuper.java
@@ -0,0 +1,5 @@
+import java.util.*;
+
+public class WildcardsSuper {
+  Enumeration<? super WildcardsSuper> f;
+}
diff --git a/annotation-file-utilities/tests/system-test/Makefile b/annotation-file-utilities/tests/system-test/Makefile
new file mode 100755
index 0000000..46c2533
--- /dev/null
+++ b/annotation-file-utilities/tests/system-test/Makefile
@@ -0,0 +1,72 @@
+# System test of annotation tools
+
+# This test runs most of the AFU tools, on a single program
+# source-ann/AnnotationTest.java that contains an annotation in every
+# possible location.
+
+ANNCAT ?= ../../../scene-lib/anncat
+JAVAVERSION = $(shell $(JAVA) -version 2>&1)
+ifeq (1.7.,$(findstring 1.7.,$(JAVAVERSION)))
+  XJAVACTARGET = -source 7 -target 7
+endif
+
+#Switch the lines below to deactivate this test
+all: clean results
+#all: warn_and_exit
+
+warn_and_exit:
+	@echo ""
+	@echo "******************************************************"
+	@echo "Warning: The sytem test has been temporarily disabled!"
+	@echo "******************************************************"
+	@echo ""
+
+results: out1 check-out2 out3 out4.class check-out5
+
+clean:
+	rm -rf out[1-5]*
+
+
+# Step 1: Compile the source with annotations.
+out1:
+	mkdir out1
+	${XJAVAC} ${XJAVACTARGET} -d out1 source-ann/AnnotationTest.java || (\rm -rf out1 && false)
+
+# Step 2: Convert the annotated class file to an annotation file.
+# Do we get the right annotations?
+out2.jaif:
+	CLASSPATH=`pwd`/out1 ${ANNCAT} --class out1/annotations/tests/AnnotationTest.class --out --index out2.jaif
+
+out2.diff: out2.jaif
+	diff -u expected-annos.jaif out2.jaif > out2.diff
+
+.PHONY: check-out2
+# Fail if out2.diff is non-empty
+check-out2: out2.diff
+	[ ! -s out2.diff ] || (cat out2.diff && false)
+
+# Step 3: Compile the source without annotations.
+out3:
+	mkdir out3
+	${XJAVAC} ${XJAVACTARGET} -d out3 source-plain/AnnotationTest.java || (\rm -rf out3 && false)
+
+# Step 4: Insert annotations into the class file.
+# Can debug with:  javap -v out4.class
+out4.class: out3
+	CLASSPATH=`pwd`/out1 ${ANNCAT} --index expected-annos.jaif --out --class out3/annotations/tests/AnnotationTest.class --to out4.class
+
+# Step 5: Convert the annotation-inserted class file to an annotation file.
+# Do we get the right annotations?
+# (The annotation-compiled and annotation-inserted class files tend to differ
+# for stupid reasons (e.g., order of items in the constant pool), so we don't
+# compare them.)
+out5.jaif: out4.class
+	CLASSPATH=`pwd`/out1 ${ANNCAT} --class out4.class --out --index out5.jaif
+
+out5.diff: out5.jaif
+	diff -u expected-annos.jaif out5.jaif > out5.diff
+
+.PHONY: check-out5
+# Fail if out5.diff is non-empty
+check-out5: out5.diff
+	[ ! -s out5.diff ] || (cat out5.diff && false)
diff --git a/annotation-file-utilities/tests/system-test/expected-annos-better-UNUSED.jaif b/annotation-file-utilities/tests/system-test/expected-annos-better-UNUSED.jaif
new file mode 100644
index 0000000..e5f669f
--- /dev/null
+++ b/annotation-file-utilities/tests/system-test/expected-annos-better-UNUSED.jaif
@@ -0,0 +1,116 @@
+package annotations.tests:
+annotation invisible @AClass:
+
+package annotations.tests:
+annotation invisible @CClass:
+
+package annotations.tests:
+annotation invisible @A0A:
+
+package annotations.tests:
+annotation invisible @C0A:
+
+package annotations.tests:
+annotation invisible @A0B:
+
+package annotations.tests:
+annotation invisible @C0B:
+
+package annotations.tests:
+annotation invisible @A06:
+
+package annotations.tests:
+annotation invisible @C06:
+
+package annotations.tests:
+annotation invisible @A0C:
+
+package annotations.tests:
+annotation invisible @C0C:
+
+package annotations.tests:
+annotation invisible @A0D:
+
+package annotations.tests:
+annotation invisible @C0D:
+
+package annotations.tests:
+annotation invisible @A08:
+
+package annotations.tests:
+annotation invisible @C08:
+
+package annotations.tests:
+annotation invisible @A09:
+
+package annotations.tests:
+annotation invisible @C09:
+
+package annotations.tests:
+annotation invisible @A00:
+
+package annotations.tests:
+annotation invisible @C00:
+
+package annotations.tests:
+annotation invisible @A01:
+
+package annotations.tests:
+annotation invisible @C01:
+
+package annotations.tests:
+annotation invisible @A02:
+
+package annotations.tests:
+annotation invisible @C02:
+
+package annotations.tests:
+annotation invisible @A04:
+
+package annotations.tests:
+annotation invisible @C04:
+
+package annotations.tests:
+annotation invisible @A05:
+
+package annotations.tests:
+annotation invisible @C05:
+
+package annotations.tests:
+annotation invisible @A0E:
+
+package annotations.tests:
+annotation invisible @C0E:
+
+package annotations.tests:
+annotation invisible @A0F:
+
+package annotations.tests:
+annotation invisible @C0F:
+
+package annotations.tests:
+class AnnotationTest: @annotations.tests.AClass @annotations.tests.CClass
+    bound 0 &0: @annotations.tests.A10 @annotations.tests.C10
+        inner-type 0: @annotations.tests.A11 @annotations.tests.C11
+
+    field field: @annotations.tests.A0E @annotations.tests.C0E
+        inner-type 0: @annotations.tests.A0F @annotations.tests.C0F
+
+    method <init>()V:
+
+    method doSomething(Ljava/util/Set;)Ljava/util/HashSet;:
+        return: @annotations.tests.A0A @annotations.tests.C0A
+            inner-type 0: @annotations.tests.A0B @annotations.tests.C0B
+        bound 0 &0: @annotations.tests.A12 @annotations.tests.C12
+            inner-type 0: @annotations.tests.A13 @annotations.tests.C13
+        parameter #0: @annotations.tests.A0C @annotations.tests.C0C
+            inner-type 0: @annotations.tests.A0D @annotations.tests.C0D
+        receiver: @annotations.tests.A06 @annotations.tests.C06
+        local 2 #23+2: @annotations.tests.A08 @annotations.tests.C08
+            inner-type 0: @annotations.tests.A09 @annotations.tests.C09
+        typecast #7: @annotations.tests.A00 @annotations.tests.C00
+            inner-type 0: @annotations.tests.A01 @annotations.tests.C01
+        instanceof #0: @annotations.tests.A02 @annotations.tests.C02
+        new #15: @annotations.tests.A04 @annotations.tests.C04
+            inner-type 0: @annotations.tests.A05 @annotations.tests.C05
+
diff --git a/annotation-file-utilities/tests/system-test/expected-annos.jaif b/annotation-file-utilities/tests/system-test/expected-annos.jaif
new file mode 100644
index 0000000..5b43195
--- /dev/null
+++ b/annotation-file-utilities/tests/system-test/expected-annos.jaif
@@ -0,0 +1,189 @@
+package annotations.tests:
+annotation @AClass:
+
+package annotations.tests:
+annotation @CClass:
+
+package annotations.tests:
+annotation @A0A: @java.lang.annotation.Target(value={TYPE_USE,METHOD})
+
+package annotations.tests:
+annotation @C0A: @java.lang.annotation.Target(value={TYPE_USE,METHOD})
+
+package annotations.tests:
+annotation @A0AT: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C0AT: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A0B: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C0B: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A06: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER})
+
+package annotations.tests:
+annotation @C06: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER})
+
+package annotations.tests:
+annotation @A0C: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER})
+
+package annotations.tests:
+annotation @C0C: @java.lang.annotation.Target(value={TYPE_USE,PARAMETER})
+
+package annotations.tests:
+annotation @A0D: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C0D: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A08: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C08: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A09: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C09: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A00: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C00: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A01: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C01: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A02: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C02: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A04: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C04: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A05: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C05: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A0E: @java.lang.annotation.Target(value={TYPE_USE,FIELD})
+
+package annotations.tests:
+annotation @C0E: @java.lang.annotation.Target(value={TYPE_USE,FIELD})
+
+package annotations.tests:
+annotation @A20: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C20: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A21: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C21: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A22: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C22: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A23: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C23: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A24: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C24: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A2B:
+
+package annotations.tests:
+annotation @C2B:
+
+package annotations.tests:
+annotation @A2C: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C2C: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A2D: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C2D: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A2E: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C2E: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @A2F: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+annotation @C2F: @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests:
+class AnnotationTest: @annotations.tests.AClass @annotations.tests.CClass
+
+    field field: @annotations.tests.A0E @annotations.tests.C0E
+        type: @annotations.tests.A0E @annotations.tests.C0E
+            inner-type 3, 0: @annotations.tests.A20 @annotations.tests.C20
+            inner-type 3, 0,0, 0: @annotations.tests.A21 @annotations.tests.C21
+
+    field inner:
+        type:
+            inner-type 1, 0,1, 0: @annotations.tests.A22 @annotations.tests.C22
+            inner-type 1, 0,1, 0,3, 0: @annotations.tests.A23 @annotations.tests.C23
+            inner-type 1, 0: @annotations.tests.A24 @annotations.tests.C24
+
+    field entry: @annotations.tests.A2B @annotations.tests.C2B
+        type: @annotations.tests.A2C @annotations.tests.C2C
+            inner-type 3, 0: @annotations.tests.A2D @annotations.tests.C2D
+            inner-type 3, 1: @annotations.tests.A2E @annotations.tests.C2E
+            inner-type 3, 1,2, 0: @annotations.tests.A2F @annotations.tests.C2F
+
+    method <init>()V:
+        return:
+
+    method doSomething(Ljava/util/Set;)Ljava/util/HashSet;: @annotations.tests.A0A @annotations.tests.C0A
+        return: @annotations.tests.A0A @annotations.tests.C0A @annotations.tests.A0AT @annotations.tests.C0AT
+            inner-type 3, 0: @annotations.tests.A0B @annotations.tests.C0B
+        receiver: @annotations.tests.A06 @annotations.tests.C06
+        parameter #0: @annotations.tests.A0C @annotations.tests.C0C
+            type: @annotations.tests.A0C @annotations.tests.C0C
+                inner-type 3, 0: @annotations.tests.A0D @annotations.tests.C0D
+        local 2 #23+2: @annotations.tests.A08 @annotations.tests.C08
+            type:
+                inner-type 3, 0: @annotations.tests.A09 @annotations.tests.C09
+        typecast #7: @annotations.tests.A00 @annotations.tests.C00
+            inner-type 3, 0: @annotations.tests.A01 @annotations.tests.C01
+        instanceof #0: @annotations.tests.A02 @annotations.tests.C02
+        new #15: @annotations.tests.A04 @annotations.tests.C04
+            inner-type 3, 0: @annotations.tests.A05 @annotations.tests.C05
+
diff --git a/annotation-file-utilities/tests/system-test/source-ann-better/AnnotationTest.java b/annotation-file-utilities/tests/system-test/source-ann-better/AnnotationTest.java
new file mode 100644
index 0000000..13c337a
--- /dev/null
+++ b/annotation-file-utilities/tests/system-test/source-ann-better/AnnotationTest.java
@@ -0,0 +1,57 @@
+package annotations.tests;
+
+import java.util.*;
+
+@interface AClass {}
+@interface A00 {}
+@interface A01 {}
+@interface A02 {}
+@interface A04 {}
+@interface A05 {}
+@interface A06 {}
+@interface A08 {}
+@interface A09 {}
+@interface A0A {}
+@interface A0B {}
+@interface A0C {}
+@interface A0D {}
+@interface A0E {}
+@interface A0F {}
+@interface A10 {}
+@interface A11 {}
+@interface A12 {}
+@interface A13 {}
+@interface CClass {}
+@interface C00 {}
+@interface C01 {}
+@interface C02 {}
+@interface C04 {}
+@interface C05 {}
+@interface C06 {}
+@interface C08 {}
+@interface C09 {}
+@interface C0A {}
+@interface C0B {}
+@interface C0C {}
+@interface C0D {}
+@interface C0E {}
+@interface C0F {}
+@interface C10 {}
+@interface C11 {}
+@interface C12 {}
+@interface C13 {}
+
+public @AClass /*@CClass*/ class AnnotationTest<Foo extends @A10 /*@C10*/ Comparable<@A11 /*@C11*/ Integer>> {
+
+    @A0E /*@C0E*/ Iterable<@A0F /*@C0F*/ String> field;
+
+    <Bar extends @A12 /*@C12*/ Comparable<@A13 /*@C13*/ Integer>> @A0A /*@C0A*/ HashSet<@A0B /*@C0B*/ Integer>
+        doSomething(@A06 AnnotationTest this, @A0C /*@C0C*/ Set<@A0D /*@C0D*/ Integer> param) /*@C06*/ {
+        @A08 /*@C08*/ HashSet<@A09 /*@C09*/ Integer> local;
+        if (param instanceof @A02 /*@C02*/ HashSet)
+            local = (@A00 /*@C00*/ HashSet<@A01 /*@C01*/ Integer>) param;
+        else
+            local = new @A04 /*@C04*/ HashSet<@A05 /*@C05*/ Integer>();
+        return local;
+    }
+}
diff --git a/annotation-file-utilities/tests/system-test/source-ann/AnnotationTest.java b/annotation-file-utilities/tests/system-test/source-ann/AnnotationTest.java
new file mode 100644
index 0000000..6996920
--- /dev/null
+++ b/annotation-file-utilities/tests/system-test/source-ann/AnnotationTest.java
@@ -0,0 +1,140 @@
+package annotations.tests;
+
+import java.util.*;
+import java.lang.annotation.*;
+
+@interface AClass {}
+@Target(ElementType.TYPE_USE)
+@interface A00 {}
+@Target(ElementType.TYPE_USE)
+@interface A01 {}
+@Target(ElementType.TYPE_USE)
+@interface A02 {}
+@Target(ElementType.TYPE_USE)
+@interface A04 {}
+@Target(ElementType.TYPE_USE)
+@interface A05 {}
+
+@Target({ElementType.TYPE_USE, ElementType.PARAMETER})
+@interface A06 {}
+@Target(ElementType.TYPE_USE)
+@interface A08 {}
+@Target(ElementType.TYPE_USE)
+@interface A09 {}
+
+@Target({ElementType.TYPE_USE, ElementType.METHOD})
+@interface A0A {}
+@Target(ElementType.TYPE_USE)
+@interface A0AT {}
+@Target(ElementType.TYPE_USE)
+@interface A0B {}
+
+@Target({ElementType.TYPE_USE, ElementType.PARAMETER})
+@interface A0C {}
+@Target(ElementType.TYPE_USE)
+@interface A0D {}
+
+@Target({ElementType.TYPE_USE, ElementType.FIELD})
+@interface A0E {}
+@Target({ElementType.TYPE_USE})
+@interface A21 {}
+
+
+@Target(ElementType.TYPE_USE)
+@interface A20 {}
+@Target(ElementType.TYPE_USE)
+@interface A22 {}
+@Target(ElementType.TYPE_USE)
+@interface A23 {}
+@Target(ElementType.TYPE_USE)
+@interface A24 {}
+@interface A2B {}
+@Target(ElementType.TYPE_USE)
+@interface A2C {}
+@Target(ElementType.TYPE_USE)
+@interface A2D {}
+@Target(ElementType.TYPE_USE)
+@interface A2E {}
+@Target(ElementType.TYPE_USE)
+@interface A2F {}
+@interface A10 {}
+@interface A11 {}
+@interface A12 {}
+@interface A13 {}
+@interface CClass {}
+@Target(ElementType.TYPE_USE)
+@interface C00 {}
+@Target(ElementType.TYPE_USE)
+@interface C01 {}
+@Target(ElementType.TYPE_USE)
+@interface C02 {}
+@Target(ElementType.TYPE_USE)
+@interface C04 {}
+@Target(ElementType.TYPE_USE)
+@interface C05 {}
+
+@Target({ElementType.TYPE_USE, ElementType.PARAMETER})
+@interface C06 {}
+@Target(ElementType.TYPE_USE)
+@interface C08 {}
+@Target(ElementType.TYPE_USE)
+@interface C09 {}
+@Target({ElementType.TYPE_USE, ElementType.METHOD})
+@interface C0A {}
+@Target(ElementType.TYPE_USE)
+@interface C0AT {}
+@Target(ElementType.TYPE_USE)
+@interface C0B {}
+
+@Target({ElementType.TYPE_USE, ElementType.PARAMETER})
+@interface C0C {}
+@Target(ElementType.TYPE_USE)
+@interface C0D {}
+@Target({ElementType.TYPE_USE, ElementType.FIELD})
+@interface C0E {}
+@Target(ElementType.TYPE_USE)
+@interface C20 {}
+@Target(ElementType.TYPE_USE)
+@interface C21 {}
+@Target(ElementType.TYPE_USE)
+@interface C22 {}
+@Target(ElementType.TYPE_USE)
+@interface C23 {}
+@Target(ElementType.TYPE_USE)
+@interface C24 {}
+@interface C2B {}
+@Target(ElementType.TYPE_USE)
+@interface C2C {}
+@Target(ElementType.TYPE_USE)
+@interface C2D {}
+@Target(ElementType.TYPE_USE)
+@interface C2E {}
+@Target(ElementType.TYPE_USE)
+@interface C2F {}
+@interface C10 {}
+@interface C11 {}
+@interface C12 {}
+@interface C13 {}
+
+public @AClass /*@CClass*/ class AnnotationTest<Foo extends /*A10*/ /*C10*/ Comparable</*A11*/ /*C11*/ Integer>> {
+    class Outer {
+        class Inner<Baz> {
+            int baz(Baz o) { return o.hashCode() ^ this.hashCode(); }
+        }
+    }
+
+    @A0E /*@C0E*/ Iterable<@A21 /*@C21*/ String @A20 /*@C20*/ []> field;
+    @A24 /*@C24*/ Outer.@A22 /*@C22*/ Inner<@A23 /*@C23*/ String> inner;
+    @A2B /*@C2B*/ Map.@A2C /*@C2C*/ Entry<@A2D /*@C2D*/ Integer, @A2E /*@C2E*/ ? extends @A2F /*@C2F*/ CharSequence> entry;
+
+    // TODO: crash when A12, C12, A13, or C13 are annotations!
+    <Bar extends /*A12*/ /*C12*/ Comparable</*A13*/ /*C13*/ Integer>> @A0A /*@C0A*/ @A0AT /*@C0AT*/ HashSet<@A0B /*@C0B*/ Integer>
+        doSomething(@A06 /*@C06*/ AnnotationTest<Foo> this, @A0C /*@C0C*/ Set<@A0D /*@C0D*/ Integer> param) {
+        @A08 /*@C08*/ HashSet<@A09 /*@C09*/ Integer> local;
+        if (param instanceof @A02 /*@C02*/ HashSet)
+            local = (@A00 /*@C00*/ HashSet<@A01 /*@C01*/ Integer>) param;
+        else
+            local = new @A04 /*@C04*/ HashSet<@A05 /*@C05*/ Integer>();
+        return local;
+    }
+}
diff --git a/annotation-file-utilities/tests/system-test/source-plain/AnnotationTest.java b/annotation-file-utilities/tests/system-test/source-plain/AnnotationTest.java
new file mode 100644
index 0000000..4f208bb
--- /dev/null
+++ b/annotation-file-utilities/tests/system-test/source-plain/AnnotationTest.java
@@ -0,0 +1,25 @@
+package annotations.tests;
+
+import java.util.*;
+
+public class AnnotationTest<Foo extends Comparable<Integer>> {
+    class Outer {
+        class Inner<Baz> {
+            int baz(Baz o) { return o.hashCode() ^ this.hashCode(); }
+        }
+    }
+
+    Iterable<String[]> field;
+    Outer.Inner<String> inner;
+    Map.Entry<Integer, ? extends CharSequence> entry;
+
+    <Bar extends Comparable<Integer>> HashSet<Integer>
+        doSomething(Set<Integer> param) {
+        HashSet<Integer> local;
+        if (param instanceof HashSet)
+            local = (HashSet<Integer>) param;
+        else
+            local = new HashSet<Integer>();
+        return local;
+    }
+}
diff --git a/asmx/.classpath b/asmx/.classpath
new file mode 100644
index 0000000..e0a7d3a
--- /dev/null
+++ b/asmx/.classpath
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="config/ow_util_ant_tasks.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/jsr308-langtools"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/asmx/.project b/asmx/.project
new file mode 100644
index 0000000..8cc17c4
--- /dev/null
+++ b/asmx/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>asmx</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/asmx/bcel.jar b/asmx/bcel.jar
new file mode 100644
index 0000000..2fa90ce
--- /dev/null
+++ b/asmx/bcel.jar
Binary files differ
diff --git a/asmx/build.config b/asmx/build.config
new file mode 100644
index 0000000..452caf8
--- /dev/null
+++ b/asmx/build.config
@@ -0,0 +1,18 @@
+# Defines values for the 'build.properties' build properties
+# This file is used to build the project in the case of bundled external jars
+
+objectweb.ant.tasks.path config/ow_util_ant_tasks.jar
+
+bcel.path test/lib/bcel-5.1.jar
+
+aspectj.path test/lib/aspectjweaver.jar
+
+serp.path test/lib/serp.jar
+
+javassist.path test/lib/javassist.jar
+
+janino.path test/lib/janino-2.3.4.jar
+
+cobertura.path test/lib/cobertura-1.8.jar;test/lib/jakarta-oro-2.0.8.jar;test/lib/log4j-1.2.9.jar;test/lib/asm-2.2.1.jar
+
+cobertura.runtime.path test/lib/cobertura-1.8.jar
\ No newline at end of file
diff --git a/asmx/build.properties b/asmx/build.properties
new file mode 100644
index 0000000..34d0c3f
--- /dev/null
+++ b/asmx/build.properties
@@ -0,0 +1,92 @@
+###############################################################################
+#ASM: a very small and fast Java bytecode manipulation framework
+#Copyright (c) 2000-2005 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.
+###############################################################################
+
+# Some information about the product
+
+product.name    asm
+product.version 2.2.2
+# product.snapshot yes
+
+plugin.version 2.2.2
+
+
+###############################################################################
+# BUILD
+###############################################################################
+
+# Wich compiler do you want to use ?
+# jikes is faster than javac but keeps line numbers in compiled classes, even
+# with the -O option (javac removes line numbers and local variable names with
+# this option, resulting in smaller classes)
+
+build.compiler modern
+
+# Build class path (classes needed to build the project)
+# Class path for the ObjectWeb utility Ant tasks (version 1.2 or higher)
+# See http://forge.objectweb.org/projects/monolog
+
+objectweb.ant.tasks.path ow_util_ant_tasks.jar
+
+###############################################################################
+# TESTS (PERFORMANCE COMPARISONS WITH BCEL AND SERP)
+###############################################################################
+
+# Class path for the BCEL library (version 5.0)
+# See http://jakarta.apache.org/bcel
+
+bcel.path bcel.jar
+
+# Class path for the SERP library (version 1.4.2)
+# See http://serp.sourceforge.net
+
+serp.path serp.jar
+
+# Class path for the Javassist library (version 2.6)
+# See http://www.csg.is.titech.ac.jp/~chiba/javassist
+
+javassist.path javassist.jar
+
+# Class path for the Janino compiler (version 2.3.3)
+# See http://www.janino.net/
+
+janino.path janino.jar
+
+###############################################################################
+# DOCUMENTATION
+###############################################################################
+
+# URLs of external Javadocs (JDK)
+
+jdk.url     http://java.sun.com/j2se/1.4.2/docs/api
+
+
+workspace  ${basedir}/../..
+global.build.properties  ${basedir}/../global.build.properties
+user.build.properties  ${basedir}/../user.build.properties
diff --git a/asmx/build.xml b/asmx/build.xml
new file mode 100644
index 0000000..d5dbc7e
--- /dev/null
+++ b/asmx/build.xml
@@ -0,0 +1,518 @@
+<!--
+ ! ASM: a very small and fast Java bytecode manipulation framework
+ ! Copyright (c) 2000-2005 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.
+-->
+
+<project name="ASM" default="compile">
+
+  <!-- ==================================== -->
+  <!-- ======== PROPERTY DEFINITION ======= -->
+  <!-- ==================================== -->
+
+  <property file="${user.home}/asm-build.properties"/>
+  <property file="build.config"/>
+  <property file="build.properties"/>
+  <property file="${global.build.properties}"/>
+  <property file="${user.build.properties}"/>
+
+  <property name="src"                value="${basedir}/src"/>
+  <property name="test"               value="${basedir}/test"/>
+  <property name="doc"                value="${basedir}/doc"/>
+  <property name="jdoc"               value="${basedir}/jdoc"/>
+  <property name="web"                value="${basedir}/web"/>
+  <property name="examples"           value="${basedir}/examples"/>
+  <property name="examples.common"    value="${examples}/common"/>
+  <property name="config"             value="${basedir}/config"/>
+  <property name="archive"            value="${basedir}/archive"/>
+
+  <property name="out"                value="${basedir}/output"/>
+  <property name="out.build"          value="${out}/build"/>
+  <property name="out.instr"          value="${out}/instr"/>
+  <property name="out.dist"           value="${out}/dist"/>
+  <property name="out.dist.lib"       value="${out.dist}/lib"/>
+  <property name="out.dist.doc"       value="${out.dist}/doc"/>
+  <property name="out.dist.jdoc"      value="${out.dist.doc}/javadoc"/>
+  <property name="out.dist.examples"  value="${out.dist}/examples"/>
+  <property name="out.dist.externals" value="${out.dist}/externals"/>
+  <property name="out.test"           value="${out}/test"/>
+  <property name="out.tmp"            value="${out}/tmp"/>
+  <property name="out.zip"            value="${out}/zip"/>
+
+  <target name="properties">
+    <condition property="examples.exist">
+      <available file="${examples}"/>
+    </condition>
+
+    <condition property="web.exist">
+      <available file="${web}/build.xml"/>
+    </condition>
+
+    <condition property="paths.configured">
+      <and>
+        <isset property="objectweb.ant.tasks.path"/>
+      </and>
+    </condition>
+
+    <condition property="product.shrink">
+      <not><isset property="product.noshrink"/></not>
+    </condition>
+  </target>
+
+  <!-- ================================== -->
+  <!-- ========  INITIALIZATION   ======= -->
+  <!-- ================================== -->
+
+  <target name="check" unless="paths.configured">
+    <echo message="The 'build.properties' file must be configured"/>
+    <fail/>
+  </target>
+
+  <target name="init" depends="properties,check">
+
+    <path id="classpath">
+      <pathelement location="${out.build}"/>
+    </path>
+
+    <path id="cobertura.classpath">
+       <pathelement path="${cobertura.path}"/>
+    </path>
+
+    <taskdef name="multipleAnt"
+             classname="org.objectweb.util.ant.MultipleAnt"
+             classpath="${objectweb.ant.tasks.path}"/>
+
+    <taskdef name="javadocMultipleLink"
+             classname="org.objectweb.util.ant.JavadocMultipleLink"
+             classpath="${objectweb.ant.tasks.path}"/>
+
+    <taskdef name="multipleCopy"
+             classname="org.objectweb.util.ant.MultipleCopy"
+             classpath="${objectweb.ant.tasks.path}"/>
+
+    <taskdef classpathref="cobertura.classpath" resource="tasks.properties"/>
+  </target>
+
+  <!-- =================================== -->
+  <!-- ==========    COMPILE    ========== -->
+  <!-- =================================== -->
+
+  <target name="compile-debug" depends="init">
+    <mkdir dir="${out.tmp}"/>
+    <javac destdir="${out.tmp}" debug="on" includeantruntime="false">
+      <classpath>
+        <pathelement location="${out.tmp}"/>
+        <pathelement path="${annotations-compiler}/dist/lib/javac.jar"/>
+      </classpath>
+      <src path="${src}"/>
+      <include name="**/*.java"/>
+    </javac>
+  </target>
+
+  <target name="shrink" depends="compile-debug" if="product.shrink">
+    <echo message="Shrinking"/>
+    <java classname="org.objectweb.asm.optimizer.Shrinker">
+      <classpath>
+        <pathelement location="${out.tmp}"/>
+      </classpath>
+      <arg value="${src}/org/objectweb/asm/optimizer/shrink.properties"/>
+      <arg value="${out.tmp}"/>
+      <arg value="${out.build}"/>
+    </java>
+  </target>
+
+  <target name="noshrink" depends="compile-debug" if="product.noshrink">
+    <copy todir="${out.build}">
+      <fileset dir="${out.tmp}"/>
+    </copy>
+  </target>
+
+  <target name="bin" depends="compile"/>
+
+  <!-- only one of shrink and noshrink will actually get executed,
+       depending on whether product.noshrink is set.
+  -->
+  <target name="compile" depends="compile-debug,shrink,noshrink">
+    <copy todir="${basedir}/bin">
+      <fileset dir="${out.build}"/>
+    </copy>
+  </target>
+
+  <!-- =================================== -->
+  <!-- ==========      TEST     ========== -->
+  <!-- =================================== -->
+
+  <target name="test" depends="compile">
+    <condition property="classes" value="${out.build}">
+      <not><isset property="debug"/></not>
+    </condition>
+    <condition property="classes" value="${out.tmp}">
+      <isset property="debug"/>
+    </condition>
+    <condition property="asm.test" value="${java.home}/lib/rt.jar,${out.test}/cases">
+      <not><isset property="asm.test"/></not>
+    </condition>
+    <ant antfile="${test}/build.xml" target="test" inheritRefs="true"/>
+  </target>
+
+  <target name="test.report">
+    <junitreport todir="${out.test}/reports">
+      <fileset dir="${out.test}/reports">
+        <include name="TEST-*.xml"/>
+      </fileset>
+      <report todir="${out.test}/reports"/>
+    </junitreport>
+  </target>
+
+  <target name="coverage" depends="compile">
+    <delete file="cobertura.ser"/>
+    <delete dir="${out.instr}"/>
+    <cobertura-instrument todir="${out.instr}">
+      <ignore regex="java.lang.Error"/>
+      <fileset dir="${out.tmp}">
+        <include name="**/*.class"/>
+        <exclude name="**/optimizer/*.class"/>
+        <exclude name="**/xml/Processor*.class"/>
+        <exclude name="**/*Test*.class" />
+      </fileset>
+    </cobertura-instrument>
+    <copy todir="${out.instr}" preservelastmodified="yes">
+      <fileset dir="${out.tmp}"/>
+    </copy>
+    <property name="coverage" value="yes"/>
+    <property name="classes" value="${out.instr}"/>
+    <property name="asm.test.class" value="pkg"/>
+    <condition property="asm.test" value="${out.test}/cases">
+      <not><isset property="asm.test"/></not>
+    </condition>
+    <condition property="test.type" value="conform">
+      <not><isset property="test.type"/></not>
+    </condition>
+    <ant antfile="${test}/build.xml" target="test" inheritRefs="true"/>
+  </target>
+
+  <target name="coverage.check" depends="init">
+    <cobertura-check branchrate="100" linerate="100">
+    </cobertura-check>
+  </target>
+
+  <target name="coverage.report" depends="init">
+    <cobertura-report destdir="${out}/coverage" srcdir="${src}" format="xml"/>
+    <cobertura-report destdir="${out}/coverage">
+      <fileset dir="${src}">
+        <include name="**/*.java"/>
+        <exclude name="**/asm/optimizer/**/*.java"/>
+      </fileset>
+    </cobertura-report>
+  </target>
+
+  <!-- =================================== -->
+  <!-- ==========      DIST     ========== -->
+  <!-- =================================== -->
+
+  <target name="dist.init">
+    <mkdir dir="${out.dist}"/>
+    <mkdir dir="${out.dist.doc}"/>
+    <mkdir dir="${out.dist.jdoc}"/>
+    <mkdir dir="${out.dist.lib}"/>
+  </target>
+
+  <target name="dist.version">
+    <tstamp>
+      <format property="product.build.time" pattern="yyyyMMdd.HHmmss"/>
+    </tstamp>
+
+    <condition property="product.artifact" value="${product.version}">
+      <not><isset property="product.snapshot"/></not>
+    </condition>
+    <condition property="product.artifact" value="${product.build.time}">
+      <isset property="product.snapshot"/>
+    </condition>
+
+    <condition property="plugin.artifact" value="${plugin.version}">
+      <not><isset property="product.snapshot"/></not>
+    </condition>
+    <condition property="plugin.artifact" value="${plugin.version}.${product.build.time}">
+      <isset property="product.snapshot"/>
+    </condition>
+  </target>
+
+  <target name="jar" depends="dist.init,dist.version,compile,shrink">
+    <multipleAnt dir="${archive}"/>
+    <java classname="org.objectweb.asm.optimizer.JarOptimizer">
+      <classpath refid="classpath"/>
+      <arg value="${out.dist.lib}"/>
+    </java>
+  </target>
+
+  <target name="jdoc" depends="init,dist.init">
+    <copy todir="${out.dist.doc}"
+          preservelastmodified="yes"
+          includeEmptyDirs="false">
+      <fileset dir="${doc}">
+        <include name="**/*"/>
+        <exclude name="**/*.fig"/>
+      </fileset>
+    </copy>
+    <multipleAnt dir="${jdoc}"/>
+  </target>
+
+  <target name="examples" depends="init,dist.init" if="examples.exist">
+    <mkdir dir="${out.dist.examples}"/>
+    <copy todir="${out.dist.examples}"
+          preservelastmodified="yes"
+          includeEmptyDirs="yes">
+      <fileset dir="${examples}">
+        <exclude name="common"/>
+        <exclude name="common/**/*"/>
+      </fileset>
+    </copy>
+
+    <multipleCopy file="${examples}/common/build.xml"
+                  toDir="${out.dist.examples}"
+                  notReplace="yes"
+                  preservelastmodified="yes">
+      <include name="*"/>
+      <exclude name="etc"/>
+      <exclude name="lib"/>
+    </multipleCopy>
+
+    <copy toDir="${out.dist.examples}" preservelastmodified="yes">
+      <fileset dir="${examples.common}">
+        <include name="**/*"/>
+        <exclude name="build.xml"/>
+      </fileset>
+    </copy>
+    <replace dir="${out.dist.examples}/etc" token="@product.version@" value="${product.version}"/>
+
+    <copy todir="${out.dist}">
+      <fileset dir=".">
+        <include name="externals/*.jar"/>
+      </fileset>
+    </copy>
+  </target>
+
+  <!--
+  <target name="eclipse.generate" depends="dist.version">
+  </target>
+
+  <target name="eclipse.plugin" depends="jar,eclipse.generate">
+    <jar zipfile="${out}/org.objectweb.asm_${plugin.artifact}.jar">
+      <zipfileset dir="${basedir}">
+        <include name="plugin.xml"/>
+        <include name="META-INF/MANIFEST.MF"/>
+        <include name="**/asm-${product.artifact}.jar"/>
+        <include name="**/asm-tree-${product.artifact}.jar"/>
+        <include name="**/asm-analysis-${product.artifact}.jar"/>
+        <include name="**/asm-util-${product.artifact}.jar"/>
+        <include name="**/asm-commons-${product.artifact}.jar"/>
+        <include name="**/asm-attrs-${product.artifact}.jar"/>
+      </zipfileset>
+      <manifest>
+        <attribute name="Bundle-ManifestVersion" value="2"/>
+        <attribute name="Bundle-Name" value="ASM Framework"/>
+        <attribute name="Bundle-SymbolicName" value="org.objectweb.asm;singleton:=true"/>
+        <attribute name="Bundle-Version" value="${plugin.artifact}"/>
+        <attribute name="Bundle-ClassPath" value="output/dist/lib/asm-${product.artifact}.jar,
+output/dist/lib/asm-tree-${product.artifact}.jar,
+output/dist/lib/asm-analysis-${product.artifact}.jar,
+output/dist/lib/asm-commons-${product.artifact}.jar,
+output/dist/lib/asm-attrs-${product.artifact}.jar,
+output/dist/lib/asm-util-${product.artifact}.jar"/>
+        <attribute name="Bundle-Vendor" value="ObjectWeb.org"/>
+        <attribute name="Bundle-Localization" value="plugin"/>
+        <attribute name="Export-Package" value="org.objectweb.asm,
+org.objectweb.asm.attrs,
+org.objectweb.asm.commons,
+org.objectweb.asm.signature,
+org.objectweb.asm.tree,
+org.objectweb.asm.tree.analysis,
+org.objectweb.asm.util,
+org.objectweb.asm.xml"/>
+        <attribute name="Eclipse-AutoStart" value="true"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="eclipse.feature" depends="eclipse.plugin">
+    <echo file="${out}/feature.xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<feature id="org.objectweb.asm.feature" label="ASM Framework"
+      version="${plugin.artifact}" provider-name="ObjectWeb.org">
+
+   <description url="http://asm.objectweb.org/eclipse/asm/index.html">
+Feature contains ASM Java bytecode manipulation framework runtime.
+   </description>
+
+   <copyright>
+Copyright (c) 2000-2005 INRIA, France Telecom.
+All rights reserved.
+   </copyright>
+
+   <license>
+Copyright (c) 2000-2005 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.
+   </license>
+
+   <url>
+      <update label="ASM Framework" url="http://download.forge.objectweb.org/eclipse-update/site.xml"/>
+   </url>
+
+   <plugin id="org.objectweb.asm" download-size="0" install-size="0" version="${plugin.artifact}"/>
+
+</feature>]]></echo>
+
+    <jar jarfile="${out}/org.objectweb.asm.feature_${plugin.artifact}.jar">
+      <fileset file="${out}/feature.xml"/>
+    </jar>
+  </target>
+
+  <target name="eclipse.site" depends="eclipse.feature">
+    <echo file="${out}/site.xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
+<site>
+   <description url="http://asm.objectweb.org/eclipse/asm/index.html">
+      ASM Framework
+   </description>
+
+   <feature url="features/org.objectweb.asm.feature_${plugin.artifact}.jar" id="org.objectweb.asm.feature" version="${plugin.artifact}">
+      <category name="asm"/>
+   </feature>
+
+   <category-def name="asm" label="ASM Framework"/>
+
+   <archive path="plugins/org.objectweb.asm_${plugin.artifact}.jar" url="http://download.forge.objectweb.org/asm/org.objectweb.asm_${plugin.artifact}.jar"/>
+</site>]]></echo>
+
+    <zip zipfile="${out}/org.objectweb.asm.update_${plugin.artifact}.zip">
+      <zipfileset dir="${out}" includes="site.xml"/>
+      <zipfileset dir="${out}" includes="org.objectweb.asm.feature_${plugin.artifact}.jar" prefix="features"/>
+      <zipfileset dir="${out}" includes="org.objectweb.asm_${plugin.artifact}.jar"  prefix="plugins"/>
+    </zip>
+  </target>
+  -->
+
+  <target name="dist" depends="jar,jdoc,examples">
+    <zip zipFile="${out.dist}/src.zip" basedir="${src}" excludes="**/optimizer/**/*"/>
+  </target>
+
+  <!-- =================================== -->
+  <!-- ==========    EXAMPLES   ========== -->
+  <!-- =================================== -->
+
+  <target name="example" depends="jar,examples">
+    <ant inheritAll="false"
+         dir="${out.dist.examples}/${example.name}"
+         target="execute"/>
+  </target>
+
+  <!-- =================================== -->
+  <!-- ==========     ZIP       ========== -->
+  <!-- =================================== -->
+
+  <!-- creates zip files of the different distribution (source, binaries) -->
+
+  <target name="zip" depends="dist">
+    <mkdir dir="${out.zip}"/>
+    <tar destfile="${out.zip}/${product.name}-${product.version}.tar.gz"
+         compression="gzip">
+      <tarfileset dir="${basedir}" prefix="${product.name}-${product.version}">
+        <exclude name="build.config"/>
+        <exclude name="config/**"/>
+        <exclude name="config"/>
+        <exclude name="**/externals/**"/>
+        <exclude name="**/externals"/>
+        <exclude name="**/lib/**"/>
+        <exclude name="**/lib"/>
+        <exclude name="eclipse/**"/>
+        <exclude name="eclipse"/>
+        <exclude name="web/**"/>
+        <exclude name="web"/>
+        <exclude name="**/output/**"/>
+        <exclude name="**/output"/>
+        <exclude name="CVSROOT/**"/>
+        <exclude name="CVSROOT"/>
+      </tarfileset>
+    </tar>
+    <zip zipFile="${out.zip}/${product.name}-${product.version}-bin.zip">
+      <zipfileset dir="${out.dist}" prefix="${product.name}-${product.version}"/>
+    </zip>
+  </target>
+
+  <!-- =================================== -->
+  <!-- ==========     CLEAN     ========== -->
+  <!-- =================================== -->
+
+  <!-- remove all directories -->
+
+  <target name="clean.web" if="web.exist">
+    <ant dir="${web}" target="clean"/>
+  </target>
+
+  <target name="clean" depends="properties,clean.web">
+    <delete dir="${basedir}/bin" />
+    <delete dir="${out.build}"/>
+    <delete dir="${out.dist}"/>
+    <delete dir="${out.tmp}"/>
+    <delete dir="${out.zip}"/>
+    <delete dir="${out.test}"/>
+    <delete dir="${out.instr}"/>
+  </target>
+
+  <!-- ==================================== -->
+  <!-- ==========     HELP       ========== -->
+  <!-- ==================================== -->
+
+  <target name="help">
+    <echo message="The available targets are the following:"/>
+    <echo message="  compile: compiles the product into ${out.build}"/>
+    <echo message="  dist: creates the product's distributions into ${out.dist}"/>
+    <echo message="  zip: creates the product's distributions zip files into ${out.zip}"/>
+    <echo message="  clean: removes all generated files."/>
+    <echo message="  jar: creates all jars in ${out.dist.lib}"/>
+    <echo message="  test: run all tests"/>
+    <echo message=""/>
+    <echo message="There are some options to run tests:"/>
+    <echo message="  -Dtest.group=&lt;group name&gt;  Only a group of test: The default target of"/>
+    <echo message="    the xml file is called the test.group contains the xml file name with"/>
+    <echo message="    directory ex: ant -Dtest.group=conform/toto test =&gt; calls the default"/>
+    <echo message="    target of the file ${test}/conform/toto.xml"/>
+    <echo message="  -Dtest.type=&lt;type name&gt;  Only a type of test: conform, deviance, stress,"/>
+    <echo message="    thread or perf. The test.type properties contains the directory name of"/>
+    <echo message="    the test type ex: ant -Dtest.type=conform test"/>
+    <echo message="  -Dtest.name=&lt;test name&gt;  Only a single test. The target &lt;test name&gt; is called"/>
+  </target>
+
+</project>
diff --git a/asmx/config/README.txt b/asmx/config/README.txt
new file mode 100644
index 0000000..4978bab
--- /dev/null
+++ b/asmx/config/README.txt
@@ -0,0 +1,2 @@
+This directory contains the external libraries required for building the 
+product, but which are not necessary to use it.
diff --git a/asmx/config/ow_util_ant_tasks.jar b/asmx/config/ow_util_ant_tasks.jar
new file mode 100644
index 0000000..9d918a0
--- /dev/null
+++ b/asmx/config/ow_util_ant_tasks.jar
Binary files differ
diff --git a/asmx/core/asm-2.2.2.jar b/asmx/core/asm-2.2.2.jar
new file mode 100644
index 0000000..fa779c2
--- /dev/null
+++ b/asmx/core/asm-2.2.2.jar
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/AnnotationVisitor.class b/asmx/core/org/objectweb/asm/AnnotationVisitor.class
new file mode 100644
index 0000000..51e5faf
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/AnnotationVisitor.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/AnnotationWriter.class b/asmx/core/org/objectweb/asm/AnnotationWriter.class
new file mode 100644
index 0000000..90934d5
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/AnnotationWriter.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Attribute.class b/asmx/core/org/objectweb/asm/Attribute.class
new file mode 100644
index 0000000..210ff23
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/Attribute.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ByteVector.class b/asmx/core/org/objectweb/asm/ByteVector.class
new file mode 100644
index 0000000..bd4ceb1
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/ByteVector.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassAdapter.class b/asmx/core/org/objectweb/asm/ClassAdapter.class
new file mode 100644
index 0000000..209b538
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/ClassAdapter.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassReader.class b/asmx/core/org/objectweb/asm/ClassReader.class
new file mode 100644
index 0000000..743a513
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/ClassReader.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassVisitor.class b/asmx/core/org/objectweb/asm/ClassVisitor.class
new file mode 100644
index 0000000..7ca9b1d
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/ClassVisitor.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/ClassWriter.class b/asmx/core/org/objectweb/asm/ClassWriter.class
new file mode 100644
index 0000000..35b2fb9
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/ClassWriter.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Edge.class b/asmx/core/org/objectweb/asm/Edge.class
new file mode 100644
index 0000000..2e9d945
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/Edge.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/FieldVisitor.class b/asmx/core/org/objectweb/asm/FieldVisitor.class
new file mode 100644
index 0000000..bc8c7a4
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/FieldVisitor.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/FieldWriter.class b/asmx/core/org/objectweb/asm/FieldWriter.class
new file mode 100644
index 0000000..efddb42
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/FieldWriter.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Handler.class b/asmx/core/org/objectweb/asm/Handler.class
new file mode 100644
index 0000000..59852b3
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/Handler.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Item.class b/asmx/core/org/objectweb/asm/Item.class
new file mode 100644
index 0000000..dec3d57
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/Item.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Label.class b/asmx/core/org/objectweb/asm/Label.class
new file mode 100644
index 0000000..5dbaaf5
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/Label.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/MethodAdapter.class b/asmx/core/org/objectweb/asm/MethodAdapter.class
new file mode 100644
index 0000000..d64648a
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/MethodAdapter.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/MethodVisitor.class b/asmx/core/org/objectweb/asm/MethodVisitor.class
new file mode 100644
index 0000000..1f04a8b
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/MethodVisitor.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/MethodWriter.class b/asmx/core/org/objectweb/asm/MethodWriter.class
new file mode 100644
index 0000000..5bf49f4
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/MethodWriter.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Opcodes.class b/asmx/core/org/objectweb/asm/Opcodes.class
new file mode 100644
index 0000000..00a7da6
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/Opcodes.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/Type.class b/asmx/core/org/objectweb/asm/Type.class
new file mode 100644
index 0000000..d8ef9b9
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/Type.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/signature/SignatureReader.class b/asmx/core/org/objectweb/asm/signature/SignatureReader.class
new file mode 100644
index 0000000..9f433df
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/signature/SignatureReader.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/signature/SignatureVisitor.class b/asmx/core/org/objectweb/asm/signature/SignatureVisitor.class
new file mode 100644
index 0000000..1319d12
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/signature/SignatureVisitor.class
Binary files differ
diff --git a/asmx/core/org/objectweb/asm/signature/SignatureWriter.class b/asmx/core/org/objectweb/asm/signature/SignatureWriter.class
new file mode 100644
index 0000000..4a95194
--- /dev/null
+++ b/asmx/core/org/objectweb/asm/signature/SignatureWriter.class
Binary files differ
diff --git a/asmx/janino.jar b/asmx/janino.jar
new file mode 100644
index 0000000..7874a29
--- /dev/null
+++ b/asmx/janino.jar
Binary files differ
diff --git a/asmx/javassist.jar b/asmx/javassist.jar
new file mode 100644
index 0000000..1672012
--- /dev/null
+++ b/asmx/javassist.jar
Binary files differ
diff --git a/asmx/ow_util_ant_tasks.jar b/asmx/ow_util_ant_tasks.jar
new file mode 100644
index 0000000..4b5dc8e
--- /dev/null
+++ b/asmx/ow_util_ant_tasks.jar
Binary files differ
diff --git a/asmx/serp.jar b/asmx/serp.jar
new file mode 100644
index 0000000..144667e
--- /dev/null
+++ b/asmx/serp.jar
Binary files differ
diff --git a/asmx/src/org/objectweb/asm/AnnotationVisitor.java b/asmx/src/org/objectweb/asm/AnnotationVisitor.java
new file mode 100644
index 0000000..8226ae8
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/AnnotationVisitor.java
@@ -0,0 +1,88 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * A visitor to visit a Java annotation. The methods of this interface 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 interface AnnotationVisitor {
+
+    /**
+     * 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}.
+     */
+    void visit(String name, Object 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.
+     */
+    void visitEnum(String name, String desc, String 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 non null visitor to visit the actual nested annotation value.
+     *         <i>The nested annotation value must be fully visited before
+     *         calling other methods on this annotation visitor</i>.
+     */
+    AnnotationVisitor visitAnnotation(String name, String desc);
+
+    /**
+     * Visits an array value of the annotation.
+     * 
+     * @param name the value name.
+     * @return a non null visitor to visit the actual array value elements. 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>.
+     */
+    AnnotationVisitor visitArray(String name);
+
+    /**
+     * Visits the end of the annotation.
+     */
+    void visitEnd();
+}
diff --git a/asmx/src/org/objectweb/asm/AnnotationWriter.java b/asmx/src/org/objectweb/asm/AnnotationWriter.java
new file mode 100644
index 0000000..918e234
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/AnnotationWriter.java
@@ -0,0 +1,311 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * An {@link AnnotationVisitor} that generates annotations in bytecode form.
+ * 
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+final class AnnotationWriter implements 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)
+    {
+        this.cw = cw;
+        this.named = named;
+        this.bv = bv;
+        this.parent = parent;
+        this.offset = offset;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the AnnotationVisitor interface
+    // ------------------------------------------------------------------------
+
+    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);
+        }
+    }
+
+    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));
+    }
+
+    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);
+    }
+
+    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);
+    }
+
+    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 out where the annotations must be put.
+     */
+    static void put(final AnnotationWriter[] panns, final ByteVector out) {
+        int size = 1 + 2 * panns.length;
+        for (int i = 0; i < panns.length; ++i) {
+            size += panns[i] == null ? 0 : panns[i].getSize();
+        }
+        out.putInt(size).putByte(panns.length);
+        for (int i = 0; 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/asmx/src/org/objectweb/asm/Attribute.java b/asmx/src/org/objectweb/asm/Attribute.java
new file mode 100644
index 0000000..c55eb93
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Attribute.java
@@ -0,0 +1,254 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.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(
+        ClassReader cr,
+        int off,
+        int len,
+        char[] buf,
+        int codeOff,
+        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(
+        ClassWriter cw,
+        byte[] code,
+        int len,
+        int maxStack,
+        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/asmx/src/org/objectweb/asm/ByteVector.java b/asmx/src/org/objectweb/asm/ByteVector.java
new file mode 100644
index 0000000..270778b
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/ByteVector.java
@@ -0,0 +1,293 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.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();
+        if (length + 2 + charLength > data.length) {
+            enlarge(2 + charLength);
+        }
+        int len = length;
+        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/asmx/src/org/objectweb/asm/ClassAdapter.java b/asmx/src/org/objectweb/asm/ClassAdapter.java
new file mode 100644
index 0000000..d9024d7
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/ClassAdapter.java
@@ -0,0 +1,130 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * An empty {@link ClassVisitor} that delegates to another {@link ClassVisitor}.
+ * This class can be used as a super class to quickly implement useful class
+ * adapter classes, just by overriding the necessary methods.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassAdapter implements ClassVisitor {
+  
+    /**
+     * The {@link ClassVisitor} to which this adapter delegates calls.
+     */
+    protected ClassVisitor cv;
+
+    /**
+     * Constructs a new {@link ClassAdapter} object.
+     * 
+     * @param cv the class visitor to which this adapter must delegate calls.
+     */
+    public ClassAdapter(final ClassVisitor cv) {
+        this.cv = cv;
+    }
+
+    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);
+    }
+
+    public void visitSource(final String source, final String debug) {
+        cv.visitSource(source, debug);
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        cv.visitOuterClass(owner, name, desc);
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        return cv.visitAnnotation(desc, visible);
+    }
+
+    //jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+        boolean visible,
+        boolean inCode)
+    {
+        return cv.visitTypeAnnotation(desc, visible, inCode);
+    }
+    // end jaime
+
+    public void visitAttribute(final Attribute attr) {
+        cv.visitAttribute(attr);
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        cv.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        return cv.visitField(access, name, desc, signature, value);
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        return cv.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    public void visitEnd() {
+        cv.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/ClassReader.java b/asmx/src/org/objectweb/asm/ClassReader.java
new file mode 100644
index 0000000..cf6595e
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/ClassReader.java
@@ -0,0 +1,2146 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+import com.sun.tools.javac.code.TargetType;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 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 performance (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;
+
+    /**
+     * The start index of each bootstrap method.
+     */
+    int[] bootstrapMethods;
+
+    // ------------------------------------------------------------------------
+    // 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;
+        // parses the constant pool
+        items = new int[readUnsignedShort(off + 8)];
+        int ll = items.length;
+        strings = new String[ll];
+        int max = 0;
+        int index = off + 10;
+        for (int i = 1; i < ll; ++i) {
+            items[i] = index + 1;
+            int tag = b[index];
+            int size;
+            switch (tag) {
+                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;
+    }
+
+    /**
+     * Copies the constant pool data into the given {@link ClassWriter}. Should
+     * be called before the {@link #accept(ClassVisitor,boolean)} 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));
+    }
+
+    /**
+     * Constructs a new {@link ClassReader} object.
+     *
+     * @param name the fully qualified name of the class to be read.
+     * @throws IOException if an exception occurs during reading.
+     */
+    public ClassReader(final String name) throws IOException {
+        this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/')
+                + ".class"));
+    }
+
+    /**
+     * Reads the bytecode of a class.
+     *
+     * @param is an input stream from which to read the class.
+     * @return the bytecode read from the given input stream.
+     * @throws IOException if a problem occurs during reading.
+     */
+    private static byte[] readClass(final InputStream is) throws IOException {
+        if (is == null) {
+            throw new IOException("Class not found");
+        }
+        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;
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // 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 skipDebug <tt>true</tt> if the debug information of the class
+     *        must not be visited. In this case the
+     *        {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
+     *        {@link MethodVisitor#visitLineNumber visitLineNumber} methods will
+     *        not be called.
+     */
+    public void accept(final ClassVisitor classVisitor, final boolean skipDebug)
+    {
+        accept(classVisitor, new Attribute[0], skipDebug);
+    }
+
+    /**
+     * 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 be ignored.
+     * @param skipDebug <tt>true</tt> if the debug information of the class
+     *        must not be visited. In this case the
+     *        {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
+     *        {@link MethodVisitor#visitLineNumber visitLineNumber} methods will
+     *        not be called.
+     */
+    public void accept(
+        final ClassVisitor classVisitor,
+        final Attribute[] attrs,
+        final boolean skipDebug)
+    {
+        byte[] b = this.b; // the bytecode array
+        char[] c = new char[maxStringLength]; // buffer used to read strings
+        int i, j, k; // loop variables
+        int u, v, w; // indexes in b
+        Attribute attr;
+
+        int access;
+        String name;
+        String desc;
+        String attrName;
+        String signature;
+        int anns = 0;
+        int ianns = 0;
+        int xanns = 0;
+        int ixanns = 0;
+        Attribute cattrs = null;
+
+        // visits the header
+        u = header; // u = u2 access_flags
+        access = readUnsignedShort(u);
+        name = readClass(u + 2, c); // name = u2 this_class
+        v = items[readUnsignedShort(u + 4)]; // u + 4 = u2 super_class
+        String superClassName = v == 0 ? null : readUTF8(v, c);
+        String[] implementedItfs = new String[readUnsignedShort(u + 6)];
+                  // u + 6 = u2 interfaces_count;
+        w = 0;
+        u += 8; // u + 8 = interfaces[interfaces_count]
+        for (i = 0; i < implementedItfs.length; ++i) {
+            implementedItfs[i] = readClass(u, c);
+            u += 2;
+        }
+
+        // u = u2 fields_count
+
+        // skips fields and methods
+        v = u;
+        i = readUnsignedShort(v); // i = u2 fields_count
+        v += 2; // v = field_info fields[fields_count]
+        for (; i > 0; --i) {
+            j = readUnsignedShort(v + 6);
+            v += 8;
+            for (; j > 0; --j) {
+                v += 6 + readInt(v + 2);
+            }
+        }
+        i = readUnsignedShort(v); // i = u2 methods_count
+        v += 2; // v = method_info methods[methods_count]
+        for (; i > 0; --i) {
+            j = readUnsignedShort(v + 6);
+            v += 8;
+            for (; j > 0; --j) {
+                v += 6 + readInt(v + 2);
+            }
+        }
+        // reads the class's attributes
+        signature = null;
+        String sourceFile = null;
+        String sourceDebug = null;
+        String enclosingOwner = null;
+        String enclosingName = null;
+        String enclosingDesc = null;
+
+        i = readUnsignedShort(v); // i = u2 attributes_count
+        v += 2; // v = attribute_info attributes[attributes_count]
+        for (; i > 0; --i) {
+            attrName = readUTF8(v, c);
+            if (attrName.equals("SourceFile")) {
+                sourceFile = readUTF8(v + 6, c);
+            } else if (attrName.equals("Deprecated")) {
+                access |= Opcodes.ACC_DEPRECATED;
+            } else if (attrName.equals("Synthetic")) {
+                access |= Opcodes.ACC_SYNTHETIC;
+            } else if (attrName.equals("Annotation")) {
+                access |= Opcodes.ACC_ANNOTATION;
+            } else if (attrName.equals("Enum")) {
+                access |= Opcodes.ACC_ENUM;
+            } else if (attrName.equals("InnerClasses")) {
+                w = v + 6;
+            } else if (attrName.equals("Signature")) {
+                signature = readUTF8(v + 6, c);
+            } else if (attrName.equals("SourceDebugExtension")) {
+                int len = readInt(v + 2);
+                sourceDebug = readUTF(v + 6, len, new char[len]);
+            } else if (attrName.equals("EnclosingMethod")) {
+                enclosingOwner = readClass(v + 6, c);
+                int item = readUnsignedShort(v + 8);
+                if (item != 0) {
+                    enclosingName = readUTF8(items[item], c);
+                    enclosingDesc = readUTF8(items[item] + 2, c);
+                }
+            } else if (attrName.equals("RuntimeVisibleAnnotations")) {
+                anns = v + 6;
+            } else if (attrName.equals("RuntimeInvisibleAnnotations")) {
+                ianns = v + 6;
+            } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
+                xanns = v + 6;
+            } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
+                ixanns = v + 6;
+            } else if (attrName.equals("BootstrapMethods")) {
+                bootstrapMethods = new int[readUnsignedShort(v + 6)];
+                for (j = 0, u = v + 8; j < bootstrapMethods.length; j++) {
+                    bootstrapMethods[j] = u;
+                    u += 2 + readUnsignedShort(u + 2) << 1;
+                }
+            } else {
+                attr = readAttribute(attrs,
+                        attrName,
+                        v + 6,
+                        readInt(v + 2),
+                        c,
+                        -1,
+                        null);
+                if (attr != null) {
+                    attr.next = cattrs;
+                    cattrs = attr;
+                }
+            }
+            v += 6 + readInt(v + 2);
+        }
+        // calls the visit method
+        classVisitor.visit(readInt(4),
+                access,
+                name,
+                signature,
+                superClassName,
+                implementedItfs);
+
+        // calls the visitSource method
+        if (sourceFile != null || sourceDebug != null) {
+            classVisitor.visitSource(sourceFile, sourceDebug);
+        }
+
+        // calls the visitOuterClass method
+        if (enclosingOwner != null) {
+            classVisitor.visitOuterClass(enclosingOwner,
+                    enclosingName,
+                    enclosingDesc);
+        }
+
+        // visits the class annotations
+        for (i = 1; i >= 0; --i) {
+            v = i == 0 ? ianns : anns;
+            if (v != 0) {
+                j = readUnsignedShort(v);
+                v += 2;
+                for (; j > 0; --j) {
+                    desc = readUTF8(v, c);
+                    v += 2;
+                    v = readAnnotationValues(v,
+                            c,
+                            classVisitor.visitAnnotation(desc, i != 0));
+                }
+            }
+        }
+
+        // TODO
+        // visits the class extended annotations
+        for (i = 1; i >= 0; --i) {
+            v = i == 0 ? ixanns : xanns;
+            if (v != 0) {
+                j = readUnsignedShort(v);
+                v += 2;
+                for (; j > 0; --j) {
+                    v = readTypeAnnotationValues(v,
+                            c, classVisitor, i != 0);
+                }
+            }
+        }
+
+        // visits the class attributes
+        while (cattrs != null) {
+            attr = cattrs.next;
+            cattrs.next = null;
+            classVisitor.visitAttribute(cattrs);
+            cattrs = attr;
+        }
+
+        // class the visitInnerClass method
+        if (w != 0) {
+            i = readUnsignedShort(w);
+            w += 2;
+            for (; i > 0; --i) {
+                classVisitor.visitInnerClass(readUnsignedShort(w) == 0
+                        ? null
+                        : readClass(w, c), readUnsignedShort(w + 2) == 0
+                        ? null
+                        : readClass(w + 2, c), readUnsignedShort(w + 4) == 0
+                        ? null
+                        : readUTF8(w + 4, c), readUnsignedShort(w + 6));
+                w += 8;
+            }
+        }
+
+        // visits the fields
+        u = header + 8 + 2 * implementedItfs.length;
+        i = readUnsignedShort(u); // i = u2 fields_count
+        u += 2; // u = field_info[fields_count]
+        for (; i > 0; --i) {
+            access = readUnsignedShort(u);
+            name = readUTF8(u + 2, c);
+            desc = readUTF8(u + 4, c);
+            // visits the field's attributes and looks for a ConstantValue
+            // attribute
+            int fieldValueItem = 0;
+            signature = null;
+            anns = 0;
+            ianns = 0;
+            xanns = 0;
+            ixanns = 0;
+            cattrs = null;
+
+            j = readUnsignedShort(u + 6); // j = u2 attributes_count
+            u += 8; // u = attributes[attributes_count]
+            for (; j > 0; --j) {
+                attrName = readUTF8(u, c);
+                if (attrName.equals("ConstantValue")) {
+                    fieldValueItem = readUnsignedShort(u + 6);
+                } else if (attrName.equals("Synthetic")) {
+                    access |= Opcodes.ACC_SYNTHETIC;
+                } else if (attrName.equals("Deprecated")) {
+                    access |= Opcodes.ACC_DEPRECATED;
+                } else if (attrName.equals("Enum")) {
+                    access |= Opcodes.ACC_ENUM;
+                } else if (attrName.equals("Signature")) {
+                    signature = readUTF8(u + 6, c);
+                } else if (attrName.equals("RuntimeVisibleAnnotations")) {
+                    anns = u + 6;
+                } else if (attrName.equals("RuntimeInvisibleAnnotations")) {
+                    ianns = u + 6;
+                } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
+                    xanns = u + 6;
+                } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
+                    ixanns = u + 6;
+                } else {
+
+                    attr = readAttribute(attrs,
+                            attrName,
+                            u + 6,
+                            readInt(u + 2),
+                            c,
+                            -1,
+                            null);
+                    if (attr != null) {
+                        attr.next = cattrs;
+                        cattrs = attr;
+                    }
+                }
+                u += 6 + readInt(u + 2);
+            }
+            // reads the field's value, if any
+            Object value = (fieldValueItem == 0
+                    ? null
+                    : readConst(fieldValueItem, c));
+            // visits the field
+            FieldVisitor fv = classVisitor.visitField(access,
+                    name,
+                    desc,
+                    signature,
+                    value);
+            // visits the field annotations and attributes
+            if (fv != null) {
+                for (j = 1; j >= 0; --j) {
+                    v = j == 0 ? ianns : anns;
+                    if (v != 0) {
+                        k = readUnsignedShort(v);
+                        v += 2;
+                        for (; k > 0; --k) {
+                            desc = readUTF8(v, c);
+                            v += 2;
+                            v = readAnnotationValues(v,
+                                    c,
+                                    fv.visitAnnotation(desc, j != 0));
+                        }
+                    }
+                }
+                //TODO
+                // now visit the extended annotations
+                if(xanns != 0) {
+                    v = xanns;
+                    k = readUnsignedShort(v);
+                    v += 2;
+                    for(; k > 0; --k) {
+                        v = readTypeAnnotationValues(v,
+                            c, fv, true);
+                    }
+                }
+
+                if(ixanns != 0) {
+                    v = ixanns;
+                    k = readUnsignedShort(v);
+                    v += 2;
+                    for(; k > 0; --k) {
+                        v = readTypeAnnotationValues(v,
+                            c, fv, false);
+                    }
+                }
+
+                while (cattrs != null) {
+                    attr = cattrs.next;
+                    cattrs.next = null;
+                    fv.visitAttribute(cattrs);
+                    cattrs = attr;
+                }
+                fv.visitEnd();
+            }
+        }
+
+        // visits the methods
+        i = readUnsignedShort(u);
+        u += 2;
+        for (; i > 0; --i) {
+            int u0 = u + 6;
+            access = readUnsignedShort(u);
+            name = readUTF8(u + 2, c);
+            desc = readUTF8(u + 4, c);
+            signature = null;
+            anns = 0;
+            ianns = 0;
+            //jaime
+            xanns = 0;
+            ixanns = 0;
+            // end jaime
+            int dann = 0;
+            int mpanns = 0;
+            int impanns = 0;
+            cattrs = null;
+            v = 0;
+            w = 0;
+
+            // looks for Code and Exceptions attributes
+            j = readUnsignedShort(u + 6);
+            u += 8;
+            for (; j > 0; --j) {
+                attrName = readUTF8(u, c);
+                u += 2;
+                int attrSize = readInt(u);
+                u += 4;
+                if (attrName.equals("Code")) {
+                    v = u;
+                } else if (attrName.equals("Exceptions")) {
+                    w = u;
+                } else if (attrName.equals("Synthetic")) {
+                    access |= Opcodes.ACC_SYNTHETIC;
+                } else if (attrName.equals("Varargs")) {
+                    access |= Opcodes.ACC_VARARGS;
+                } else if (attrName.equals("Bridge")) {
+                    access |= Opcodes.ACC_BRIDGE;
+                } else if (attrName.equals("Deprecated")) {
+                    access |= Opcodes.ACC_DEPRECATED;
+                } else if (attrName.equals("Signature")) {
+                    signature = readUTF8(u, c);
+                } else if (attrName.equals("AnnotationDefault")) {
+                    dann = u;
+                } else if (attrName.equals("RuntimeVisibleAnnotations")) {
+                    anns = u;
+                } else if (attrName.equals("RuntimeInvisibleAnnotations")) {
+                    ianns = u;
+                } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
+                    xanns = u;
+                } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
+                    ixanns = u;
+                } else if (attrName.equals("RuntimeVisibleParameterAnnotations")) {
+                    mpanns = u;
+                } else if (attrName.equals("RuntimeInvisibleParameterAnnotations")) {
+                    impanns = u;
+                } else {
+                    attr = readAttribute(attrs,
+                            attrName,
+                            u,
+                            attrSize,
+                            c,
+                            -1,
+                            null);
+                    if (attr != null) {
+                        attr.next = cattrs;
+                        cattrs = attr;
+                    }
+                }
+                u += attrSize;
+            }
+            // reads declared exceptions
+            String[] exceptions;
+            if (w == 0) {
+                exceptions = null;
+            } else {
+                exceptions = new String[readUnsignedShort(w)];
+                w += 2;
+                for (j = 0; j < exceptions.length; ++j) {
+                    exceptions[j] = readClass(w, c);
+                    w += 2;
+                }
+            }
+
+            // visits the method's code, if any
+            MethodVisitor mv = classVisitor.visitMethod(access,
+                    name,
+                    desc,
+                    signature,
+                    exceptions);
+
+            if (mv != null) {
+                /*
+                 * 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 (mv instanceof MethodWriter) {
+                    MethodWriter mw = (MethodWriter) mv;
+                    if (mw.cw.cr == this) {
+                        if (signature == mw.signature) {
+                            boolean sameExceptions = false;
+                            if (exceptions == null) {
+                                sameExceptions = mw.exceptionCount == 0;
+                            } else {
+                                if (exceptions.length == mw.exceptionCount) {
+                                    sameExceptions = true;
+                                    for (j = exceptions.length - 1; j >= 0; --j)
+                                    {
+                                        w -= 2;
+                                        if (mw.exceptions[j] != readUnsignedShort(w))
+                                        {
+                                            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 = u0;
+                                mw.classReaderLength = u - u0;
+                                continue;
+                            }
+                        }
+                    }
+                }
+                if (dann != 0) {
+                    AnnotationVisitor dv = mv.visitAnnotationDefault();
+                    readAnnotationValue(dann, c, null, dv);
+                    dv.visitEnd();
+                }
+                for (j = 1; j >= 0; --j) {
+                    w = j == 0 ? ianns : anns;
+                    if (w != 0) {
+                        k = readUnsignedShort(w);
+                        w += 2;
+                        for (; k > 0; --k) {
+                            desc = readUTF8(w, c);
+                            w += 2;
+                            w = readAnnotationValues(w,
+                                    c,
+                                    mv.visitAnnotation(desc, j != 0));
+                        }
+                    }
+                }
+
+                // now visit the method extended annotations
+                for (j = 1; j >= 0; --j) {
+                    w = j == 0 ? ixanns : xanns;
+                    if (w != 0) {
+                        k = readUnsignedShort(w);
+                        w += 2;
+                        for (; k > 0; --k) {
+                            w = readTypeAnnotationValues(w,
+                                  c, mv, j != 0);
+                        }
+                    }
+                }
+
+                if (mpanns != 0) {
+                    readParameterAnnotations(mpanns, c, true, mv);
+                }
+                if (impanns != 0) {
+                    readParameterAnnotations(impanns, c, false, mv);
+                }
+
+                while (cattrs != null) {
+                    attr = cattrs.next;
+                    cattrs.next = null;
+                    mv.visitAttribute(cattrs);
+                    cattrs = attr;
+                }
+            }
+
+            if (mv != null && v != 0) {
+                int maxStack = readUnsignedShort(v);
+                int maxLocals = readUnsignedShort(v + 2);
+                int codeLength = readInt(v + 4);
+                v += 8;
+
+                int codeStart = v;
+                int codeEnd = v + codeLength;
+
+                mv.visitCode();
+
+                // 1st phase: finds the labels
+                int label;
+                Label[] labels = new Label[codeLength + 1];
+                while (v < codeEnd) {
+                    int opcode = b[v] & 0xFF;
+                    switch (ClassWriter.TYPE[opcode]) {
+                        case ClassWriter.NOARG_INSN:
+                        case ClassWriter.IMPLVAR_INSN:
+                            v += 1;
+                            break;
+                        case ClassWriter.LABEL_INSN:
+                            label = v - codeStart + readShort(v + 1);
+                            if (labels[label] == null) {
+                                labels[label] = new Label();
+                            }
+                            v += 3;
+                            break;
+                        case ClassWriter.LABELW_INSN:
+                            label = v - codeStart + readInt(v + 1);
+                            if (labels[label] == null) {
+                                labels[label] = new Label();
+                            }
+                            v += 5;
+                            break;
+                        case ClassWriter.WIDE_INSN:
+                            opcode = b[v + 1] & 0xFF;
+                            if (opcode == Opcodes.IINC) {
+                                v += 6;
+                            } else {
+                                v += 4;
+                            }
+                            break;
+                        case ClassWriter.TABL_INSN:
+                            // skips 0 to 3 padding bytes
+                            w = v - codeStart;
+                            v = v + 4 - (w & 3);
+                            // reads instruction
+                            label = w + readInt(v);
+                            v += 4;
+                            if (labels[label] == null) {
+                                labels[label] = new Label();
+                            }
+                            j = readInt(v);
+                            v += 4;
+                            j = readInt(v) - j + 1;
+                            v += 4;
+                            for (; j > 0; --j) {
+                                label = w + readInt(v);
+                                v += 4;
+                                if (labels[label] == null) {
+                                    labels[label] = new Label();
+                                }
+                            }
+                            break;
+                        case ClassWriter.LOOK_INSN:
+                            // skips 0 to 3 padding bytes
+                            w = v - codeStart;
+                            v = v + 4 - (w & 3);
+                            // reads instruction
+                            label = w + readInt(v);
+                            v += 4;
+                            if (labels[label] == null) {
+                                labels[label] = new Label();
+                            }
+                            j = readInt(v);
+                            v += 4;
+                            for (; j > 0; --j) {
+                                v += 4; // skips key
+                                label = w + readInt(v);
+                                v += 4;
+                                if (labels[label] == null) {
+                                    labels[label] = new Label();
+                                }
+                            }
+                            break;
+                        case ClassWriter.VAR_INSN:
+                        case ClassWriter.SBYTE_INSN:
+                        case ClassWriter.LDC_INSN:
+                            v += 2;
+                            break;
+                        case ClassWriter.SHORT_INSN:
+                        case ClassWriter.LDCW_INSN:
+                        case ClassWriter.FIELDORMETH_INSN:
+                        case ClassWriter.TYPE_INSN:
+                        case ClassWriter.IINC_INSN:
+                            v += 3;
+                            break;
+                        case ClassWriter.ITFMETH_INSN:
+                        case ClassWriter.INDY:
+                            v += 5;
+                            break;
+                        // case MANA_INSN:
+                        default:
+                            v += 4;
+                            break;
+                    }
+                }
+                // parses the try catch entries
+                j = readUnsignedShort(v);
+                v += 2;
+                for (; j > 0; --j) {
+                    label = readUnsignedShort(v);
+                    Label start = labels[label];
+                    if (start == null) {
+                        labels[label] = start = new Label();
+                    }
+                    label = readUnsignedShort(v + 2);
+                    Label end = labels[label];
+                    if (end == null) {
+                        labels[label] = end = new Label();
+                    }
+                    label = readUnsignedShort(v + 4);
+                    Label handler = labels[label];
+                    if (handler == null) {
+                        labels[label] = handler = new Label();
+                    }
+
+                    int type = readUnsignedShort(v + 6);
+                    if (type == 0) {
+                        mv.visitTryCatchBlock(start, end, handler, null);
+                    } else {
+                        mv.visitTryCatchBlock(start,
+                                end,
+                                handler,
+                                readUTF8(items[type], c));
+                    }
+                    v += 8;
+                }
+                // parses the local variable, line number tables, and code
+                // attributes
+                int varTable = 0;
+                int varTypeTable = 0;
+                cattrs = null;
+                j = readUnsignedShort(v);
+                v += 2;
+                for (; j > 0; --j) {
+                    attrName = readUTF8(v, c);
+                    if (attrName.equals("LocalVariableTable")) {
+                        if (!skipDebug) {
+                            varTable = v + 6;
+                            k = readUnsignedShort(v + 6);
+                            w = v + 8;
+                            for (; k > 0; --k) {
+                                label = readUnsignedShort(w);
+                                if (labels[label] == null) {
+                                    labels[label] = new Label();
+                                }
+                                label += readUnsignedShort(w + 2);
+                                if (labels[label] == null) {
+                                    labels[label] = new Label();
+                                }
+                                w += 10;
+                            }
+                        }
+                    } else if (attrName.equals("LocalVariableTypeTable")) {
+                        varTypeTable = v + 6;
+                    } else if (attrName.equals("LineNumberTable")) {
+                        if (!skipDebug) {
+                            k = readUnsignedShort(v + 6);
+                            w = v + 8;
+                            for (; k > 0; --k) {
+                                label = readUnsignedShort(w);
+                                if (labels[label] == null) {
+                                    labels[label] = new Label();
+                                }
+                                labels[label].line = readUnsignedShort(w + 2);
+                                w += 4;
+                            }
+                        }
+                    } else if (attrName.equals("RuntimeInvisibleTypeAnnotations")) {
+                        k = readUnsignedShort(v + 6);
+                        w = v + 8;
+                        for (; k > 0; --k) {
+                            w = readTypeAnnotationValues(w,
+                                    c, mv, false);
+                        }
+                    } else if (attrName.equals("RuntimeVisibleTypeAnnotations")) {
+                        k = readUnsignedShort(v + 6);
+                        w = v + 8;
+                        for (; k > 0; --k) {
+                            w = readTypeAnnotationValues(w,
+                                    c, mv, true);
+                        }
+                    } else {
+                        for (k = 0; k < attrs.length; ++k) {
+                            if (attrs[k].type.equals(attrName)) {
+                                attr = attrs[k].read(this,
+                                        v + 6,
+                                        readInt(v + 2),
+                                        c,
+                                        codeStart - 8,
+                                        labels);
+                                if (attr != null) {
+                                    attr.next = cattrs;
+                                    cattrs = attr;
+                                }
+                            }
+                        }
+                    }
+                    v += 6 + readInt(v + 2);
+                }
+
+                // 2nd phase: visits each instruction
+                v = codeStart;
+                PrecompiledMethodVisitor pmv =
+                    (mv instanceof PrecompiledMethodVisitor)
+                    ? (PrecompiledMethodVisitor) mv : null;
+                Label l;
+                while (v < codeEnd) {
+                    w = v - codeStart;
+                    if (pmv != null)
+                        pmv.visitCurrentPosition(w);
+                    l = labels[w];
+                    if (l != null) {
+                        mv.visitLabel(l);
+                        if (!skipDebug && l.line > 0) {
+                            mv.visitLineNumber(l.line, l);
+                        }
+                    }
+                    int opcode = b[v] & 0xFF;
+                    switch (ClassWriter.TYPE[opcode]) {
+                        case ClassWriter.NOARG_INSN:
+                            mv.visitInsn(opcode);
+                            v += 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);
+                            }
+                            v += 1;
+                            break;
+                        case ClassWriter.LABEL_INSN:
+                            mv.visitJumpInsn(opcode, labels[w
+                                    + readShort(v + 1)]);
+                            v += 3;
+                            break;
+                        case ClassWriter.LABELW_INSN:
+                            mv.visitJumpInsn(opcode - 33, labels[w
+                                    + readInt(v + 1)]);
+                            v += 5;
+                            break;
+                        case ClassWriter.WIDE_INSN:
+                            opcode = b[v + 1] & 0xFF;
+                            if (opcode == Opcodes.IINC) {
+                                mv.visitIincInsn(readUnsignedShort(v + 2),
+                                        readShort(v + 4));
+                                v += 6;
+                            } else {
+                                mv.visitVarInsn(opcode,
+                                        readUnsignedShort(v + 2));
+                                v += 4;
+                            }
+                            break;
+                        case ClassWriter.TABL_INSN:
+                            // skips 0 to 3 padding bytes
+                            v = v + 4 - (w & 3);
+                            // reads instruction
+                            label = w + readInt(v);
+                            v += 4;
+                            int min = readInt(v);
+                            v += 4;
+                            int max = readInt(v);
+                            v += 4;
+                            Label[] table = new Label[max - min + 1];
+                            for (j = 0; j < table.length; ++j) {
+                                table[j] = labels[w + readInt(v)];
+                                v += 4;
+                            }
+                            mv.visitTableSwitchInsn(min,
+                                    max,
+                                    labels[label],
+                                    table);
+                            break;
+                        case ClassWriter.LOOK_INSN:
+                            // skips 0 to 3 padding bytes
+                            v = v + 4 - (w & 3);
+                            // reads instruction
+                            label = w + readInt(v);
+                            v += 4;
+                            j = readInt(v);
+                            v += 4;
+                            int[] keys = new int[j];
+                            Label[] values = new Label[j];
+                            for (j = 0; j < keys.length; ++j) {
+                                keys[j] = readInt(v);
+                                v += 4;
+                                values[j] = labels[w + readInt(v)];
+                                v += 4;
+                            }
+                            mv.visitLookupSwitchInsn(labels[label],
+                                    keys,
+                                    values);
+                            break;
+                        case ClassWriter.VAR_INSN:
+                            mv.visitVarInsn(opcode, b[v + 1] & 0xFF);
+                            v += 2;
+                            break;
+                        case ClassWriter.SBYTE_INSN:
+                            mv.visitIntInsn(opcode, b[v + 1]);
+                            v += 2;
+                            break;
+                        case ClassWriter.SHORT_INSN:
+                            mv.visitIntInsn(opcode, readShort(v + 1));
+                            v += 3;
+                            break;
+                        case ClassWriter.LDC_INSN:
+                            mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c));
+                            v += 2;
+                            break;
+                        case ClassWriter.LDCW_INSN:
+                            mv.visitLdcInsn(readConst(readUnsignedShort(v + 1),
+                                    c));
+                            v += 3;
+                            break;
+                        case ClassWriter.FIELDORMETH_INSN:
+                        case ClassWriter.ITFMETH_INSN:
+                            int cpIndex = items[readUnsignedShort(v + 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) {
+                                v += 5;
+                            } else {
+                                v += 3;
+                            }
+                            break;
+                        case ClassWriter.TYPE_INSN:
+                            mv.visitTypeInsn(opcode, readClass(v + 1, c));
+                            v += 3;
+                            break;
+                        case ClassWriter.IINC_INSN:
+                            mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]);
+                            v += 3;
+                            break;
+                        case ClassWriter.INDY:
+                            cpIndex = items[readUnsignedShort(v + 1)];
+                            int bsmIndex = bootstrapMethods[readUnsignedShort(cpIndex)];
+                            Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c);
+                            int bsmArgCount = readUnsignedShort(bsmIndex + 2);
+                            Object[] bsmArgs = new Object[bsmArgCount];
+                            bsmIndex += 4;
+                            for (j = 0; j < bsmArgCount; j++) {
+                                bsmArgs[j] = readConst(readUnsignedShort(bsmIndex), c);
+                                bsmIndex += 2;
+                            }
+                            cpIndex = items[readUnsignedShort(cpIndex + 2)];
+                            iname = readUTF8(cpIndex, c);
+                            idesc = readUTF8(cpIndex + 2, c);
+                            mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs);
+                            v += 5;
+                            break;
+                        // case MANA_INSN:
+                        default:
+                            mv.visitMultiANewArrayInsn(readClass(v + 1, c),
+                                    b[v + 3] & 0xFF);
+                            v += 4;
+                            break;
+                    }
+                }
+                l = labels[codeEnd - codeStart];
+                if (l != null) {
+                    if (pmv != null)
+                        pmv.visitCurrentPosition(codeEnd - codeStart);
+                    mv.visitLabel(l);
+                }
+
+                // visits the local variable tables
+                if (!skipDebug && varTable != 0) {
+                    int[] typeTable = null;
+                    if (varTypeTable != 0) {
+                        w = varTypeTable;
+                        k = readUnsignedShort(w) * 3;
+                        w += 2;
+                        typeTable = new int[k];
+                        while (k > 0) {
+                            typeTable[--k] = w + 6; // signature
+                            typeTable[--k] = readUnsignedShort(w + 8); // index
+                            typeTable[--k] = readUnsignedShort(w); // start
+                            w += 10;
+                        }
+                    }
+                    w = varTable;
+                    k = readUnsignedShort(w);
+                    w += 2;
+                    for (; k > 0; --k) {
+                        int start = readUnsignedShort(w);
+                        int length = readUnsignedShort(w + 2);
+                        int index = readUnsignedShort(w + 8);
+                        String vsignature = null;
+                        if (typeTable != null) {
+                            for (int a = 0; a < typeTable.length; a += 3) {
+                                if (typeTable[a] == start
+                                        && typeTable[a + 1] == index)
+                                {
+                                    vsignature = readUTF8(typeTable[a + 2], c);
+                                    break;
+                                }
+                            }
+                        }
+                        mv.visitLocalVariable(readUTF8(w + 4, c),
+                                readUTF8(w + 6, c),
+                                vsignature,
+                                labels[start],
+                                labels[start + length],
+                                index);
+                        w += 10;
+                    }
+                }
+                // visits the other attributes
+                while (cattrs != null) {
+                    attr = cattrs.next;
+                    cattrs.next = null;
+                    mv.visitAttribute(cattrs);
+                    cattrs = attr;
+                }
+                // visits the max stack and max locals values
+                mv.visitMaxs(maxStack, maxLocals);
+            }
+
+            if (mv != null) {
+                mv.visitEnd();
+            }
+        }
+
+        // visits the end of the class
+        classVisitor.visitEnd();
+    }
+
+    /**
+     * 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 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 char[] buf,
+        final boolean visible,
+        final MethodVisitor mv)
+    {
+        int n = b[v++] & 0xFF;
+        for (int i = 0; i < n; ++i) {
+            int j = readUnsignedShort(v);
+            v += 2;
+            for (; j > 0; --j) {
+                String desc = readUTF8(v, buf);
+                v += 2;
+                AnnotationVisitor av = mv.visitParameterAnnotation(i,
+                        desc,
+                        visible);
+                v = readAnnotationValues(v, buf, 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 av the visitor that must visit the values.
+     * @return the end offset of the annotations values.
+     */
+    private int readAnnotationValues(
+        int v,
+        final char[] buf,
+        final AnnotationVisitor av)
+    {
+        int i = readUnsignedShort(v);
+        v += 2;
+        for (; i > 0; --i) {
+            String name = readUTF8(v, buf);
+            v += 2;
+            v = readAnnotationValue(v, buf, name, av);
+        }
+        av.visitEnd();
+        return v;
+    }
+   /**
+    * Reads the values and reference info of an extended 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 mv the visitor to generate the visitor that must visit the values.
+    * @param visible {@code true} if the annotation is visible at runtime.
+    * @return the end offset of the annotations values.
+    * @author jaimeq
+    */
+    private int readTypeAnnotationValues(
+        int v,
+        final char[] buf,
+        final MemberVisitor mv,
+        final boolean visible)
+    {
+        // first handle
+        //
+        // u1 target_type
+        // { ...
+        // } reference_info
+        //
+
+        int target_type_value = readByte(v);
+        v += 1;
+
+        Integer offset = null;
+        Integer location_length = null;
+        List<TypePathEntry> locations = new ArrayList<TypePathEntry>();
+        Integer start_pc = null;
+        Integer length = null;
+        Integer index = null;
+        Integer param_index = null;
+        Integer bound_index = null;
+        Integer type_index = null;
+        Integer exception_index = null;
+        Integer table_length = null;
+
+        TargetType target_type = TargetType.fromTargetTypeValue(target_type_value);
+
+        switch(target_type) {
+        // type test (instanceof)
+        // object creation
+        // constructor/method reference receiver
+        // {
+        //   u2 offset;
+        // } reference_info;
+        case INSTANCEOF:
+        case NEW:
+        case CONSTRUCTOR_REFERENCE:
+        case METHOD_REFERENCE:
+          offset = readUnsignedShort(v);
+          v += 2;
+          break;
+
+        // method receiver
+        // {
+        // } reference_info;
+        case METHOD_RECEIVER:
+          break;
+
+        // local variable
+        // u2 table_length;
+        // {
+        //   u2 start_pc;
+        //   u2 length;
+        //   u2 index;
+        // } reference_info;
+        case LOCAL_VARIABLE:
+        // resource variable
+        case RESOURCE_VARIABLE:
+          table_length = readUnsignedShort(v);
+          v += 2;
+          assert table_length == 1; // FIXME
+          start_pc = readUnsignedShort(v);
+          v += 2;
+          length = readUnsignedShort(v);
+          v += 2;
+          index = readUnsignedShort(v);
+          v += 2;
+          break;
+
+        // method return type
+        // {
+        // } reference_info;
+        case METHOD_RETURN:
+          break;
+
+        // method parameter
+        // {
+        //   u1 param;
+        // } reference_info;
+        case METHOD_FORMAL_PARAMETER:
+          param_index = readByte(v);
+          v++;
+          break;
+
+        // field
+        // {
+        // } reference_info;
+        case FIELD:
+          break;
+
+        // class type parameter bound
+        // method type parameter bound
+        // {
+        //   u1 param_index;
+        //   u1 bound_index;
+        // } reference_info;
+        case CLASS_TYPE_PARAMETER_BOUND:
+        case METHOD_TYPE_PARAMETER_BOUND:
+          param_index = readByte(v);
+          v++;
+          bound_index = readByte(v);
+          v++;
+          break;
+
+        // class extends/implements
+        // exception type in throws
+        // {
+        //    u1 type_index;
+        // } reference_info;
+        case CLASS_EXTENDS:
+          type_index = readUnsignedShort(v);
+          if (type_index == 0xFFFF) type_index = -1;
+          v += 2;
+          break;
+        case THROWS:
+          type_index = readUnsignedShort(v);
+          v += 2;
+          break;
+        case EXCEPTION_PARAMETER:
+          exception_index = readUnsignedShort(v);
+          v += 2;
+          break;
+
+        // typecast
+        // type argument in constructor call
+        // type argument in method call
+        // type argument in constructor reference
+        // type argument in method reference
+        // {
+        //   u2 offset;
+        //   u1 type_index;
+        // } reference_info;
+        case CAST:
+        case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
+        case METHOD_INVOCATION_TYPE_ARGUMENT:
+        case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
+        case METHOD_REFERENCE_TYPE_ARGUMENT:
+          offset = readUnsignedShort(v);
+          v += 2;
+
+          type_index = readByte(v);
+          v++;
+          break;
+
+        // method type parameter
+        // {
+        //    u1 param_index;
+        // } reference_info;
+        case CLASS_TYPE_PARAMETER:
+        case METHOD_TYPE_PARAMETER:
+          param_index = readByte(v);
+          v++;
+          break;
+
+        default: throw new RuntimeException(
+              "Unrecognized target type: " + target_type);
+        }
+
+        // now read in the location information
+        {
+            location_length = readByte(v);
+            v += 1;
+            for (int m = 0; m < location_length; m++) {
+              int loctag = readByte(v);
+              int locarg = readByte(v + 1);
+              v += TypePathEntry.bytesPerEntry;
+              locations.add(TypePathEntry.fromBinary(loctag, locarg));
+            }
+        }
+
+        String desc = readUTF8(v, buf);
+        v += 2;
+        TypeAnnotationVisitor xav = mv.visitTypeAnnotation(desc, visible, false);
+
+        xav.visitXTargetType(target_type_value);
+        if (start_pc != null) {
+            xav.visitXStartPc(start_pc);
+        }
+        if (length != null) {
+            xav.visitXLength(length);
+        }
+        if (index != null) {
+            xav.visitXIndex(index);
+        }
+        if (offset != null) {
+            xav.visitXOffset(offset);
+        }
+        if (type_index != null) {
+            xav.visitXTypeIndex(type_index);
+        }
+        if (param_index != null) {
+            xav.visitXParamIndex(param_index);
+        }
+        if (bound_index != null) {
+            xav.visitXBoundIndex(bound_index);
+        }
+        if (exception_index != null) {
+            xav.visitXExceptionIndex(exception_index);
+        }
+        if (location_length != null) {
+            xav.visitXLocationLength(location_length);
+        }
+        for (TypePathEntry location : locations) {
+            xav.visitXLocation(location);
+        }
+        // Visit the annotation name and save space for the values count.
+        xav.visitXNameAndArgsSize();
+
+        // then read annotation values
+        int i = readUnsignedShort(v);
+        v += 2;
+        for (; i > 0; --i) {
+            String name = readUTF8(v, buf);
+            v += 2;
+            // can use the same method as for declaration annotations because
+            // the first part of an extended annotation matches the normal
+            // annotations
+            v = readAnnotationValue(v, buf, name, xav);
+        }
+
+        xav.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;
+        switch (readByte(v++)) {
+            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
+                boolean b = readInt(items[readUnsignedShort(v)]) == 0;
+                av.visit(name, b ? 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
+                String desc = readUTF8(v, buf);
+                v += 2;
+                v = readAnnotationValues(v, buf, av.visitAnnotation(name, desc));
+                break;
+            case '[': // array_value
+                int size = readUnsignedShort(v);
+                v += 2;
+                if (size == 0) {
+                    av.visitArray(name).visitEnd();
+                    return v;
+                }
+                switch (readByte(v++)) {
+                    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--;
+                        AnnotationVisitor aav = av.visitArray(name);
+                        for (i = size; i > 0; --i) {
+                            v = readAnnotationValue(v, buf, null, aav);
+                        }
+                        aav.visitEnd();
+                }
+        }
+        return v;
+    }
+
+    /**
+     * 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 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];
+    }
+
+    /**
+     * 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);
+        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, int utfLen, char[] buf) {
+        int endIndex = index + utfLen;
+        byte[] b = this.b;
+        int strLen = 0;
+        int c, d, e;
+        while (index < endIndex) {
+            c = b[index++] & 0xFF;
+            switch (c >> 4) {
+                case 0:
+                case 1:
+                case 2:
+                case 3:
+                case 4:
+                case 5:
+                case 6:
+                case 7:
+                    // 0xxxxxxx
+                    buf[strLen++] = (char) c;
+                    break;
+                case 12:
+                case 13:
+                    // 110x xxxx 10xx xxxx
+                    d = b[index++];
+                    buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F));
+                    break;
+                default:
+                    // 1110 xxxx 10xx xxxx 10xx xxxx
+                    d = b[index++];
+                    e = b[index++];
+                    buf[strLen++] = (char) (((c & 0x0F) << 12)
+                            | ((d & 0x3F) << 6) | (e & 0x3F));
+                    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} or {@link Type} 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:
+                String s = readUTF8(index, buf);
+                return Type.getType(s.charAt(0) == '[' ? s : "L" + s + ";");
+            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/asmx/src/org/objectweb/asm/ClassVisitor.java b/asmx/src/org/objectweb/asm/ClassVisitor.java
new file mode 100644
index 0000000..a6e12a0
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/ClassVisitor.java
@@ -0,0 +1,195 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * A visitor to visit a Java class. The methods of this interface 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 interface ClassVisitor extends MemberVisitor {
+
+    /**
+     * 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>.
+     */
+    void visit(
+        int version,
+        int access,
+        String name,
+        String signature,
+        String superName,
+        String[] 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>.
+     */
+    void visitSource(String source, String 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.
+     */
+    void visitOuterClass(String owner, String name, String 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 non null visitor to visit the annotation values.
+     */
+    AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+    /**
+     * Visits a non standard attribute of the class.
+     * 
+     * @param attr an attribute.
+     */
+    void visitAttribute(Attribute 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>.
+     * @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.
+     */
+    void visitInnerClass(
+        String name,
+        String outerName,
+        String innerName,
+        int 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.
+     */
+    FieldVisitor visitField(
+        int access,
+        String name,
+        String desc,
+        String signature,
+        Object value);
+
+    /**
+     * 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.
+     */
+    MethodVisitor visitMethod(
+        int access,
+        String name,
+        String desc,
+        String signature,
+        String[] exceptions);
+
+    /**
+     * 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.
+     */
+    void visitEnd();
+}
diff --git a/asmx/src/org/objectweb/asm/ClassWriter.java b/asmx/src/org/objectweb/asm/ClassWriter.java
new file mode 100644
index 0000000..f3718c6
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/ClassWriter.java
@@ -0,0 +1,1506 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.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 implements ClassVisitor {
+
+    /**
+     * The type of instructions without any argument.
+     */
+    final static int NOARG_INSN = 0;
+
+    /**
+     * The type of instructions with an signed byte argument.
+     */
+    final static int SBYTE_INSN = 1;
+
+    /**
+     * The type of instructions with an signed short argument.
+     */
+    final static int SHORT_INSN = 2;
+
+    /**
+     * The type of instructions with a local variable index argument.
+     */
+    final static int VAR_INSN = 3;
+
+    /**
+     * The type of instructions with an implicit local variable index argument.
+     */
+    final static int IMPLVAR_INSN = 4;
+
+    /**
+     * The type of instructions with a type descriptor argument.
+     */
+    final static int TYPE_INSN = 5;
+
+    /**
+     * The type of field and method invocations instructions.
+     */
+    final static int FIELDORMETH_INSN = 6;
+
+    /**
+     * The type of the INVOKEINTERFACE instruction.
+     */
+    final static int ITFMETH_INSN = 7;
+
+    /**
+     * The type of instructions with a 2 bytes bytecode offset label.
+     */
+    final static int LABEL_INSN = 8;
+
+    /**
+     * The type of instructions with a 4 bytes bytecode offset label.
+     */
+    final static int LABELW_INSN = 9;
+
+    /**
+     * The type of the LDC instruction.
+     */
+    final static int LDC_INSN = 10;
+
+    /**
+     * The type of the LDC_W and LDC2_W instructions.
+     */
+    final static int LDCW_INSN = 11;
+
+    /**
+     * The type of the IINC instruction.
+     */
+    final static int IINC_INSN = 12;
+
+    /**
+     * The type of the TABLESWITCH instruction.
+     */
+    final static int TABL_INSN = 13;
+
+    /**
+     * The type of the LOOKUPSWITCH instruction.
+     */
+    final static int LOOK_INSN = 14;
+
+    /**
+     * The type of the MULTIANEWARRAY instruction.
+     */
+    final static int MANA_INSN = 15;
+
+    /**
+     * The type of the WIDE instruction.
+     */
+    final static int WIDE_INSN = 16;
+
+    /**
+     * 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.
+     */
+    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 hash {@link #items items}.
+     */
+    Item key;
+
+    /**
+     * A reusable key used to look for items in the hash {@link #items items}.
+     */
+    Item key2;
+
+    /**
+     * A reusable key used to look for items in the hash {@link #items items}.
+     */
+    Item key3;
+
+    /**
+     * A reusable key used to look for items in the hash {@link #items items}.
+     */
+    Item key4;
+
+    /**
+     * 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 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;
+
+    //jaime
+    private TypeAnnotationWriter xanns;
+    private TypeAnnotationWriter ixanns;
+    //end jaime
+
+    /**
+     * 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#next} 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#next} 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#next} 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#next} 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;
+
+    // ------------------------------------------------------------------------
+    // Static initializer
+    // ------------------------------------------------------------------------
+
+    /**
+     * Computes the instruction types of JVM opcodes.
+     */
+    static {
+        int i;
+        byte[] b = new byte[220];
+        String s = "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD"
+                + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+                + "AAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAA"
+                + "AAAAGGGGGGGHSFBFAAFFAAQPIIJJIIIIIIIIIIIIIIIIII";
+        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] = INDY;
+        //
+        // // 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 ClassWriter} object.
+     *
+     * @param computeMaxs <tt>true</tt> if the maximum stack size and the
+     *        maximum number of local variables must be automatically computed.
+     *        If this flag is <tt>true</tt>, 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.
+     */
+    public ClassWriter(final boolean computeMaxs) {
+        this(computeMaxs, false);
+    }
+
+    /**
+     * Constructs a new {@link ClassWriter} object.
+     *
+     * @param computeMaxs <tt>true</tt> if the maximum stack size and the
+     *        maximum number of local variables must be automatically computed.
+     *        If this flag is <tt>true</tt>, 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.
+     * @param skipUnknownAttributes <b>Deprecated</b>. The value of this
+     *        parameter is ignored.
+     */
+    public ClassWriter(
+        final boolean computeMaxs,
+        final boolean skipUnknownAttributes)
+    {
+        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 = computeMaxs;
+    }
+
+    /**
+     * 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 a custom
+     * {@link ClassAdapter} or 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 computeMaxs <tt>true</tt> if the maximum stack size and the
+     *        maximum number of local variables must be automatically computed.
+     *        If this flag is <tt>true</tt>, 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.
+     */
+    public ClassWriter(
+        final ClassReader classReader,
+        final boolean computeMaxs)
+    {
+        this(computeMaxs, false);
+        classReader.copyPool(this);
+        this.cr = classReader;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the ClassVisitor interface
+    // ------------------------------------------------------------------------
+
+    @Override
+    public 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);
+        if (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 void visitSource(final String file, final String debug) {
+        if (file != null) {
+            sourceFile = newUTF8(file);
+        }
+        if (debug != null) {
+            sourceDebug = new ByteVector().putUTF8(debug);
+        }
+    }
+
+    @Override
+    public 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 AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        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;
+    }
+
+    //jaime
+    @Override
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+        boolean visible,
+        boolean inCode)
+    {
+        ByteVector bv = new ByteVector();
+        TypeAnnotationWriter xaw = new TypeAnnotationWriter(this, true, bv, bv, desc);
+        if(visible) {
+            xaw.next = xanns;
+            xanns = xaw;
+        } else {
+            xaw.next = ixanns;
+            ixanns = xaw;
+        }
+
+        return xaw;
+    }
+    //end jaime
+
+    @Override
+    public void visitAttribute(final Attribute attr) {
+        attr.next = attrs;
+        attrs = attr;
+    }
+
+    @Override
+    public 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 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 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);
+    }
+
+    @Override
+    public 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() {
+        // 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 = fb.next;
+        }
+        int nbMethods = 0;
+        MethodWriter mb = firstMethod;
+        while (mb != null) {
+            ++nbMethods;
+            size += mb.getSize();
+            mb = mb.next;
+        }
+        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 (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
+                && (version & 0xffff) < Opcodes.V1_5)
+        {
+            ++attributeCount;
+            size += 6;
+            newUTF8("Synthetic");
+        }
+        if (version == Opcodes.V1_4) {
+            if ((access & Opcodes.ACC_ANNOTATION) != 0) {
+                ++attributeCount;
+                size += 6;
+                newUTF8("Annotation");
+            }
+            if ((access & Opcodes.ACC_ENUM) != 0) {
+                ++attributeCount;
+                size += 6;
+                newUTF8("Enum");
+            }
+        }
+        if (innerClasses != null) {
+            ++attributeCount;
+            size += 8 + innerClasses.length;
+            newUTF8("InnerClasses");
+        }
+        if (anns != null) {
+            ++attributeCount;
+            size += 8 + anns.getSize();
+            newUTF8("RuntimeVisibleAnnotations");
+        }
+        if (ianns != null) {
+            ++attributeCount;
+            size += 8 + ianns.getSize();
+            newUTF8("RuntimeInvisibleAnnotations");
+        }
+
+        if(xanns != null) {
+          ++attributeCount;
+          size += 8 + xanns.getSize();
+          newUTF8("RuntimeVisibleTypeAnnotations");
+        }
+        if(ixanns != null) {
+          ++attributeCount;
+          size += 8 + ixanns.getSize();
+          newUTF8("RuntimeInvisibleTypeAnnotations");
+        }
+
+        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);
+        out.putShort(access).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 = fb.next;
+        }
+        out.putShort(nbMethods);
+        mb = firstMethod;
+        while (mb != null) {
+            mb.put(out);
+            mb = mb.next;
+        }
+        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 (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
+                && (version & 0xffff) < Opcodes.V1_5)
+        {
+            out.putShort(newUTF8("Synthetic")).putInt(0);
+        }
+        if (version == Opcodes.V1_4) {
+            if ((access & Opcodes.ACC_ANNOTATION) != 0) {
+                out.putShort(newUTF8("Annotation")).putInt(0);
+            }
+            if ((access & Opcodes.ACC_ENUM) != 0) {
+                out.putShort(newUTF8("Enum")).putInt(0);
+            }
+        }
+        if (innerClasses != null) {
+            out.putShort(newUTF8("InnerClasses"));
+            out.putInt(innerClasses.length + 2).putShort(innerClassesCount);
+            out.putByteArray(innerClasses.data, 0, innerClasses.length);
+        }
+        if (anns != null) {
+            out.putShort(newUTF8("RuntimeVisibleAnnotations"));
+            anns.put(out);
+        }
+        if (ianns != null) {
+            out.putShort(newUTF8("RuntimeInvisibleAnnotations"));
+            ianns.put(out);
+        }
+        if(xanns != null) {
+          out.putShort(newUTF8("RuntimeVisibleTypeAnnotations"));
+          xanns.put(out);
+        }
+        if(ixanns != null) {
+          out.putShort(newUTF8("RuntimeInvisibleTypeAnnotations"));
+          ixanns.put(out);
+        }
+        if (attrs != null) {
+            attrs.put(this, null, 0, -1, -1, out);
+        }
+        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 the index of a new or already existing class reference item.
+     */
+    public int newClass(final String value) {
+        return newClassItem(value).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.
+     */
+    private 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 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 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)
+    {
+        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.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(Float.floatToIntBits(value));
+            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);
+            put(result);
+            index += 2;
+        }
+        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(Double.doubleToLongBits(value));
+            result = new Item(index, key);
+            put(result);
+            index += 2;
+        }
+        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) {
+        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.index;
+    }
+
+    /**
+     * 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 && !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 > 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 two bytes and one short into the constant pool.
+     *
+     * @param b a byte.
+     * @param s1 a short.
+     * @param s2 another short.
+     */
+    private void put112(final int b1, final int b2, final int s) {
+        pool.put11(b1, b2).putShort(s);
+    }
+
+    /**
+     * 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);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/Edge.java b/asmx/src/org/objectweb/asm/Edge.java
new file mode 100644
index 0000000..c0bf108
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Edge.java
@@ -0,0 +1,57 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * An edge in the control flow graph of a method body. See {@link Label Label}.
+ * 
+ * @author Eric Bruneton
+ */
+class Edge {
+
+    /**
+     * 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.
+     */
+    int stackSize;
+
+    /**
+     * 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/asmx/src/org/objectweb/asm/FieldVisitor.java b/asmx/src/org/objectweb/asm/FieldVisitor.java
new file mode 100644
index 0000000..078aa56
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/FieldVisitor.java
@@ -0,0 +1,62 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * A visitor to visit a Java field. The methods of this interface must be called
+ * in the following order: ( <tt>visitAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* <tt>visitEnd</tt>.
+ * 
+ * @author Eric Bruneton
+ */
+public interface FieldVisitor extends MemberVisitor {
+    /**
+     * 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 non null visitor to visit the annotation values.
+     */
+    AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+    /**
+     * Visits a non standard attribute of the field.
+     * 
+     * @param attr an attribute.
+     */
+    void visitAttribute(Attribute 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.
+     */
+    void visitEnd();
+}
diff --git a/asmx/src/org/objectweb/asm/FieldWriter.java b/asmx/src/org/objectweb/asm/FieldWriter.java
new file mode 100644
index 0000000..6a193c8
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/FieldWriter.java
@@ -0,0 +1,332 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * An {@link FieldVisitor} that generates Java fields in bytecode form.
+ * 
+ * @author Eric Bruneton
+ */
+final class FieldWriter implements FieldVisitor {
+  
+    /**
+     * Next field writer (see {@link ClassWriter#firstField firstField}).
+     */
+    FieldWriter next;
+
+    /**
+     * The class writer to which this field must be added.
+     */
+    private ClassWriter cw;
+
+    /**
+     * Access flags of this field.
+     */
+    private int access;
+
+    /**
+     * The index of the constant pool item that contains the name of this
+     * method.
+     */
+    private int name;
+
+    /**
+     * The index of the constant pool item that contains the descriptor of this
+     * field.
+     */
+    private 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;
+
+    //jaime
+    private TypeAnnotationWriter xanns;
+    private TypeAnnotationWriter ixanns;
+    //end jaime
+
+    /**
+     * 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>.
+     */
+    protected FieldWriter(
+        final ClassWriter cw,
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        if (cw.firstField == null) {
+            cw.firstField = this;
+        } else {
+            cw.lastField.next = this;
+        }
+        cw.lastField = this;
+        this.cw = cw;
+        this.access = access;
+        this.name = cw.newUTF8(name);
+        this.desc = cw.newUTF8(desc);
+        if (signature != null) {
+            this.signature = cw.newUTF8(signature);
+        }
+        if (value != null) {
+            this.value = cw.newConstItem(value).index;
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the FieldVisitor interface
+    // ------------------------------------------------------------------------
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        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;
+    }
+
+    //jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible,
+        final boolean inCode)
+    {
+        ByteVector bv = new ByteVector();
+        TypeAnnotationWriter xaw = 
+          new TypeAnnotationWriter(cw, true, bv, bv, desc);
+        if(visible) {
+            xaw.next = xanns;
+            xanns = xaw;
+        } else {
+            xaw.next = ixanns;
+            ixanns = xaw;
+        }
+        return xaw;
+    }
+    // end jaime
+
+    public void visitAttribute(final Attribute attr) {
+        attr.next = attrs;
+        attrs = attr;
+    }
+
+    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
+                && (cw.version & 0xffff) < Opcodes.V1_5)
+        {
+            cw.newUTF8("Synthetic");
+            size += 6;
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            cw.newUTF8("Deprecated");
+            size += 6;
+        }
+        if (cw.version == Opcodes.V1_4 && (access & Opcodes.ACC_ENUM) != 0) {
+            cw.newUTF8("Enum");
+            size += 6;
+        }
+        if (signature != 0) {
+            cw.newUTF8("Signature");
+            size += 8;
+        }
+        if (anns != null) {
+            cw.newUTF8("RuntimeVisibleAnnotations");
+            size += 8 + anns.getSize();
+        }
+        if (ianns != null) {
+            cw.newUTF8("RuntimeInvisibleAnnotations");
+            size += 8 + ianns.getSize();
+        }
+        //jaime
+        if (xanns != null) {
+            cw.newUTF8("RuntimeVisibleTypeAnnotations");
+            size += 8 + xanns.getSize();
+        }
+        if (ixanns != null) {
+            cw.newUTF8("RuntimeInvisibleTypeAnnotations");
+            size += 8 + ixanns.getSize();
+        }
+        // end jaime
+        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) {
+        out.putShort(access).putShort(name).putShort(desc);
+        int attributeCount = 0;
+        if (value != 0) {
+            ++attributeCount;
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0
+                && (cw.version & 0xffff) < Opcodes.V1_5)
+        {
+            ++attributeCount;
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            ++attributeCount;
+        }
+        if (cw.version == Opcodes.V1_4 && (access & Opcodes.ACC_ENUM) != 0) {
+            ++attributeCount;
+        }
+        if (signature != 0) {
+            ++attributeCount;
+        }
+        if (anns != null) {
+            ++attributeCount;
+        }
+        if (ianns != null) {
+            ++attributeCount;
+        }
+        //jaime
+        if (xanns != null) {
+            ++attributeCount;
+        }
+        if (ixanns != null) {
+            ++attributeCount;
+        }
+        // end jaime
+
+        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
+                && (cw.version & 0xffff) < Opcodes.V1_5)
+        {
+            out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+        }
+        if (cw.version == Opcodes.V1_4 && (access & Opcodes.ACC_ENUM) != 0) {
+            out.putShort(cw.newUTF8("Enum")).putInt(0);
+        }
+        if (signature != 0) {
+            out.putShort(cw.newUTF8("Signature"));
+            out.putInt(2).putShort(signature);
+        }
+        if (anns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+            anns.put(out);
+        }
+        if (ianns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+            ianns.put(out);
+        }
+
+        //jaime
+        if (xanns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
+            xanns.put(out);
+        }
+        if (ixanns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
+            ixanns.put(out);
+        }
+        // end jaime
+
+        if (attrs != null) {
+            attrs.put(cw, null, 0, -1, -1, out);
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/Handle.java b/asmx/src/org/objectweb/asm/Handle.java
new file mode 100644
index 0000000..a627911
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Handle.java
@@ -0,0 +1,170 @@
+/***
+ * 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 org.objectweb.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 class that owns the field or method designated
+     * 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 class that owns the field or method
+     *            designated 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 class that owns the field or method
+     * designated by this handle.
+     * 
+     * @return the internal name of the class that owns the field or method
+     *         designated 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/asmx/src/org/objectweb/asm/Handler.java b/asmx/src/org/objectweb/asm/Handler.java
new file mode 100644
index 0000000..8bef845
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Handler.java
@@ -0,0 +1,70 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.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;
+}
diff --git a/asmx/src/org/objectweb/asm/Item.java b/asmx/src/org/objectweb/asm/Item.java
new file mode 100644
index 0000000..ed14416
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Item.java
@@ -0,0 +1,325 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.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;
+
+    /**
+     * Value of this item, for a float item.
+     */
+    float floatVal;
+
+    /**
+     * Value of this item, for a double item.
+     */
+    double doubleVal;
+
+    /**
+     * 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;
+        floatVal = i.floatVal;
+        doubleVal = i.doubleVal;
+        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.floatVal = 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.doubleVal = 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.
+     */
+    @SuppressWarnings("fallthrough")
+    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.CLASS:
+            this.intVal = 0;     // intVal of a class must be zero, see visitInnerClass
+        case ClassWriter.UTF8:
+        case ClassWriter.STR:
+        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) {
+        if (i.type == type) {
+            switch (type) {
+                case ClassWriter.INT:
+                    return i.intVal == intVal;
+                case ClassWriter.LONG:
+                    return i.longVal == longVal;
+                case ClassWriter.FLOAT:
+                    return i.floatVal == floatVal;
+                case ClassWriter.DOUBLE:
+                    return i.doubleVal == doubleVal;
+                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:
+                    return i.longVal == longVal;
+                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);
+                }
+                // ClassWriter.FIELD:
+                // ClassWriter.METH:
+                // ClassWriter.IMETH:
+                // ClassWriter.HANDLE_BASE + 1..9
+                default:
+                    return i.strVal1.equals(strVal1)
+                        && i.strVal2.equals(strVal2)
+                        && i.strVal3.equals(strVal3);
+            }
+        }
+        return false;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/Label.java b/asmx/src/org/objectweb/asm/Label.java
new file mode 100644
index 0000000..79291f2
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Label.java
@@ -0,0 +1,299 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.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.
+ * 
+ * @author Eric Bruneton
+ */
+public class Label {
+
+    /**
+     * The line number corresponding to this label, if known.
+     */
+    int line;
+
+    /**
+     * Indicates if the position of this label is known.
+     */
+    boolean resolved;
+
+    /**
+     * The position of this label in the code, if known.
+     */
+    int position;
+
+    /**
+     * If the label position has been updated, after instruction resizing.
+     */
+    boolean resized;
+
+    /**
+     * 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.
+     */
+    private int[] srcAndRefPositions;
+
+    /*
+     * 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 it successors in the graph, as a linked list of Edge
+     * objects.
+     */
+
+    /**
+     * The stack size at the beginning of this basic block. This size is
+     * initially unknown. It is computed by the control flow analysis algorithm
+     * (see {@link MethodWriter#visitMaxs visitMaxs}).
+     */
+    int beginStackSize;
+
+    /**
+     * The (relative) maximum stack size corresponding to this basic block. This
+     * size is relative to the stack size at the beginning of the basic block,
+     * i.e., the true maximum stack size is equal to {@link #beginStackSize
+     * beginStackSize} + {@link #maxStackSize maxStackSize}.
+     */
+    int maxStackSize;
+
+    /**
+     * 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. See
+     * {@link MethodWriter#visitMaxs visitMaxs}.
+     */
+    Label next;
+
+    /**
+     * <tt>true</tt> if this basic block has been pushed in the basic block
+     * stack. See {@link MethodWriter#visitMaxs visitMaxs}.
+     */
+    boolean pushed;
+
+    // ------------------------------------------------------------------------
+    // 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 (!resolved) {
+            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 (resolved) {
+            if (wideOffset) {
+                out.putInt(position - source);
+            } else {
+                out.putShort(position - source);
+            }
+        } else {
+            if (wideOffset) {
+                addReference(-1 - source, out.length);
+                out.putInt(-1);
+            } else {
+                addReference(source, out.length);
+                out.putShort(-1);
+            }
+        }
+    }
+
+    /**
+     * 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.resolved = true;
+        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;
+    }
+
+    // ------------------------------------------------------------------------
+    // Overriden Object methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns a string representation of this label.
+     * 
+     * @return a string representation of this label.
+     */
+    public String toString() {
+        return "L" + System.identityHashCode(this);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/MemberVisitor.java b/asmx/src/org/objectweb/asm/MemberVisitor.java
new file mode 100644
index 0000000..df184bd
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/MemberVisitor.java
@@ -0,0 +1,19 @@
+package org.objectweb.asm;
+
+/**
+ * Shared methods between {@link ClassVisitor}, {@link FieldVisitor}, and
+ * {@link MethodVisitor}.
+ */
+public interface MemberVisitor {
+
+    /**
+     * Visits a type annotation of this member.
+     *
+     * @param desc the class descriptor of the annotation class.
+     * @param visible <tt>true</tt> if the annotation is visible at runtime.
+     * @param inCode <tt>true</tt> if this annotation belongs in the <tt>Code</tt>
+     *               attribute, <tt>false</tt> otherwise. This is only used for methods.
+     * @return a non null visitor to visit the annotation values.
+     */
+    TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode);
+}
diff --git a/asmx/src/org/objectweb/asm/MethodAdapter.java b/asmx/src/org/objectweb/asm/MethodAdapter.java
new file mode 100644
index 0000000..20587df
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/MethodAdapter.java
@@ -0,0 +1,214 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * An empty {@link MethodVisitor} that delegates to another
+ * {@link MethodVisitor}. This class can be used as a super class to quickly
+ * implement usefull method adapter classes, just by overriding the necessary
+ * methods.
+ * 
+ * @author Eric Bruneton
+ */
+public class MethodAdapter implements MethodVisitor {
+
+    /**
+     * The {@link MethodVisitor} to which this adapter delegates calls.
+     */
+    protected MethodVisitor mv;
+
+    /**
+     * Constructs a new {@link MethodAdapter} object.
+     * 
+     * @param mv the code visitor to which this adapter must delegate calls.
+     */
+    public MethodAdapter(final MethodVisitor mv) {
+        this.mv = mv;
+    }
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        return mv.visitAnnotationDefault();
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        return mv.visitAnnotation(desc, visible);
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, 
+        boolean visible,
+        boolean inCode)
+    {
+        return mv.visitTypeAnnotation(desc, visible, inCode);
+    }
+    //end jaime
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        return mv.visitParameterAnnotation(parameter, desc, visible);
+    }
+
+    public void visitAttribute(final Attribute attr) {
+        mv.visitAttribute(attr);
+    }
+
+    public void visitCode() {
+        mv.visitCode();
+    }
+
+    public void visitInsn(final int opcode) {
+        mv.visitInsn(opcode);
+    }
+
+    public void visitIntInsn(final int opcode, final int operand) {
+        mv.visitIntInsn(opcode, operand);
+    }
+
+    public void visitVarInsn(final int opcode, final int var) {
+        mv.visitVarInsn(opcode, var);
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        mv.visitTypeInsn(opcode, desc);
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(
+        final String name,
+        final String desc,
+        final Handle bsm,
+        final Object... bsmArgs)
+    {
+        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+        mv.visitJumpInsn(opcode, label);
+    }
+
+    public void visitLabel(final Label label) {
+        mv.visitLabel(label);
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        mv.visitLdcInsn(cst);
+    }
+
+    public void visitIincInsn(final int var, final int increment) {
+        mv.visitIincInsn(var, increment);
+    }
+
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label labels[])
+    {
+        mv.visitTableSwitchInsn(min, max, dflt, labels);
+    }
+
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int keys[],
+        final Label labels[])
+    {
+        mv.visitLookupSwitchInsn(dflt, keys, labels);
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        mv.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(
+        int typeRef,
+        TypePath typePath,
+        String desc,
+        boolean visible)
+    {
+        return mv.visitInsnAnnotation(typeRef, typePath, desc, visible);
+    }
+
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        mv.visitTryCatchBlock(start, end, handler, type);
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        mv.visitLocalVariable(name, desc, signature, start, end, index);
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        mv.visitLineNumber(line, start);
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        mv.visitMaxs(maxStack, maxLocals);
+    }
+
+    public void visitEnd() {
+        mv.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/MethodVisitor.java b/asmx/src/org/objectweb/asm/MethodVisitor.java
new file mode 100644
index 0000000..5e7e049
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/MethodVisitor.java
@@ -0,0 +1,387 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+/**
+ * A visitor to visit a Java method. The methods of this interface must be
+ * called in the following order: [ <tt>visitAnnotationDefault</tt> ] (
+ * <tt>visitAnnotation</tt> | <tt>visitParameterAnnotation</tt> |
+ * <tt>visitAttribute</tt> )* [ <tt>visitCode</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, 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 interface MethodVisitor extends MemberVisitor {
+
+    // -------------------------------------------------------------------------
+    // Annotations and non standard attributes
+    // -------------------------------------------------------------------------
+
+    /**
+     * Visits the default value of this annotation interface method.
+     * 
+     * @return a non null visitor to the visit the actual default value of this
+     *         annotation interface method. 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.
+     */
+    AnnotationVisitor visitAnnotationDefault();
+
+    /**
+     * 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 non null visitor to visit the annotation values.
+     */
+    AnnotationVisitor visitAnnotation(String desc, boolean visible);
+
+    /**
+     * 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 non null visitor to visit the annotation values.
+     */
+    AnnotationVisitor visitParameterAnnotation(
+        int parameter,
+        String desc,
+        boolean visible);
+
+    /**
+     * Visits a non standard attribute of this method.
+     * 
+     * @param attr an attribute.
+     */
+    void visitAttribute(Attribute attr);
+
+    /**
+     * Starts the visit of the method's code, if any (i.e. non abstract method).
+     */
+    void visitCode();
+
+    // -------------------------------------------------------------------------
+    // 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.
+     */
+    void visitInsn(int 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}.
+     */
+    void visitIntInsn(int opcode, int 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.
+     */
+    void visitVarInsn(int opcode, int var);
+
+    /**
+     * Visits a type instruction. A type instruction is an instruction that
+     * takes a type descriptor as parameter.
+     * 
+     * @param opcode the opcode of the type instruction to be visited. This
+     *        opcode is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
+     * @param desc the operand of the instruction to be visited. This operand is
+     *        must be a fully qualified class name in internal form, or the type
+     *        descriptor of an array type (see {@link Type Type}).
+     */
+    void visitTypeInsn(int opcode, String desc);
+
+    /**
+     * 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}).
+     */
+    void visitFieldInsn(int opcode, String owner, String name, String 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}).
+     */
+    void visitMethodInsn(int opcode, String owner, String name, String 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.
+     */
+    void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... 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.
+     */
+    void visitJumpInsn(int opcode, Label label);
+
+    /**
+     * Visits a label. A label designates the instruction that will be visited
+     * just after it.
+     * 
+     * @param label a {@link Label Label} object.
+     */
+    void visitLabel(Label label);
+
+    // -------------------------------------------------------------------------
+    // Special instructions
+    // -------------------------------------------------------------------------
+
+    /**
+     * Visits a LDC instruction.
+     * 
+     * @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} (or a {@link Type} for
+     *        <tt>.class</tt> constants, for classes whose version is 49.0 or
+     *        more).
+     */
+    void visitLdcInsn(Object cst);
+
+    /**
+     * Visits an IINC instruction.
+     * 
+     * @param var index of the local variable to be incremented.
+     * @param increment amount to increment the local variable by.
+     */
+    void visitIincInsn(int var, int 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.
+     */
+    void visitTableSwitchInsn(int min, int max, Label dflt, Label 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.
+     */
+    void visitLookupSwitchInsn(Label dflt, int keys[], Label 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.
+     */
+    void visitMultiANewArrayInsn(String desc, int dims);
+
+    /**
+     * Visits an annotation on an instruction. This method must be called just
+     * <i>after</i> the annotated instruction. It can be called several times
+     * for the same instruction.
+     * 
+     * @param typeRef
+     *            a reference to the annotated type. The sort of this type
+     *            reference must be {@link TypeReference#INSTANCEOF INSTANCEOF},
+     *            {@link TypeReference#NEW NEW},
+     *            {@link TypeReference#CONSTRUCTOR_REFERENCE
+     *            CONSTRUCTOR_REFERENCE}, {@link TypeReference#METHOD_REFERENCE
+     *            METHOD_REFERENCE}, {@link TypeReference#CAST CAST},
+     *            {@link TypeReference#CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+     *            CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT},
+     *            {@link TypeReference#METHOD_INVOCATION_TYPE_ARGUMENT
+     *            METHOD_INVOCATION_TYPE_ARGUMENT},
+     *            {@link TypeReference#CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+     *            CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT}, or
+     *            {@link TypeReference#METHOD_REFERENCE_TYPE_ARGUMENT
+     *            METHOD_REFERENCE_TYPE_ARGUMENT}. See {@link TypeReference}.
+     * @param typePath
+     *            the path to the annotated type argument, wildcard bound, array
+     *            element type, or static inner type within 'typeRef'. May be
+     *            <tt>null</tt> if the annotation targets 'typeRef' as a whole.
+     * @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 visitInsnAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible);
+
+    // -------------------------------------------------------------------------
+    // Exceptions table entries, debug information,
+    // max stack size 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).
+     */
+    void visitTryCatchBlock(Label start, Label end, Label handler, String 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).
+     */
+    void visitLocalVariable(
+        String name,
+        String desc,
+        String signature,
+        Label start,
+        Label end,
+        int 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).
+     */
+    void visitLineNumber(int line, Label 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.
+     */
+    void visitMaxs(int maxStack, int 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.
+     */
+    void visitEnd();
+}
diff --git a/asmx/src/org/objectweb/asm/MethodWriter.java b/asmx/src/org/objectweb/asm/MethodWriter.java
new file mode 100644
index 0000000..84efabd
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/MethodWriter.java
@@ -0,0 +1,2201 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.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 implements MethodVisitor {
+
+    /**
+     * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
+     */
+    MethodWriter next;
+
+    /**
+     * The class writer to which this method must be added.
+     */
+    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 int name;
+
+    /**
+     * The index of the constant pool item that contains the descriptor of this
+     * method.
+     */
+    private int desc;
+
+    /**
+     * The descriptor of this method.
+     */
+    private String descriptor;
+
+    /**
+     * 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;
+
+    /**
+     * The signature of this method.
+     */
+    String signature;
+
+    /**
+     * 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;
+
+    //jaime
+    /**
+     * The runtime visible type annotations of this method. May be
+     * <tt>null</tt>.
+     */
+    private TypeAnnotationWriter xanns;
+
+    /**
+     * The runtime invisible type annotations of this method. May be
+     * <tt>null</tt>.
+     */
+    private TypeAnnotationWriter ixanns;
+    //end jaime
+
+    /**
+     * The runtime visible type annotations of this method that belong in the
+     * <tt>Code</tt> attribute. May be <tt>null</tt>.
+     */
+    private TypeAnnotationWriter cxanns;
+
+    /**
+     * The runtime invisible type annotations of this method that belong in the
+     * <tt>Code</tt> attribute. May be <tt>null</tt>.
+     */
+    private TypeAnnotationWriter cixanns;
+
+    /**
+     * 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 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 entries in the catch table of this method.
+     */
+    private int catchCount;
+
+    /**
+     * The catch table of this method.
+     */
+    private Handler catchTable;
+
+    /**
+     * The last element in the catchTable 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 start offset of the last visited instruction.
+     */
+    private int lastCodeOffset;
+
+    /**
+     * 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;
+
+    /*
+     * 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.
+     */
+
+    /**
+     * <tt>true</tt> if the maximum stack size and number of local variables
+     * must be automatically computed.
+     */
+    private final boolean computeMaxs;
+
+    /**
+     * 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#beginStackSize 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#beginStackSize beginStackSize} of the current basic
+     * block plus <tt>stackSize</tt>.
+     */
+    private int maxStackSize;
+
+    /**
+     * The current basic block. This block is the basic block to which the next
+     * instruction to be visited must be added.
+     */
+    private Label currentBlock;
+
+    /**
+     * The basic block stack used by the control flow analysis algorithm. This
+     * stack is represented by a linked list of {@link Label Label} objects,
+     * linked to each other by their {@link Label#next} field. This stack must
+     * not be confused with the JVM stack used to execute the JVM instructions!
+     */
+    private Label blockStack;
+
+    /**
+     * 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.
+     */
+    private final static int[] SIZE;
+
+    // ------------------------------------------------------------------------
+    // Static initializer
+    // ------------------------------------------------------------------------
+
+    /**
+     * 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, //UNUSED, // NOT VISITED
+        // 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();
+    }
+
+    // ------------------------------------------------------------------------
+    // 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.
+     */
+    MethodWriter(
+        final ClassWriter cw,
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions,
+        final boolean computeMaxs)
+    {
+        if (cw.firstMethod == null) {
+            cw.firstMethod = this;
+        } else {
+            cw.lastMethod.next = this;
+        }
+        cw.lastMethod = this;
+        this.cw = cw;
+        this.access = access;
+        this.name = cw.newUTF8(name);
+        this.desc = cw.newUTF8(desc);
+        this.descriptor = desc;
+        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.computeMaxs = computeMaxs;
+        if (computeMaxs) {
+            // updates maxLocals
+            int size = getArgumentsAndReturnSizes(desc) >> 2;
+            if ((access & Opcodes.ACC_STATIC) != 0) {
+                --size;
+            }
+            maxLocals = size;
+            // pushes the first block onto the stack of blocks to be visited
+            currentBlock = new Label();
+            currentBlock.pushed = true;
+            blockStack = currentBlock;
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the MethodVisitor interface
+    // ------------------------------------------------------------------------
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        annd = new ByteVector();
+        return new AnnotationWriter(cw, false, annd, null, 0);
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        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;
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible,
+        final boolean inCode)
+    {
+        ByteVector bv = new ByteVector();
+        TypeAnnotationWriter xaw = 
+          new TypeAnnotationWriter(cw, true, bv, bv, desc);
+        if (inCode) {
+            if (visible) {
+                xaw.next = cxanns;
+                cxanns = xaw;
+            } else {
+                xaw.next = cixanns;
+                cixanns = xaw;
+            }
+        } else {
+            if (visible) {
+                xaw.next = xanns;
+                xanns = xaw;
+            } else {
+                xaw.next = ixanns;
+                ixanns = xaw;
+            }
+        }
+        return xaw;
+    }
+    //end jaime
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        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) {
+            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;
+    }
+
+    public void visitAttribute(final Attribute attr) {
+        if (attr.isCodeAttribute()) {
+            attr.next = cattrs;
+            cattrs = attr;
+        } else {
+            attr.next = attrs;
+            attrs = attr;
+        }
+    }
+
+    public void visitCode() {
+    }
+
+    public void visitInsn(final int opcode) {
+        if (computeMaxs) {
+            // updates current and max stack sizes
+            int size = stackSize + 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)
+            {
+                if (currentBlock != null) {
+                    currentBlock.maxStackSize = maxStackSize;
+                    currentBlock = null;
+                }
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        code.putByte(opcode);
+    }
+
+    public void visitIntInsn(final int opcode, final int operand) {
+        if (computeMaxs && opcode != Opcodes.NEWARRAY) {
+            // updates current and max stack sizes only if opcode == 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);
+        }
+    }
+
+    public void visitVarInsn(final int opcode, final int var) {
+        if (computeMaxs) {
+            // updates current and max stack sizes
+            if (opcode == Opcodes.RET) {
+                // no stack change, but end of current block (no successor)
+                if (currentBlock != null) {
+                    currentBlock.maxStackSize = maxStackSize;
+                    currentBlock = null;
+                }
+            } else { // xLOAD or xSTORE
+                int size = stackSize + SIZE[opcode];
+                if (size > maxStackSize) {
+                    maxStackSize = size;
+                }
+                stackSize = size;
+            }
+            // 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);
+        }
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        if (computeMaxs && opcode == Opcodes.NEW) {
+            // updates current and max stack sizes only if opcode == NEW
+            // (stack size variation = 0 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, cw.newClass(desc));
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        if (computeMaxs) {
+            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, cw.newField(owner, name, desc));
+    }
+
+    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;
+        if (computeMaxs) {
+            /*
+             * 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 = 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 (!computeMaxs) {
+                if (argSize == 0) {
+                    argSize = getArgumentsAndReturnSizes(desc);
+                    i.intVal = argSize;
+                }
+            }
+            code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
+        } else {
+            code.put12(opcode, i.index);
+        }
+    }
+
+    public void visitInvokeDynamicInsn(int ix1, int ix2) {
+        code.putByte(ix1);
+        code.putByte(ix2);
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(final String name, final String desc,
+            final Handle bsm, final Object... bsmArgs) {
+        lastCodeOffset = code.length;
+        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);
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+        if (computeMaxs) {
+            if (opcode == Opcodes.GOTO) {
+                // no stack change, but end of current block (with one new
+                // successor)
+                if (currentBlock != null) {
+                    currentBlock.maxStackSize = maxStackSize;
+                    addSuccessor(stackSize, label);
+                    currentBlock = null;
+                }
+            } else if (opcode == Opcodes.JSR) {
+                if (currentBlock != null) {
+                    addSuccessor(stackSize + 1, label);
+                }
+            } else {
+                // updates current stack size (max stack size unchanged because
+                // stack size variation always negative in this case)
+                stackSize += SIZE[opcode];
+                if (currentBlock != null) {
+                    addSuccessor(stackSize, label);
+                }
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        if (label.resolved && 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 {
+                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);
+        }
+    }
+
+    public void visitLabel(final Label label) {
+        if (computeMaxs) {
+            if (currentBlock != null) {
+                // ends current block (with one new successor)
+                currentBlock.maxStackSize = maxStackSize;
+                addSuccessor(stackSize, label);
+            }
+            // begins a new current block,
+            // resets the relative current and max stack sizes
+            currentBlock = label;
+            stackSize = 0;
+            maxStackSize = 0;
+        }
+        // resolves previous forward references to label, if any
+        resize |= label.resolve(this, code.length, code.data);
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        Item i = cw.newConstItem(cst);
+        if (computeMaxs) {
+            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);
+        }
+    }
+
+    public void visitIincInsn(final int var, final int increment) {
+        if (computeMaxs) {
+            // updates max locals only (no stack change)
+            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);
+        }
+    }
+
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label labels[])
+    {
+        if (computeMaxs) {
+            // updates current stack size (max stack size unchanged)
+            --stackSize;
+            // ends current block (with many new successors)
+            if (currentBlock != null) {
+                currentBlock.maxStackSize = maxStackSize;
+                addSuccessor(stackSize, dflt);
+                for (int i = 0; i < labels.length; ++i) {
+                    addSuccessor(stackSize, labels[i]);
+                }
+                currentBlock = null;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        int source = code.length;
+        code.putByte(Opcodes.TABLESWITCH);
+        while (code.length % 4 != 0) {
+            code.putByte(0);
+        }
+        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);
+        }
+    }
+
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int keys[],
+        final Label labels[])
+    {
+        if (computeMaxs) {
+            // updates current stack size (max stack size unchanged)
+            --stackSize;
+            // ends current block (with many new successors)
+            if (currentBlock != null) {
+                currentBlock.maxStackSize = maxStackSize;
+                addSuccessor(stackSize, dflt);
+                for (int i = 0; i < labels.length; ++i) {
+                    addSuccessor(stackSize, labels[i]);
+                }
+                currentBlock = null;
+            }
+        }
+        // adds the instruction to the bytecode of the method
+        int source = code.length;
+        code.putByte(Opcodes.LOOKUPSWITCH);
+        while (code.length % 4 != 0) {
+            code.putByte(0);
+        }
+        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);
+        }
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        if (computeMaxs) {
+            // 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, cw.newClass(desc)).putByte(dims);
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(int typeRef,
+            TypePath typePath, String desc, boolean visible) {
+        ByteVector bv = new ByteVector();
+        // write target_type and target_info
+        typeRef = (typeRef & 0xFF0000FF) | (lastCodeOffset << 8);
+        // TODO: AnnotationWriter.putTarget(typeRef, typePath, bv);
+        // write type, and reserve space for values count
+        bv.putShort(cw.newUTF8(desc)).putShort(0);
+        AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv,
+                bv.length - 2);
+        // TODO: 
+        //if (visible) {
+        //    aw.next = ctanns;
+        //    ctanns = aw;
+        //} else {
+        //    aw.next = ictanns;
+        //    ictanns = aw;
+        //}
+        return aw;
+    }
+
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        if (computeMaxs) {
+            // pushes handler block onto the stack of blocks to be visited
+            if (!handler.pushed) {
+                handler.beginStackSize = 1;
+                handler.pushed = true;
+                handler.next = blockStack;
+                blockStack = handler;
+            }
+        }
+        ++catchCount;
+        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) {
+            catchTable = h;
+        } else {
+            lastHandler.next = h;
+        }
+        lastHandler = h;
+    }
+
+    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(computeMaxs) {
+            // updates max locals
+            char c = desc.charAt(0);
+            int n = index + ( c=='L' || c=='D' ? 2 : 1);
+            if (n > maxLocals) {
+                maxLocals = n;
+            }
+        }
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        if (lineNumber == null) {
+            lineNumber = new ByteVector();
+        }
+        ++lineNumberCount;
+        lineNumber.putShort(start.position);
+        lineNumber.putShort(line);
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        if (computeMaxs) {
+            // true (non relative) max stack size
+            int max = 0;
+            /*
+             * 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#beginStackSize} of the
+             * blocks in the block stack are the true (non relative) beginning
+             * stack sizes of these blocks.
+             */
+            Label stack = blockStack;
+            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.beginStackSize;
+                int blockMax = start + l.maxStackSize;
+                // updates the global max stack size
+                if (blockMax > max) {
+                    max = blockMax;
+                }
+                // analyses the successors of the block
+                Edge b = l.successors;
+                while (b != null) {
+                    l = b.successor;
+                    // if this successor has not already been pushed onto the
+                    // stack...
+                    if (!l.pushed) {
+                        // computes the true beginning stack size of this
+                        // successor block
+                        l.beginStackSize = start + b.stackSize;
+                        // pushes this successor onto the stack
+                        l.pushed = true;
+                        l.next = stack;
+                        stack = l;
+                    }
+                    b = b.next;
+                }
+            }
+            this.maxStack = max;
+        } else {
+            this.maxStack = maxStack;
+            this.maxLocals = maxLocals;
+        }
+    }
+
+    public void visitEnd() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods: control flow analysis algorithm
+    // ------------------------------------------------------------------------
+
+    /**
+     * 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>).
+     */
+    private 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;
+            }
+        }
+    }
+
+    /**
+     * Adds a successor to the {@link #currentBlock currentBlock} block.
+     * 
+     * @param stackSize the current (relative) stack size in the current block.
+     * @param successor the successor block to be added to the current block.
+     */
+    private void addSuccessor(final int stackSize, final Label successor) {
+        Edge b = new Edge();
+        // initializes the previous Edge object...
+        b.stackSize = stackSize;
+        b.successor = successor;
+        // ...and adds it to the successor list of the currentBlock block
+        b.next = currentBlock.successors;
+        currentBlock.successors = b;
+    }
+
+    // ------------------------------------------------------------------------
+    // 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.
+            resizeInstructions(new int[0], new int[0], 0);
+        }
+        int size = 8;
+        if (code.length > 0) {
+            cw.newUTF8("Code");
+            size += 18 + code.length + 8 * catchCount;
+            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 (cxanns != null) {
+                cw.newUTF8("RuntimeVisibleTypeAnnotations");
+                size += 8 + cxanns.getSize();
+            }
+            if (cixanns != null) {
+                cw.newUTF8("RuntimeInvisibleTypeAnnotations");
+                size += 8 + cixanns.getSize();
+            }
+            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
+                && (cw.version & 0xffff) < Opcodes.V1_5)
+        {
+            cw.newUTF8("Synthetic");
+            size += 6;
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            cw.newUTF8("Deprecated");
+            size += 6;
+        }
+        if (cw.version == Opcodes.V1_4) {
+            if ((access & Opcodes.ACC_VARARGS) != 0) {
+                cw.newUTF8("Varargs");
+                size += 6;
+            }
+            if ((access & Opcodes.ACC_BRIDGE) != 0) {
+                cw.newUTF8("Bridge");
+                size += 6;
+            }
+        }
+        if (signature != null) {
+            cw.newUTF8("Signature");
+            cw.newUTF8(signature);
+            size += 8;
+        }
+        if (annd != null) {
+            cw.newUTF8("AnnotationDefault");
+            size += 6 + annd.length;
+        }
+        if (anns != null) {
+            cw.newUTF8("RuntimeVisibleAnnotations");
+            size += 8 + anns.getSize();
+        }
+        if (ianns != null) {
+            cw.newUTF8("RuntimeInvisibleAnnotations");
+            size += 8 + ianns.getSize();
+        }
+        if (xanns != null) {
+            cw.newUTF8("RuntimeVisibleTypeAnnotations");
+            size += 8 + xanns.getSize();
+        }
+        if (ixanns != null) {
+            cw.newUTF8("RuntimeInvisibleTypeAnnotations");
+            size += 8 + ixanns.getSize();
+        }
+        if (panns != null) {
+            cw.newUTF8("RuntimeVisibleParameterAnnotations");
+            size += 7 + 2 * panns.length;
+            for (int i = panns.length - 1; i >= 0; --i) {
+                size += panns[i] == null ? 0 : panns[i].getSize();
+            }
+        }
+        if (ipanns != null) {
+            cw.newUTF8("RuntimeInvisibleParameterAnnotations");
+            size += 7 + 2 * ipanns.length;
+            for (int i = ipanns.length - 1; i >= 0; --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) {
+        out.putShort(access).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
+                && (cw.version & 0xffff) < Opcodes.V1_5)
+        {
+            ++attributeCount;
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            ++attributeCount;
+        }
+        if (cw.version == Opcodes.V1_4) {
+            if ((access & Opcodes.ACC_VARARGS) != 0) {
+                ++attributeCount;
+            }
+            if ((access & Opcodes.ACC_BRIDGE) != 0) {
+                ++attributeCount;
+            }
+        }
+        if (signature != null) {
+            ++attributeCount;
+        }
+        if (annd != null) {
+            ++attributeCount;
+        }
+        if (anns != null) {
+            ++attributeCount;
+        }
+        if (ianns != null) {
+            ++attributeCount;
+        }
+        if (xanns != null) {
+            ++attributeCount;
+        }
+        if (ixanns != null) {
+            ++attributeCount;
+        }
+        if (panns != null) {
+            ++attributeCount;
+        }
+        if (ipanns != null) {
+            ++attributeCount;
+        }
+        if (attrs != null) {
+            attributeCount += attrs.getCount();
+        }
+        out.putShort(attributeCount);
+        if (code.length > 0) {
+            int size = 12 + code.length + 8 * catchCount;
+            if (localVar != null) {
+                size += 8 + localVar.length;
+            }
+            if (localVarType != null) {
+                size += 8 + localVarType.length;
+            }
+            if (lineNumber != null) {
+                size += 8 + lineNumber.length;
+            }
+            if (cxanns != null) {
+                size += 8 + cxanns.getSize();
+            }
+            if (cixanns != null) {
+                size += 8 + cixanns.getSize();
+            }
+            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(catchCount);
+            if (catchCount > 0) {
+                Handler h = catchTable;
+                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 (cxanns != null) {
+                ++attributeCount;
+            }
+            if (cixanns != 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 (cxanns != null) {
+                out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
+                cxanns.put(out);
+            }
+            if (cixanns != null) {
+                out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
+                cixanns.put(out);
+            }
+            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
+                && (cw.version & 0xffff) < Opcodes.V1_5)
+        {
+            out.putShort(cw.newUTF8("Synthetic")).putInt(0);
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            out.putShort(cw.newUTF8("Deprecated")).putInt(0);
+        }
+        if (cw.version == Opcodes.V1_4) {
+            if ((access & Opcodes.ACC_VARARGS) != 0) {
+                out.putShort(cw.newUTF8("Varargs")).putInt(0);
+            }
+            if ((access & Opcodes.ACC_BRIDGE) != 0) {
+                out.putShort(cw.newUTF8("Bridge")).putInt(0);
+            }
+        }
+        if (signature != null) {
+            out.putShort(cw.newUTF8("Signature"))
+                    .putInt(2)
+                    .putShort(cw.newUTF8(signature));
+        }
+        if (annd != null) {
+            out.putShort(cw.newUTF8("AnnotationDefault"));
+            out.putInt(annd.length);
+            out.putByteArray(annd.data, 0, annd.length);
+        }
+        if (anns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
+            anns.put(out);
+        }
+        if (ianns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
+            ianns.put(out);
+        }
+        if (xanns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleTypeAnnotations"));
+            xanns.put(out);
+        }
+        if (ixanns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleTypeAnnotations"));
+            ixanns.put(out);
+        }
+        if (panns != null) {
+            out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
+            AnnotationWriter.put(panns, out);
+        }
+        if (ipanns != null) {
+            out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
+            AnnotationWriter.put(ipanns, 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 the designated instructions, 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.
+     * 
+     * @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 &lt; <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). The gaps introduced by
+     *        this method must be filled in "manually" in {@link #code code}
+     *        method.
+     * @param len the number of instruction to be resized. Must be smaller than
+     *        or equal to <tt>indexes</tt>.length and <tt>sizes</tt>.length.
+     * @return the <tt>indexes</tt> array, which now contains the new
+     *         positions of the resized instructions (designated as above).
+     */
+    private int[] resizeInstructions(
+        final int[] indexes,
+        final int[] sizes,
+        final int len)
+    {
+        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[len]; // copy of indexes
+        int[] allSizes = new int[len]; // copy of sizes
+        boolean[] resize; // instructions to be resized
+        int newOffset; // future offset of a jump instruction
+
+        System.arraycopy(indexes, 0, allIndexes, 0, len);
+        System.arraycopy(sizes, 0, allSizes, 0, len);
+        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:
+                        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) {
+            for (i = allIndexes.length - 1; i >= 0; --i) {
+                if (allIndexes[i] == u) {
+                    if (i < len) {
+                        if (sizes[i] > 0) {
+                            newCode.putByteArray(null, 0, sizes[i]);
+                        } else {
+                            newCode.length += sizes[i];
+                        }
+                        indexes[i] = newCode.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);
+                    while (newCode.length % 4 != 0) {
+                        newCode.putByte(0);
+                    }
+                    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);
+                    while (newCode.length % 4 != 0) {
+                        newCode.putByte(0);
+                    }
+                    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:
+                    newCode.putByteArray(b, u, 5);
+                    u += 5;
+                    break;
+                // case MANA_INSN:
+                default:
+                    newCode.putByteArray(b, u, 4);
+                    u += 4;
+                    break;
+            }
+        }
+
+        // updates the exception handler block labels
+        Handler h = catchTable;
+        while (h != null) {
+            getNewOffset(allIndexes, allSizes, h.start);
+            getNewOffset(allIndexes, allSizes, h.end);
+            getNewOffset(allIndexes, allSizes, h.handler);
+            h = h.next;
+        }
+        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
+        while (cattrs != null) {
+            Label[] labels = cattrs.getLabels();
+            if (labels != null) {
+                for (i = labels.length - 1; i >= 0; --i) {
+                    if (!labels[i].resized) {
+                        labels[i].position = getNewOffset(allIndexes,
+                                allSizes,
+                                0,
+                                labels[i].position);
+                        labels[i].resized = true;
+                    }
+                }
+            }
+        }
+
+        // replaces old bytecodes with new ones
+        code = newCode;
+
+        // returns the positions of the resized instructions
+        return indexes;
+    }
+
+    /**
+     * 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.resized) {
+            label.position = getNewOffset(indexes, sizes, 0, label.position);
+            label.resized = true;
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/Opcodes.java b/asmx/src/org/objectweb/asm/Opcodes.java
new file mode 100644
index 0000000..e5c2b33
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Opcodes.java
@@ -0,0 +1,361 @@
+/***
+ * 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 org.objectweb.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;
+    int ASM5 = 5 << 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;
+    int V1_8 = 0 << 16 | 52;
+
+    // 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, parameter
+    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, parameter
+    int ACC_ANNOTATION = 0x2000; // class
+    int ACC_ENUM = 0x4000; // class(?) field inner
+    int ACC_MANDATED = 0x8000; // parameter
+
+    // 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/asmx/src/org/objectweb/asm/PrecompiledMethodVisitor.java b/asmx/src/org/objectweb/asm/PrecompiledMethodVisitor.java
new file mode 100644
index 0000000..22407ca
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/PrecompiledMethodVisitor.java
@@ -0,0 +1,18 @@
+package org.objectweb.asm;
+
+/**
+ * A visitor to visit a Java method in which the bytecode positions of the
+ * instructions and labels are already known.  When an object that has these
+ * positions (for example, a {@link ClassReader}) accepts a
+ * {@link MethodVisitor}, it should test whether the visitor happens to be a
+ * {@link PrecompiledMethodVisitor}; if so, it should supply the positions
+ * using {@link #visitCurrentPosition}.
+ */
+public interface PrecompiledMethodVisitor extends MethodVisitor {
+    /**
+     * Informs the visitor of the current bytecode position in the method's
+     * code.  This position applies to all labels and instructions visited
+     * until {@link #visitCurrentPosition} is called again.
+     */
+    void visitCurrentPosition(int position);
+}
diff --git a/asmx/src/org/objectweb/asm/Type.java b/asmx/src/org/objectweb/asm/Type.java
new file mode 100644
index 0000000..33a8bf0
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/Type.java
@@ -0,0 +1,896 @@
+/***
+ * 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 org.objectweb.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 &lt;&lt; 2) | retSize</tt> (argSize is therefore equal to
+     *         <tt>i &gt;&gt; 2</tt>, and retSize to <tt>i &amp; 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:
+            StringBuilder sb = new StringBuilder(getElementType().getClassName());
+            for (int i = getDimensions(); i > 0; --i) {
+                sb.append("[]");
+            }
+            return sb.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 &lt;&lt; 2) | retSize</tt>
+     *         (argSize is therefore equal to <tt>i &gt;&gt; 2</tt>,
+     *         and retSize to <tt>i &amp; 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/asmx/src/org/objectweb/asm/TypeAnnotationVisitor.java b/asmx/src/org/objectweb/asm/TypeAnnotationVisitor.java
new file mode 100644
index 0000000..ec93575
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/TypeAnnotationVisitor.java
@@ -0,0 +1,119 @@
+package org.objectweb.asm;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/** 
+ * A visitor to visit a Java extended annotation.  The methods of this 
+ * interface must be called in the following order:
+ * (<tt>visit<tt> | 
+ *  <tt>visitEnum<tt> | 
+ *  <tt>visitAnnotation<tt> | 
+ *  <tt>visitArray<tt>)* 
+ * 
+ * <tt>visitXTargetType<tt>
+ * 
+ * (<i>visit the fields (if any) of the <tt>reference_info</tt> structure
+ *  for the given target type in the correct order using <tt>visitXOffset</tt>,
+ *  <tt>visitXStartPc</tt>, <tt>visitXLength</tt>, <tt>visitXIndex</tt>,
+ *  <tt>visitXParamIndex</tt>, <tt>visitXBoundIndex</tt>,
+ *  <tt>visitXLocationLength</tt>, and/or <tt>visitXLocation</tt></i>)
+ *  
+ * <tt>visitEnd<tt>
+ * 
+ * @author jaimeq
+ */
+
+public interface TypeAnnotationVisitor extends AnnotationVisitor {
+  /**
+   * Visits the target type of the extended annotation, which defines the
+   * type and structure of the reference info of the extended annotation.
+   * 
+   * @param target_type the target type of the extended annotation
+   */
+  void visitXTargetType(int target_type);
+
+  /**
+   * Visits the offset specified by the extended annotation, whose meaning
+   * depends on the extended annotation's target type.
+   * 
+   * @param offset the offset specified by the extended annotation
+   */
+  void visitXOffset(int offset);
+
+  /**
+   * Visits the location_length specified by the extended annotation, whose 
+   * meaning depends on the extended annotation's target type.
+   * 
+   * @param location_length the location_length specified by the extended 
+   *  annotation
+   */
+  void visitXLocationLength(int location_length);
+
+  /**
+   * Visits the location specified by the extended annotation, whose meaning 
+   * depends on the extended annotation's target type.
+   * 
+   * @param location the location specified by the extended annotation
+   */
+  void visitXLocation(TypePathEntry location);
+
+  void visitXNumEntries(int num_entries);
+
+  /**
+   * Visits the start_pc specified by the extended annotation, whose meaning
+   * depends on the extended annotation's target type.
+   * 
+   * @param start_pc the start_pc specified by the extended annotation
+   */
+  void visitXStartPc(int start_pc);
+
+  /**
+   * Visits the length specified by the extended annotation, whose meaning
+   * depends on the extended annotation's target type.
+   * 
+   * @param length the length specified by the extended annotation
+   */
+  void visitXLength(int length);
+
+  /**
+   * Visits the index specified by the extended annotation, whose meaning 
+   * depends on the extended annotation's target type.
+   * 
+   * @param index the index specified by the extended annotation
+   */
+  void visitXIndex(int index);
+
+  /**
+   * Visits the param_index specified by the extended annotation, whose meaning
+   * depends on the extended annotation's target type.
+   * 
+   * @param param_index the param_index specified by the extended annotation
+   */
+  public void visitXParamIndex(int param_index);
+
+  /**
+   * Visits the bound_index specified by the extended annotation, whose meaning
+   * depends on the extended annotation's target type.
+   * 
+   * @param bound_index the bound_index specified by the extended annotation
+   */
+  public void visitXBoundIndex(int bound_index);
+
+  /**
+   * Visits the type_index specified by the extended annotation, whose meaning
+   * depends on the extended annotation's target type.
+   * @param type_index
+   */
+  public void visitXTypeIndex(int type_index);
+
+  /**
+   * Visits the exception_index specified by the extended annotation.
+   * @param exception_index
+   */
+  public void visitXExceptionIndex(int exception_index);
+
+  /**
+   * Visits the annotation name and arguments size.
+   */
+  public void visitXNameAndArgsSize();
+}
diff --git a/asmx/src/org/objectweb/asm/TypeAnnotationWriter.java b/asmx/src/org/objectweb/asm/TypeAnnotationWriter.java
new file mode 100644
index 0000000..fdff53f
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/TypeAnnotationWriter.java
@@ -0,0 +1,405 @@
+// Source code copied from AnnotationWriter.java, and modified to 
+//  accommodate extended annotations.  
+// Specifically, the int x* fields and visitX* methods were added.
+
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * An {@link TypeAnnotationVisitor} that generates 
+ * extended annotations in bytecode form.
+ * 
+ * @author jaimeq
+ */
+final class TypeAnnotationWriter implements TypeAnnotationVisitor {
+
+    /**
+     * 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 int offset;
+
+    /**
+     * The name of this annotation.
+     */
+    private final String desc;
+
+    /**
+     * Next annotation writer. This field is used to store annotation lists.
+     */
+    TypeAnnotationWriter next;
+
+    /**
+     * Previous annotation writer. This field is used to store annotation lists.
+     */
+    TypeAnnotationWriter prev;
+
+    private TypePathEntry xlocations[];
+    private int xlocations_index;
+
+    // ------------------------------------------------------------------------
+    // Constructor
+    // ------------------------------------------------------------------------
+
+    /**
+     * Constructs a new {@link TypeAnnotationWriter}.
+     * 
+     * @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 desc the name of this annotation.
+     */
+    TypeAnnotationWriter(
+        final ClassWriter cw,
+        final boolean named,
+        final ByteVector bv,
+        final ByteVector parent,
+        final String desc)
+    {
+        this.cw = cw;
+        this.named = named;
+        this.bv = bv;
+        this.parent = parent;
+        this.desc = desc;
+
+        // extended information
+        this.xlocations = null;
+        this.xlocations_index = 0;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the AnnotationVisitor interface
+    // ------------------------------------------------------------------------
+
+    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);
+        }
+    }
+
+    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));
+    }
+
+    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);
+    }
+
+    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);
+    }
+
+    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;
+        TypeAnnotationWriter 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;
+        TypeAnnotationWriter aw = this;
+        TypeAnnotationWriter 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 out where the annotations must be put.
+     */
+    static void put(final TypeAnnotationWriter[] panns, final ByteVector out) {
+        int size = 1 + 2 * panns.length;
+        for (int i = 0; i < panns.length; ++i) {
+            size += panns[i] == null ? 0 : panns[i].getSize();
+        }
+        out.putInt(size).putByte(panns.length);
+        for (int i = 0; i < panns.length; ++i) {
+            TypeAnnotationWriter aw = panns[i];
+            TypeAnnotationWriter 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;
+            }
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the TypeAnnotationVisitor interface
+    // ------------------------------------------------------------------------
+
+    // below are all the methods for implementing extended annotations
+    public void visitXTargetType(int target_type) {
+        bv.putByte(target_type);
+    }
+
+    // used for typecasts, object creation, field generic/array
+    public void visitXOffset(int offset) {
+        bv.putShort(offset);
+    }
+
+    // used for generic type arguments or arrays
+    public void visitXLocationLength(int location_length) {
+        this.xlocations = new TypePathEntry[location_length];
+        this.xlocations_index = 0;
+        bv.putByte(location_length);
+    }
+
+    // used for generic type arguments or arrays
+    public void visitXLocation(TypePathEntry location) {
+        this.xlocations[this.xlocations_index] = location;
+        this.xlocations_index++;
+        bv.putByte(location.tag.tag);
+        bv.putByte(location.arg);
+    }
+
+    // used for local variables
+    public void visitXNumEntries(int num_entries) {
+        bv.putShort(num_entries);
+    }
+
+    // used for local variables
+    public void visitXStartPc(int start_pc) {
+        bv.putShort(start_pc);
+    }
+
+    // used for local variables
+    public void visitXLength(int length) {
+        bv.putShort(length);
+    }
+
+    // used for local variables
+    public void visitXIndex(int index) {
+        bv.putShort(index);
+    }
+
+    // used for type parameter bounds
+    public void visitXParamIndex(int param_index) {
+        bv.putByte(param_index);
+    }
+
+    // used for type parameter bounds
+    public void visitXBoundIndex(int bound_index) {
+        bv.putByte(bound_index);
+    }
+
+    // used for type index for class extends/implements and 
+    // throws exception types
+    public void visitXTypeIndex(int type_index) {
+        bv.putByte(type_index);
+    }
+
+    public void visitXExceptionIndex(int exception_index) {
+        bv.putByte(exception_index);
+    }
+
+    public void visitXNameAndArgsSize() {
+        bv.putShort(cw.newUTF8(desc));
+        // Placeholder for size
+        bv.putShort(0);
+        // Set offset so we know where to put the size on a call to visitEnd
+        offset = bv.length - 2;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/TypePath.java b/asmx/src/org/objectweb/asm/TypePath.java
new file mode 100644
index 0000000..d9c99b1
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/TypePath.java
@@ -0,0 +1,196 @@
+/***

+ * ASM: a very small and fast Java bytecode manipulation framework

+ * Copyright (c) 2000-2013 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 org.objectweb.asm;

+

+/**

+ * The path to a type argument, wildcard bound, array element type, or static

+ * inner type within an enclosing type.

+ * 

+ * @author Eric Bruneton

+ */

+public class TypePath {

+

+    /**

+     * A type path step that steps into the element type of an array type. See

+     * {@link #getStep getStep}.

+     */

+    public final static int ARRAY_ELEMENT = 0;

+

+    /**

+     * A type path step that steps into the nested type of a class type. See

+     * {@link #getStep getStep}.

+     */

+    public final static int INNER_TYPE = 1;

+

+    /**

+     * A type path step that steps into the bound of a wildcard type. See

+     * {@link #getStep getStep}.

+     */

+    public final static int WILDCARD_BOUND = 2;

+

+    /**

+     * A type path step that steps into a type argument of a generic type. See

+     * {@link #getStep getStep}.

+     */

+    public final static int TYPE_ARGUMENT = 3;

+

+    /**

+     * The byte array where the path is stored, in Java class file format.

+     */

+    byte[] b;

+

+    /**

+     * The offset of the first byte of the type path in 'b'.

+     */

+    int offset;

+

+    /**

+     * Creates a new type path.

+     * 

+     * @param b

+     *            the byte array containing the type path in Java class file

+     *            format.

+     * @param offset

+     *            the offset of the first byte of the type path in 'b'.

+     */

+    TypePath(byte[] b, int offset) {

+        this.b = b;

+        this.offset = offset;

+    }

+

+    /**

+     * Returns the length of this path.

+     * 

+     * @return the length of this path.

+     */

+    public int getLength() {

+        return b[offset];

+    }

+

+    /**

+     * Returns the value of the given step of this path.

+     * 

+     * @param index

+     *            an index between 0 and {@link #getLength()}, exclusive.

+     * @return {@link #ARRAY_ELEMENT ARRAY_ELEMENT}, {@link #INNER_TYPE

+     *         INNER_TYPE}, {@link #WILDCARD_BOUND WILDCARD_BOUND}, or

+     *         {@link #TYPE_ARGUMENT TYPE_ARGUMENT}.

+     */

+    public int getStep(int index) {

+        return b[offset + 2 * index + 1];

+    }

+

+    /**

+     * Returns the index of the type argument that the given step is stepping

+     * into. This method should only be used for steps whose value is

+     * {@link #TYPE_ARGUMENT TYPE_ARGUMENT}.

+     * 

+     * @param index

+     *            an index between 0 and {@link #getLength()}, exclusive.

+     * @return the index of the type argument that the given step is stepping

+     *         into.

+     */

+    public int getStepArgument(int index) {

+        return b[offset + 2 * index + 2];

+    }

+

+    /**

+     * Converts a type path in string form, in the format used by

+     * {@link #toString()}, into a TypePath object.

+     * 

+     * @param typePath

+     *            a type path in string form, in the format used by

+     *            {@link #toString()}. May be null or empty.

+     * @return the corresponding TypePath object, or null if the path is empty.

+     */

+    public static TypePath fromString(final String typePath) {

+        if (typePath == null || typePath.length() == 0) {

+            return null;

+        }

+        int n = typePath.length();

+        ByteVector out = new ByteVector(n);

+        out.putByte(0);

+        for (int i = 0; i < n;) {

+            char c = typePath.charAt(i++);

+            if (c == '[') {

+                out.put11(ARRAY_ELEMENT, 0);

+            } else if (c == '.') {

+                out.put11(INNER_TYPE, 0);

+            } else if (c == '*') {

+                out.put11(WILDCARD_BOUND, 0);

+            } else if (c >= '0' && c <= '9') {

+                int typeArg = c - '0';

+                while (i < n && (c = typePath.charAt(i)) >= '0' && c <= '9') {

+                    typeArg = typeArg * 10 + c - '0';

+                    i += 1;

+                }

+                if (i < n && typePath.charAt(i) == ';') {

+                    i += 1;

+                }

+                out.put11(TYPE_ARGUMENT, typeArg);

+            }

+        }

+        out.data[0] = (byte) (out.length / 2);

+        return new TypePath(out.data, 0);

+    }

+

+    /**

+     * Returns a string representation of this type path. {@link #ARRAY_ELEMENT

+     * ARRAY_ELEMENT} steps are represented with '[', {@link #INNER_TYPE

+     * INNER_TYPE} steps with '.', {@link #WILDCARD_BOUND WILDCARD_BOUND} steps

+     * with '*' and {@link #TYPE_ARGUMENT TYPE_ARGUMENT} steps with their type

+     * argument index in decimal form followed by ';'.

+     */

+    @Override

+    public String toString() {

+        int length = getLength();

+        StringBuilder result = new StringBuilder(length * 2);

+        for (int i = 0; i < length; ++i) {

+            switch (getStep(i)) {

+            case ARRAY_ELEMENT:

+                result.append('[');

+                break;

+            case INNER_TYPE:

+                result.append('.');

+                break;

+            case WILDCARD_BOUND:

+                result.append('*');

+                break;

+            case TYPE_ARGUMENT:

+                result.append(getStepArgument(i)).append(';');

+                break;

+            default:

+                result.append('_');

+            }

+        }

+        return result.toString();

+    }

+}

diff --git a/asmx/src/org/objectweb/asm/attrs/StackMapAttribute.java b/asmx/src/org/objectweb/asm/attrs/StackMapAttribute.java
new file mode 100644
index 0000000..4df2f7d
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/attrs/StackMapAttribute.java
@@ -0,0 +1,378 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.attrs;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ByteVector;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+
+/**
+ * StackMapAttribute is used by CDLC preverifier. Definition is given in
+ * appendix "CLDC Byte Code Typechecker Specification" from CDLC 1.1
+ * specification. <p> <i>Note that this implementation does not calculate
+ * StackMapFrame structures from the method bytecode. If method code is changed
+ * or generated from scratch, then developer is responsible to prepare a correct
+ * StackMapFrame structures.</i> <p> The format of the stack map in the class
+ * file is given below. In the following, <ul> <li>if the length of the
+ * method's byte code1 is 65535 or less, then <tt>uoffset</tt> represents the
+ * type u2; otherwise <tt>uoffset</tt> represents the type u4.</li> <li>If
+ * the maximum number of local variables for the method is 65535 or less, then
+ * <tt>ulocalvar</tt> represents the type u2; otherwise <tt>ulocalvar</tt>
+ * represents the type u4.</li> <li>If the maximum size of the operand stack
+ * is 65535 or less, then <tt>ustack</tt> represents the type u2; otherwise
+ * ustack represents the type u4.</li> </ul>
+ * 
+ * <pre>
+ * stack_map { // attribute StackMap
+ *   u2 attribute_name_index;
+ *   u4 attribute_length
+ *   uoffset number_of_entries;
+ *   stack_map_frame entries[number_of_entries];
+ * }
+ * </pre>
+ * 
+ * Each stack map frame has the following format:
+ * 
+ * <pre>
+ * stack_map_frame {
+ *   uoffset offset;
+ *   ulocalvar number_of_locals;
+ *   verification_type_info locals[number_of_locals];
+ *   ustack number_of_stack_items;
+ *   verification_type_info stack[number_of_stack_items];
+ * }
+ * </pre>
+ * 
+ * The <tt>verification_type_info</tt> structure consists of a one-byte tag
+ * followed by zero or more bytes, giving more information about the tag. Each
+ * <tt>verification_type_info</tt> structure specifies the verification type
+ * of one or two locations.
+ * 
+ * <pre>
+ * union verification_type_info {
+ *   Top_variable_info;
+ *   Integer_variable_info;
+ *   Float_variable_info;
+ *   Long_variable_info;
+ *   Double_variable_info;
+ *   Null_variable_info;
+ *   UninitializedThis_variable_info;
+ *   Object_variable_info;
+ *   Uninitialized_variable_info;
+ * }
+ *      
+ * Top_variable_info {
+ *   u1 tag = ITEM_Top; // 0
+ * }
+ *      
+ * Integer_variable_info {
+ *   u1 tag = ITEM_Integer; // 1
+ * }
+ *      
+ * Float_variable_info {
+ *   u1 tag = ITEM_Float; // 2
+ * }
+ *      
+ * Long_variable_info {
+ *   u1 tag = ITEM_Long; // 4
+ * }
+ *      
+ * Double_variable_info {
+ *   u1 tag = ITEM_Double; // 3
+ * }
+ *      
+ * Null_variable_info {
+ *  u1 tag = ITEM_Null; // 5
+ * }
+ *      
+ * UninitializedThis_variable_info {
+ *   u1 tag = ITEM_UninitializedThis; // 6
+ * }
+ *      
+ * Object_variable_info {
+ *   u1 tag = ITEM_Object; // 7
+ *   u2 cpool_index;
+ * }
+ *      
+ * Uninitialized_variable_info {
+ *   u1 tag = ITEM_Uninitialized // 8
+ *   uoffset offset;
+ * }
+ * </pre>
+ * 
+ * @see <a href="http://www.jcp.org/en/jsr/detail?id=139">JSR 139 : Connected
+ *      Limited Device Configuration 1.1</a>
+ * 
+ * @author Eugene Kuleshov
+ */
+public class StackMapAttribute extends Attribute {
+
+    static final int MAX_SIZE = 65535;
+
+    /**
+     * A List of <code>StackMapFrame</code> instances.
+     */
+    public List frames = new ArrayList();
+
+    public StackMapAttribute() {
+        super("StackMap");
+    }
+
+    public StackMapAttribute(List frames) {
+        this();
+        this.frames = frames;
+    }
+
+    public List getFrames() {
+        return frames;
+    }
+
+    public StackMapFrame getFrame(Label label) {
+        for (int i = 0; i < frames.size(); i++) {
+            StackMapFrame frame = (StackMapFrame) frames.get(i);
+            if (frame.label == label) {
+                return frame;
+            }
+        }
+        return null;
+    }
+
+    public boolean isUnknown() {
+        return false;
+    }
+
+    public boolean isCodeAttribute() {
+        return true;
+    }
+
+    protected Attribute read(
+        ClassReader cr,
+        int off,
+        int len,
+        char[] buf,
+        int codeOff,
+        Label[] labels)
+    {
+        StackMapAttribute attr = new StackMapAttribute();
+        // note that this is not the size of Code attribute
+        boolean isExtCodeSize = cr.readInt(codeOff + 4) > MAX_SIZE;
+        boolean isExtLocals = cr.readUnsignedShort(codeOff + 2) > MAX_SIZE;
+        boolean isExtStack = cr.readUnsignedShort(codeOff) > MAX_SIZE;
+
+        int size = 0;
+        if (isExtCodeSize) {
+            size = cr.readInt(off);
+            off += 4;
+        } else {
+            size = cr.readUnsignedShort(off);
+            off += 2;
+        }
+        for (int i = 0; i < size; i++) {
+            int offset;
+            if (isExtCodeSize) {
+                offset = cr.readInt(off);
+                off += 4;
+            } else {
+                offset = cr.readUnsignedShort(off);
+                off += 2;
+            }
+
+            Label label = getLabel(offset, labels);
+            List locals = new ArrayList();
+            List stack = new ArrayList();
+
+            off = readTypeInfo(cr,
+                    off,
+                    locals,
+                    labels,
+                    buf,
+                    isExtLocals,
+                    isExtCodeSize);
+            off = readTypeInfo(cr,
+                    off,
+                    stack,
+                    labels,
+                    buf,
+                    isExtStack,
+                    isExtCodeSize);
+
+            attr.frames.add(new StackMapFrame(label, locals, stack));
+        }
+        return attr;
+    }
+
+    private int readTypeInfo(
+        ClassReader cr,
+        int off,
+        List info,
+        Label[] labels,
+        char[] buf,
+        boolean isExt,
+        boolean isExtCode)
+    {
+        int n = 0;
+        if (isExt) {
+            n = cr.readInt(off);
+            off += 4;
+        } else {
+            n = cr.readUnsignedShort(off);
+            off += 2;
+        }
+        for (int j = 0; j < n; j++) {
+            int itemType = cr.readByte(off++);
+            StackMapType typeInfo = StackMapType.getTypeInfo(itemType);
+            info.add(typeInfo);
+            switch (itemType) {
+                case StackMapType.ITEM_Object: //
+                    typeInfo.setObject(cr.readClass(off, buf));
+                    off += 2;
+                    break;
+                case StackMapType.ITEM_Uninitialized: //
+                    int offset;
+                    if (isExtCode) {
+                        offset = cr.readInt(off);
+                        off += 4;
+                    } else {
+                        offset = cr.readUnsignedShort(off);
+                        off += 2;
+                    }
+                    typeInfo.setLabel(getLabel(offset, labels));
+                    break;
+            }
+        }
+        return off;
+    }
+
+    private void writeTypeInfo(ByteVector bv, ClassWriter cw, List info, int max)
+    {
+        if (max > StackMapAttribute.MAX_SIZE) {
+            bv.putInt(info.size());
+        } else {
+            bv.putShort(info.size());
+        }
+        for (int j = 0; j < info.size(); j++) {
+            StackMapType typeInfo = (StackMapType) info.get(j);
+            bv.putByte(typeInfo.getType());
+            switch (typeInfo.getType()) {
+                case StackMapType.ITEM_Object: //
+                    bv.putShort(cw.newClass(typeInfo.getObject()));
+                    break;
+
+                case StackMapType.ITEM_Uninitialized: //
+                    bv.putShort(typeInfo.getLabel().getOffset());
+                    break;
+
+            }
+        }
+    }
+
+    private Label getLabel(int offset, Label[] labels) {
+        Label l = labels[offset];
+        if (l != null) {
+            return l;
+        }
+        return labels[offset] = new Label();
+    }
+
+    protected ByteVector write(
+        ClassWriter cw,
+        byte[] code,
+        int len,
+        int maxStack,
+        int maxLocals)
+    {
+        ByteVector bv = new ByteVector();
+        if (code != null && code.length > MAX_SIZE) { // TODO verify value
+            bv.putInt(frames.size());
+        } else {
+            bv.putShort(frames.size());
+        }
+        for (int i = 0; i < frames.size(); i++) {
+            writeFrame((StackMapFrame) frames.get(i),
+                    cw,
+                    maxStack,
+                    maxLocals,
+                    bv);
+        }
+        return bv;
+    }
+
+    protected Label[] getLabels() {
+        HashSet labels = new HashSet();
+        for (int i = 0; i < frames.size(); i++) {
+            getFrameLabels((StackMapFrame) frames.get(i), labels);
+        }
+        return (Label[]) labels.toArray(new Label[labels.size()]);
+    }
+
+    private void writeFrame(
+        StackMapFrame frame,
+        ClassWriter cw,
+        int maxStack,
+        int maxLocals,
+        ByteVector bv)
+    {
+        bv.putShort(frame.label.getOffset());
+        writeTypeInfo(bv, cw, frame.locals, maxLocals);
+        writeTypeInfo(bv, cw, frame.stack, maxStack);
+    }
+
+    private void getFrameLabels(StackMapFrame frame, Set labels) {
+        labels.add(frame.label);
+        getTypeInfoLabels(labels, frame.locals);
+        getTypeInfoLabels(labels, frame.stack);
+    }
+
+    private void getTypeInfoLabels(Set labels, List info) {
+        for (Iterator it = info.iterator(); it.hasNext();) {
+            StackMapType typeInfo = (StackMapType) it.next();
+            if (typeInfo.getType() == StackMapType.ITEM_Uninitialized) {
+                labels.add(typeInfo.getLabel());
+            }
+        }
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer("StackMap[");
+        for (int i = 0; i < frames.size(); i++) {
+            sb.append('\n').append('[').append(frames.get(i)).append(']');
+        }
+        sb.append("\n]");
+        return sb.toString();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/attrs/StackMapFrame.java b/asmx/src/org/objectweb/asm/attrs/StackMapFrame.java
new file mode 100644
index 0000000..bdd4641
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/attrs/StackMapFrame.java
@@ -0,0 +1,82 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.attrs;
+
+import java.util.List;
+
+import org.objectweb.asm.Label;
+
+/**
+ * Holds the state of the stack and local variables for a single execution
+ * branch.
+ * 
+ * <i>Note that Long and Double types are represented by two entries in locals
+ * and stack. Second entry should be always of type Top.</i>
+ * 
+ * @see <a href="http://www.jcp.org/en/jsr/detail?id=139">JSR 139 : Connected
+ *      Limited Device Configuration 1.1</a>
+ * 
+ * @see "ClassFileFormat-Java6.fm Page 138 Friday, April 15, 2005 3:22 PM"
+ * 
+ * @author Eugene Kuleshov
+ */
+public class StackMapFrame {
+
+    /**
+     * A <code>Label</code> for frame offset within method bytecode.
+     */
+    public Label label;
+
+    /**
+     * A List of <code>StackMapType</code> instances that represent locals for
+     * this frame.
+     */
+    public List locals;
+
+    /**
+     * A List of <code>StackMapType</code> instances that represent stack for
+     * this frame.
+     */
+    public List stack;
+
+    public StackMapFrame(Label label, List locals, List stack) {
+        this.label = label;
+        this.locals = locals;
+        this.stack = stack;
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer("Frame:L");
+        sb.append(System.identityHashCode(label));
+        sb.append(" locals").append(locals);
+        sb.append(" stack").append(stack);
+        return sb.toString();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/attrs/StackMapTableAttribute.java b/asmx/src/org/objectweb/asm/attrs/StackMapTableAttribute.java
new file mode 100644
index 0000000..d388104
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/attrs/StackMapTableAttribute.java
@@ -0,0 +1,927 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.attrs;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ByteVector;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * The stack map attribute is used during the process of verification by
+ * typechecking (&sect;4.11.1). <br> <br> A stack map attribute consists of zero or
+ * more stack map frames. Each stack map frame specifies (either explicitly or
+ * implicitly) a bytecode offset, the verification types (&sect;4.11.1) for the local
+ * variables, and the verification types for the operand stack. <br> <br> The
+ * type checker deals with and manipulates the expected types of a method's
+ * local variables and operand stack. Throughout this section, a location refers
+ * to either a single local variable or to a single operand stack entry. <br>
+ * <br> We will use the terms stack frame map and type state interchangeably to
+ * describe a mapping from locations in the operand stack and local variables of
+ * a method to verification types. We will usually use the term stack frame map
+ * when such a mapping is provided in the class file, and the term type state
+ * when the mapping is inferred by the type checker. <br> <br> If a method's
+ * Code attribute does not have a StackMapTable attribute, it has an implicit
+ * stack map attribute. This implicit stack map attribute is equivalent to a
+ * StackMapTable attribute with number_of_entries equal to zero. A method's Code
+ * attribute may have at most one StackMapTable attribute, otherwise a
+ * java.lang.ClassFormatError is thrown. <br> <br> The format of the stack map
+ * in the class file is given below. In the following, if the length of the
+ * method's byte code is 65535 or less, then uoffset represents the type u2;
+ * otherwise uoffset represents the type u4. If the maximum number of local
+ * variables for the method is 65535 or less, then <code>ulocalvar</code>
+ * represents the type u2; otherwise ulocalvar represents the type u4. If the
+ * maximum size of the operand stack is 65535 or less, then <code>ustack</code>
+ * represents the type u2; otherwise ustack represents the type u4.
+ * 
+ * <pre>
+ * stack_map { // attribute StackMapTable
+ *   u2 attribute_name_index;
+ *   u4 attribute_length
+ *   uoffset number_of_entries;
+ *   stack_map_frame entries[number_of_entries];
+ * }
+ * </pre>
+ * 
+ * Each stack_map_frame structure specifies the type state at a particular byte
+ * code offset. Each frame type specifies (explicitly or implicitly) a value,
+ * offset_delta, that is used to calulate the actual byte code offset at which
+ * it applies. The byte code offset at which the frame applies is given by
+ * adding <code>1 + offset_delta</code> to the <code>offset</code> of the
+ * previous frame, unless the previous frame is the initial frame of the method,
+ * in which case the byte code offset is <code>offset_delta</code>. <br> <br>
+ * <i>Note that the length of the byte codes is not the same as the length of
+ * the Code attribute. The byte codes are embedded in the Code attribute, along
+ * with other information.</i> <br> <br> By using an offset delta rather than
+ * the actual byte code offset we ensure, by definition, that stack map frames
+ * are in the correctly sorted order. Furthermore, by consistently using the
+ * formula <code>offset_delta + 1</code> for all explicit frames, we guarantee
+ * the absence of duplicates. <br> <br> All frame types, even full_frame, rely
+ * on the previous frame for some of their semantics. This raises the question
+ * of what is the very first frame? The initial frame is implicit, and computed
+ * from the method descriptor. See the Prolog code for methodInitialStacFrame.
+ * <br> <br> The stack_map_frame structure consists of a one-byte tag followed
+ * by zero or more bytes, giving more information, depending upon the tag. <br>
+ * <br> A stack map frame may belong to one of several frame types
+ * 
+ * <pre>
+ * union stack_map_frame {
+ *   same_frame;
+ *   same_locals_1_stack_item_frame;
+ *   chop_frame;
+ *   same_frame_extended;
+ *   append_frame;
+ *   full_frame;
+ * }
+ * </pre>
+ * 
+ * The frame type same_frame is represented by tags in the range [0-63]. If the
+ * frame type is same_frame, it means the frame has exactly the same locals as
+ * the previous stack map frame and that the number of stack items is zero. The
+ * offset_delta value for the frame is the value of the tag field, frame_type.
+ * The form of such a frame is then:
+ * 
+ * <pre>
+ * same_frame {
+ *   u1 frame_type = SAME;  // 0-63
+ * }
+ * </pre>
+ * 
+ * The frame type same_locals_1_stack_item_frame is represented by tags in the
+ * range [64, 127]. If the frame_type is same_locals_1_stack_item_frame, it
+ * means the frame has exactly the same locals as the previous stack map frame
+ * and that the number of stack items is 1. The offset_delta value for the frame
+ * is the value (frame_type - 64). There is a verification_type_info following
+ * the frame_type for the one stack item. The form of such a frame is then:
+ * 
+ * <pre>
+ * same_locals_1_stack_item_frame {
+ *   u1 frame_type = SAME_LOCALS_1_STACK_ITEM;  // 64-127
+ *    verification_type_info stack[1];
+ * }
+ * </pre>
+ * 
+ * Tags in the range [128-247] are reserved for future use. <br> <br> The frame
+ * type chop_frame is represented by tags in the range [248-250]. If the
+ * frame_type is chop_frame, it means that the 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. <br> <br> The form of
+ * such a frame is then:
+ * 
+ * <pre>
+ * chop_frame {
+ *   u1 frame_type=CHOP;  // 248-250
+ *   uoffset offset_delta;
+ * }
+ * </pre>
+ * 
+ * The frame type same_frame_extended is represented by the tag value 251. If
+ * the frame type is same_frame_extended, it means the frame has exactly the
+ * same locals as the previous stack map frame and that the number of stack
+ * items is zero. The form of such a frame is then:
+ * 
+ * <pre>
+ * same_frame_extended {
+ *   u1 frame_type = SAME_FRAME_EXTENDED;  // 251
+ *   uoffset offset_delta;
+ * }
+ * </pre>
+ * 
+ * The frame type append_frame is represented by tags in the range [252-254]. If
+ * the frame_type is append_frame, it means that the 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. <br> <br> The
+ * form of such a frame is then:
+ * 
+ * <pre>
+ * append_frame {
+ *   u1 frame_type =APPEND;  // 252-254
+ *   uoffset offset_delta;
+ *   verification_type_info locals[frame_type -251];
+ * }
+ * </pre>
+ * 
+ * The 0th entry in locals represents the type of the first additional local
+ * variable. If locals[M] represents local variable N, then locals[M+1]
+ * represents local variable N+1 if locals[M] is one of Top_variable_info,
+ * Integer_variable_info, Float_variable_info, Null_variable_info,
+ * UninitializedThis_variable_info, Object_variable_info, or
+ * Uninitialized_variable_info, otherwise locals[M+1] represents local variable
+ * N+2. It is an error if, for any index i, locals[i] represents a local
+ * variable whose index is greater than the maximum number of local variables
+ * for the method. <br> <br> The frame type full_frame is represented by the tag
+ * value 255. The form of such a frame is then:
+ * 
+ * <pre>
+ * full_frame {
+ *   u1 frame_type = FULL_FRAME;  // 255
+ *   uoffset offset_delta;
+ *   ulocalvar number_of_locals;
+ *   verification_type_info locals[number_of_locals];
+ *   ustack number_of_stack_items;
+ *   verification_type_info stack[number_of_stack_items];
+ * }
+ * </pre>
+ * 
+ * The 0th entry in locals represents the type of local variable 0. If locals[M]
+ * represents local variable N, then locals[M+1] represents local variable N+1
+ * if locals[M] is one of Top_variable_info, Integer_variable_info,
+ * Float_variable_info, Null_variable_info, UninitializedThis_variable_info,
+ * Object_variable_info, or Uninitialized_variable_info, otherwise locals[M+1]
+ * represents local variable N+2. It is an error if, for any index i, locals[i]
+ * represents a local variable whose index is greater than the maximum number of
+ * local variables for the method. <br> <br> The 0th entry in stack represents
+ * the type of the bottom of the stack, and subsequent entries represent types
+ * of stack elements closer to the top of the operand stack. We shall refer to
+ * the bottom element of the stack as stack element 0, and to subsequent
+ * elements as stack element 1, 2 etc. If stack[M] represents stack element N,
+ * then stack[M+1] represents stack element N+1 if stack[M] is one of
+ * Top_variable_info, Integer_variable_info, Float_variable_info,
+ * Null_variable_info, UninitializedThis_variable_info, Object_variable_info, or
+ * Uninitialized_variable_info, otherwise stack[M+1] represents stack element
+ * N+2. It is an error if, for any index i, stack[i] represents a stack entry
+ * whose index is greater than the maximum operand stack size for the method.
+ * <br> <br> We say that an instruction in the byte code has a corresponding
+ * stack map frame if the offset in the offset field of the stack map frame is
+ * the same as the offset of the instruction in the byte codes. <br> <br> The
+ * verification_type_info structure consists of a one-byte tag followed by zero
+ * or more bytes, giving more information about the tag. Each
+ * verification_type_info structure specifies the verification type of one or
+ * two locations.
+ * 
+ * <pre>
+ * union verification_type_info {
+ *   Top_variable_info;
+ *   Integer_variable_info;
+ *   Float_variable_info;
+ *   Long_variable_info;
+ *   Double_variable_info;
+ *   Null_variable_info;
+ *   UninitializedThis_variable_info;
+ *   Object_variable_info;
+ *   Uninitialized_variable_info;
+ * }
+ * </pre>
+ * 
+ * The Top_variable_info type indicates that the local variable has the
+ * verification type top (T.)
+ * 
+ * <pre>
+ * Top_variable_info {
+ *   u1 tag = ITEM_Top; // 0
+ * }
+ * </pre>
+ * 
+ * The Integer_variable_info type indicates that the location contains the
+ * verification type int.
+ * 
+ * <pre>
+ * Integer_variable_info {
+ *   u1 tag = ITEM_Integer; // 1
+ * }
+ * </pre>
+ * 
+ * The Float_variable_info type indicates that the location contains the
+ * verification type float.
+ * 
+ * <pre>
+ * Float_variable_info {
+ *   u1 tag = ITEM_Float; // 2
+ * }
+ * </pre>
+ * 
+ * The Long_variable_info type indicates that the location contains the
+ * verification type long. If the location is a local variable, then:
+ * 
+ * <ul> <li>It must not be the local variable with the highest index.</li>
+ * <li>The next higher numbered local variable contains the verification type
+ * T.</li> </ul>
+ * 
+ * If the location is an operand stack entry, then:
+ * 
+ * <ul> <li>The current location must not be the topmost location of the
+ * operand stack.</li> <li>the next location closer to the top of the operand
+ * stack contains the verification type T.</li> </ul>
+ * 
+ * This structure gives the contents of two locations in the operand stack or in
+ * the local variables.
+ * 
+ * <pre>
+ * Long_variable_info {
+ *   u1 tag = ITEM_Long; // 4
+ * }
+ * </pre>
+ * 
+ * The Double_variable_info type indicates that the location contains the
+ * verification type double. If the location is a local variable, then:
+ * 
+ * <ul> <li>It must not be the local variable with the highest index.</li>
+ * <li>The next higher numbered local variable contains the verification type
+ * T. <li> </ul>
+ * 
+ * If the location is an operand stack entry, then:
+ * 
+ * <ul> <li>The current location must not be the topmost location of the
+ * operand stack.</li> <li>the next location closer to the top of the operand
+ * stack contains the verification type T.</li> </ul>
+ * 
+ * This structure gives the contents of two locations in in the operand stack or
+ * in the local variables.
+ * 
+ * <pre>
+ * Double_variable_info {
+ *   u1 tag = ITEM_Double; // 3
+ * }
+ * </pre>
+ * 
+ * The Null_variable_info type indicates that location contains the verification
+ * type null.
+ * 
+ * <pre>
+ * Null_variable_info {
+ *   u1 tag = ITEM_Null; // 5
+ * }
+ * </pre>
+ * 
+ * The UninitializedThis_variable_info type indicates that the location contains
+ * the verification type uninitializedThis.
+ * 
+ * <pre>
+ * UninitializedThis_variable_info {
+ *   u1 tag = ITEM_UninitializedThis; // 6
+ * }
+ * </pre>
+ * 
+ * The Object_variable_info type indicates that the location contains an
+ * instance of the class referenced by the constant pool entry.
+ * 
+ * <pre>
+ * Object_variable_info {
+ *   u1 tag = ITEM_Object; // 7
+ *   u2 cpool_index;
+ * }
+ * </pre>
+ * 
+ * The Uninitialized_variable_info indicates that the location contains the
+ * verification type uninitialized(offset). The offset item indicates the offset
+ * of the new instruction that created the object being stored in the location.
+ * 
+ * <pre>
+ * Uninitialized_variable_info {
+ *   u1 tag = ITEM_Uninitialized // 8
+ *   uoffset offset;
+ * }
+ * </pre>
+ * 
+ * @see "ClassFileFormat-Java6.fm Page 138 Friday, April 15, 2005 3:22 PM"
+ * 
+ * @author Eugene Kuleshov
+ */
+public class StackMapTableAttribute extends Attribute {
+    /**
+     * Frame has exactly the same locals as the previous stack map frame and
+     * number of stack items is zero.
+     */
+    public 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
+     */
+    public static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127
+
+    // (40-7f)
+
+    /**
+     * Reserved for future use
+     */
+    public 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;
+     */
+    public 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.
+     */
+    public 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;
+     */
+    public 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.
+     */
+    public static final int APPEND_FRAME = 252; // to 254 // fc-fe
+
+    /**
+     * Full frame
+     */
+    public static final int FULL_FRAME = 255; // ff
+
+    private static final int MAX_SHORT = 65535;
+
+    /**
+     * A <code>List</code> of <code>StackMapFrame</code> instances.
+     */
+    private List frames;
+
+    public StackMapTableAttribute() {
+        super("StackMapTable");
+    }
+
+    public StackMapTableAttribute(List frames) {
+        this();
+        this.frames = frames;
+    }
+
+    public List getFrames() {
+        return frames;
+    }
+
+    public StackMapFrame getFrame(Label label) {
+        for (int i = 0; i < frames.size(); i++) {
+            StackMapFrame frame = (StackMapFrame) frames.get(i);
+            if (frame.label == label) {
+                return frame;
+            }
+        }
+        return null;
+    }
+
+    public boolean isUnknown() {
+        return false;
+    }
+
+    public boolean isCodeAttribute() {
+        return true;
+    }
+
+    protected Attribute read(
+        ClassReader cr,
+        int off,
+        int len,
+        char[] buf,
+        int codeOff,
+        Label[] labels)
+    {
+
+        ArrayList frames = new ArrayList();
+
+        // note that this is not the size of Code attribute
+        boolean isExtCodeSize = cr.readInt(codeOff + 4) > MAX_SHORT;
+        boolean isExtLocals = cr.readUnsignedShort(codeOff + 2) > MAX_SHORT;
+        boolean isExtStack = cr.readUnsignedShort(codeOff) > MAX_SHORT;
+
+        int offset = 0;
+
+        int methodOff = getMethodOff(cr, codeOff, buf);
+        StackMapFrame frame = new StackMapFrame(getLabel(offset, labels),
+                calculateLocals(cr.readClass(cr.header + 2, buf), // owner
+                        cr.readUnsignedShort(methodOff), // method access
+                        cr.readUTF8(methodOff + 2, buf), // method name
+                        cr.readUTF8(methodOff + 4, buf)), // method desc
+                Collections.EMPTY_LIST);
+        frames.add(frame);
+
+        // System.err.println( cr.readUTF8( methodOff + 2, buf));
+        // System.err.println( offset +" delta:" + 0 +" : "+ frame);
+
+        int size;
+        if (isExtCodeSize) {
+            size = cr.readInt(off);
+            off += 4;
+        } else {
+            size = cr.readUnsignedShort(off);
+            off += 2;
+        }
+
+        for (; size > 0; size--) {
+            int tag = cr.readByte(off); // & 0xff;
+            off++;
+
+            List stack;
+            List locals;
+
+            int offsetDelta;
+            if (tag < SAME_LOCALS_1_STACK_ITEM_FRAME) {  // SAME_FRAME
+                offsetDelta = tag;
+
+                locals = new ArrayList(frame.locals);
+                stack = Collections.EMPTY_LIST;
+
+            } else if (tag < RESERVED) {  // SAME_LOCALS_1_STACK_ITEM_FRAME
+                offsetDelta = tag - SAME_LOCALS_1_STACK_ITEM_FRAME;
+
+                locals = new ArrayList(frame.locals);
+                stack = new ArrayList();
+                // read verification_type_info stack[1];
+                off = readType(stack, isExtCodeSize, cr, off, labels, buf);
+
+            } else {
+                if (isExtCodeSize) {
+                    offsetDelta = cr.readInt(off);
+                    off += 4;
+                } else {
+                    offsetDelta = cr.readUnsignedShort(off);
+                    off += 2;
+                }
+
+                if (tag == SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) {  // SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED
+                    locals = new ArrayList(frame.locals);
+                    stack = new ArrayList();
+                    // read verification_type_info stack[1];
+                    off = readType(stack, isExtCodeSize, cr, off, labels, buf);
+
+                } else if (tag >= CHOP_FRAME && tag < SAME_FRAME_EXTENDED) {  // CHOP_FRAME
+                    stack = Collections.EMPTY_LIST;
+
+                    int k = SAME_FRAME_EXTENDED - tag;
+                    // copy locals from prev frame and chop last k
+                    locals = new ArrayList(frame.locals.subList(0,
+                            frame.locals.size() - k));
+
+                } else if (tag == SAME_FRAME_EXTENDED) {  // SAME_FRAME_EXTENDED
+                    stack = Collections.EMPTY_LIST;
+                    locals = new ArrayList(frame.locals);
+
+                } else if ( /* tag>=APPEND && */tag < FULL_FRAME) {  // APPEND_FRAME
+                    stack = Collections.EMPTY_LIST;
+
+                    // copy locals from prev frame and append new k
+                    locals = new ArrayList(frame.locals);
+                    for (int k = tag - SAME_FRAME_EXTENDED; k > 0; k--) {
+                        off = readType(locals,
+                                isExtCodeSize,
+                                cr,
+                                off,
+                                labels,
+                                buf);
+                    }
+
+                } else if (tag == FULL_FRAME) {  // FULL_FRAME
+                    // read verification_type_info locals[number_of_locals];
+                    locals = new ArrayList();
+                    off = readTypes(locals,
+                            isExtLocals,
+                            isExtCodeSize,
+                            cr,
+                            off,
+                            labels,
+                            buf);
+
+                    // read verification_type_info stack[number_of_stack_items];
+                    stack = new ArrayList();
+                    off = readTypes(stack,
+                            isExtStack,
+                            isExtCodeSize,
+                            cr,
+                            off,
+                            labels,
+                            buf);
+
+                } else {
+                    throw new RuntimeException("Unknown frame type " + tag
+                            + " after offset " + offset);
+
+                }
+            }
+
+            offset += offsetDelta;
+
+            Label offsetLabel = getLabel(offset, labels);
+
+            frame = new StackMapFrame(offsetLabel, locals, stack);
+            frames.add(frame);
+            // System.err.println( tag +" " + offset +" delta:" + offsetDelta +
+            // " frameType:"+ frameType+" : "+ frame);
+
+            offset++;
+        }
+
+        return new StackMapTableAttribute(frames);
+    }
+
+    protected ByteVector write(
+        ClassWriter cw,
+        byte[] code,
+        int len,
+        int maxStack,
+        int maxLocals)
+    {
+        ByteVector bv = new ByteVector();
+        // TODO verify this value (MAX_SHORT)
+        boolean isExtCodeSize = code != null && code.length > MAX_SHORT;
+        writeSize(frames.size() - 1, bv, isExtCodeSize);
+
+        if (frames.size() < 2) {
+            return bv;
+        }
+
+        boolean isExtLocals = maxLocals > MAX_SHORT;
+        boolean isExtStack = maxStack > MAX_SHORT;
+
+        // skip the first frame
+        StackMapFrame frame = (StackMapFrame) frames.get(0);
+        List locals = frame.locals;
+        int offset = frame.label.getOffset();
+
+        for (int i = 1; i < frames.size(); i++) {
+            frame = (StackMapFrame) frames.get(i);
+
+            List clocals = frame.locals;
+            List cstack = frame.stack;
+            int coffset = frame.label.getOffset();
+
+            int clocalsSize = clocals.size();
+            int cstackSize = cstack.size();
+
+            int localsSize = locals.size();
+
+            int delta = coffset - offset;
+
+            int type = FULL_FRAME;
+            int k = 0;
+            if (cstackSize == 0) {
+                k = clocalsSize - localsSize;
+                switch (k) {
+                    case -3:
+                    case -2:
+                    case -1:
+                        type = CHOP_FRAME; // CHOP or FULL
+                        localsSize = clocalsSize; // for full_frame check
+                        break;
+
+                    case 0:
+                        // SAME, SAME_EXTENDED or FULL
+                        type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
+                        break;
+
+                    case 1:
+                    case 2:
+                    case 3:
+                        type = APPEND_FRAME; // APPEND or FULL
+                        break;
+                }
+            } else if (localsSize == clocalsSize && cstackSize == 1) {
+                // SAME_LOCAL_1_STACK or FULL
+                type = delta < 63
+                        ? SAME_LOCALS_1_STACK_ITEM_FRAME
+                        : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
+            }
+
+            if (type != FULL_FRAME) {
+                // verify if stack and locals are the same
+                for (int j = 0; j < localsSize && type != FULL_FRAME; j++) {
+                    if (!locals.get(j).equals(clocals.get(j)))
+                        type = FULL_FRAME;
+                }
+            }
+
+            switch (type) {
+                case SAME_FRAME:
+                    bv.putByte(delta);
+                    break;
+
+                case SAME_LOCALS_1_STACK_ITEM_FRAME:
+                    bv.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
+                    writeTypeInfos(bv, cw, cstack, 0, 1);
+                    break;
+
+                case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
+                    bv.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED);
+                    writeSize(delta, bv, isExtCodeSize);
+                    writeTypeInfos(bv, cw, cstack, 0, 1);
+                    break;
+
+                case SAME_FRAME_EXTENDED:
+                    bv.putByte(SAME_FRAME_EXTENDED);
+                    writeSize(delta, bv, isExtCodeSize);
+                    break;
+
+                case CHOP_FRAME:
+                    bv.putByte(SAME_FRAME_EXTENDED + k); // negative k
+                    writeSize(delta, bv, isExtCodeSize);
+                    break;
+
+                case APPEND_FRAME:
+                    bv.putByte(SAME_FRAME_EXTENDED + k); // positive k
+                    writeSize(delta, bv, isExtCodeSize);
+                    writeTypeInfos(bv,
+                            cw,
+                            clocals,
+                            clocalsSize - 1,
+                            clocalsSize);
+                    break;
+
+                case FULL_FRAME:
+                    bv.putByte(FULL_FRAME);
+                    writeSize(delta, bv, isExtCodeSize);
+                    writeSize(clocalsSize, bv, isExtLocals);
+                    writeTypeInfos(bv, cw, clocals, 0, clocalsSize);
+                    writeSize(cstackSize, bv, isExtStack);
+                    writeTypeInfos(bv, cw, cstack, 0, cstackSize);
+                    break;
+
+                default:
+                    throw new RuntimeException();
+            }
+            offset = coffset + 1; // compensating non first offset
+            locals = clocals;
+        }
+        return bv;
+    }
+
+    private void writeSize(int delta, ByteVector bv, boolean isExt) {
+        if (isExt) {
+            bv.putInt(delta);
+        } else {
+            bv.putShort(delta);
+        }
+    }
+
+    private void writeTypeInfos(
+        ByteVector bv,
+        ClassWriter cw,
+        List info,
+        int start,
+        int end)
+    {
+        for (int j = start; j < end; j++) {
+            StackMapType typeInfo = (StackMapType) info.get(j);
+            bv.putByte(typeInfo.getType());
+
+            switch (typeInfo.getType()) {
+                case StackMapType.ITEM_Object: //
+                    bv.putShort(cw.newClass(typeInfo.getObject()));
+                    break;
+
+                case StackMapType.ITEM_Uninitialized: //
+                    bv.putShort(typeInfo.getLabel().getOffset());
+                    break;
+
+            }
+        }
+    }
+
+    public static int getMethodOff(ClassReader cr, int codeOff, char[] buf) {
+        int off = cr.header + 6;
+
+        int interfacesCount = cr.readUnsignedShort(off);
+        off += 2 + interfacesCount * 2;
+
+        int fieldsCount = cr.readUnsignedShort(off);
+        off += 2;
+        for (; fieldsCount > 0; --fieldsCount) {
+            int attrCount = cr.readUnsignedShort(off + 6); // field attributes
+            off += 8;
+            for (; attrCount > 0; --attrCount) {
+                off += 6 + cr.readInt(off + 2);
+            }
+        }
+
+        int methodsCount = cr.readUnsignedShort(off);
+        off += 2;
+        for (; methodsCount > 0; --methodsCount) {
+            int methodOff = off;
+            int attrCount = cr.readUnsignedShort(off + 6); // method attributes
+            off += 8;
+            for (; attrCount > 0; --attrCount) {
+                String attrName = cr.readUTF8(off, buf);
+                off += 6;
+                if (attrName.equals("Code")) {
+                    if (codeOff == off) {
+                        return methodOff;
+                    }
+                }
+                off += cr.readInt(off - 4);
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Use method signature and access flags to resolve initial locals state.
+     * 
+     * @param className name of the method's owner class.
+     * @param access access flags of the method.
+     * @param methodName name of the method.
+     * @param methodDesc descriptor of the method.
+     * @return list of <code>StackMapType</code> instances representing locals
+     *         for an initial frame.
+     */
+    public static List calculateLocals(
+        String className,
+        int access,
+        String methodName,
+        String methodDesc)
+    {
+        List locals = new ArrayList();
+
+        // TODO
+        if ("<init>".equals(methodName)
+                && !className.equals("java/lang/Object"))
+        {
+            StackMapType typeInfo = StackMapType.getTypeInfo(StackMapType.ITEM_UninitializedThis);
+            typeInfo.setObject(className); // this
+            locals.add(typeInfo);
+        } else if ((access & Opcodes.ACC_STATIC) == 0) {
+            StackMapType typeInfo = StackMapType.getTypeInfo(StackMapType.ITEM_Object);
+            typeInfo.setObject(className); // this
+            locals.add(typeInfo);
+        }
+
+        Type[] types = Type.getArgumentTypes(methodDesc);
+        for (int i = 0; i < types.length; i++) {
+            Type t = types[i];
+            StackMapType smt;
+            switch (t.getSort()) {
+                case Type.LONG:
+                    smt = StackMapType.getTypeInfo(StackMapType.ITEM_Long);
+                    break;
+                case Type.DOUBLE:
+                    smt = StackMapType.getTypeInfo(StackMapType.ITEM_Double);
+                    break;
+
+                case Type.FLOAT:
+                    smt = StackMapType.getTypeInfo(StackMapType.ITEM_Float);
+                    break;
+
+                case Type.ARRAY:
+                case Type.OBJECT:
+                    smt = StackMapType.getTypeInfo(StackMapType.ITEM_Object);
+                    smt.setObject(t.getDescriptor()); // TODO verify name
+                    break;
+
+                default:
+                    smt = StackMapType.getTypeInfo(StackMapType.ITEM_Integer);
+                    break;
+            }
+        }
+
+        return locals;
+    }
+
+    private int readTypes(
+        List info,
+        boolean isExt,
+        boolean isExtCodeSize,
+        ClassReader cr,
+        int off,
+        Label[] labels,
+        char[] buf)
+    {
+        int n = 0;
+        if (isExt) {
+            n = cr.readInt(off);
+            off += 4;
+        } else {
+            n = cr.readUnsignedShort(off);
+            off += 2;
+        }
+
+        for (; n > 0; n--) {
+            off = readType(info, isExtCodeSize, cr, off, labels, buf);
+        }
+        return off;
+    }
+
+    private int readType(
+        List info,
+        boolean isExtCodeSize,
+        ClassReader cr,
+        int off,
+        Label[] labels,
+        char[] buf)
+    {
+        int itemType = cr.readByte(off++);
+        StackMapType typeInfo = StackMapType.getTypeInfo(itemType);
+        info.add(typeInfo);
+        switch (itemType) {
+            // case StackMapType.ITEM_Long: //
+            // case StackMapType.ITEM_Double: //
+            // info.add(StackMapType.getTypeInfo(StackMapType.ITEM_Top));
+            // break;
+
+            case StackMapType.ITEM_Object: //
+                typeInfo.setObject(cr.readClass(off, buf));
+                off += 2;
+                break;
+
+            case StackMapType.ITEM_Uninitialized: //
+                int offset;
+                if (isExtCodeSize) {
+                    offset = cr.readInt(off);
+                    off += 4;
+                } else {
+                    offset = cr.readUnsignedShort(off);
+                    off += 2;
+                }
+
+                typeInfo.setLabel(getLabel(offset, labels));
+                break;
+        }
+        return off;
+    }
+
+    private Label getLabel(int offset, Label[] labels) {
+        Label l = labels[offset];
+        if (l != null) {
+            return l;
+        }
+        return labels[offset] = new Label();
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer("StackMapTable[");
+        for (int i = 0; i < frames.size(); i++) {
+            sb.append('\n').append('[').append(frames.get(i)).append(']');
+        }
+        sb.append("\n]");
+        return sb.toString();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/attrs/StackMapType.java b/asmx/src/org/objectweb/asm/attrs/StackMapType.java
new file mode 100644
index 0000000..ae3b8ce
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/attrs/StackMapType.java
@@ -0,0 +1,114 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.attrs;
+
+import org.objectweb.asm.Label;
+
+/**
+ * Verification type info used by {@link StackMapAttribute}.
+ * 
+ * @see <a href="http://www.jcp.org/en/jsr/detail?id=139">JSR 139 : Connected
+ *      Limited Device Configuration 1.1</a>
+ * 
+ * @see "ClassFileFormat-Java6.fm Page 138 Friday, April 15, 2005 3:22 PM"
+ * 
+ * @author Eugene Kuleshov
+ */
+
+public class StackMapType {
+
+    public static final int ITEM_Top = 0;
+    public static final int ITEM_Integer = 1;
+    public static final int ITEM_Float = 2;
+    public static final int ITEM_Double = 3;
+    public static final int ITEM_Long = 4;
+    public static final int ITEM_Null = 5;
+    public static final int ITEM_UninitializedThis = 6;
+    public static final int ITEM_Object = 7;
+    public static final int ITEM_Uninitialized = 8;
+
+    public static final String[] ITEM_NAMES = {
+        "Top",
+        "Integer",
+        "Float",
+        "Double",
+        "Long",
+        "Null",
+        "UninitializedThis",
+        "Object",
+        "Uninitialized" };
+
+    private int type;
+    private Label offset;
+    private String object;
+
+    private StackMapType(int type) {
+        this.type = type;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public static StackMapType getTypeInfo(int itemType) {
+        if (itemType < ITEM_Top || itemType > ITEM_Uninitialized) {
+            throw new IllegalArgumentException("" + itemType);
+        }
+        return new StackMapType(itemType);
+    }
+
+    public void setLabel(Label offset) {
+        this.offset = offset;
+    }
+
+    public void setObject(String object) {
+        this.object = object;
+    }
+
+    public Label getLabel() {
+        return offset;
+    }
+
+    public String getObject() {
+        return object;
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer(ITEM_NAMES[type]);
+        if (type == ITEM_Object) {
+            sb.append(":").append(object);
+        }
+        if (type == ITEM_Uninitialized) {
+            sb.append(":L").append(System.identityHashCode(offset));
+        }
+        return sb.toString();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/commons/AdviceAdapter.java b/asmx/src/org/objectweb/asm/commons/AdviceAdapter.java
new file mode 100644
index 0000000..141c8e2
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/AdviceAdapter.java
@@ -0,0 +1,643 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A <code>MethodAdapter</code> to dispatch method body instruction
+ * <p>
+ * The behavior 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 ArrayList stackFrame;
+    private HashMap branches;
+
+    
+    /**
+     * Creates a new {@link AdviceAdapter}.
+     * 
+     * @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 AdviceAdapter(MethodVisitor mv, int access, String name, String desc) {
+        super(mv, access, name, desc);
+        methodAccess = access;
+        methodDesc = desc;
+
+        constructor = "<init>".equals(name);
+        if (!constructor) {
+            superInitialized = true;
+            onMethodEnter();
+        } else {
+            stackFrame = new ArrayList();
+            branches = new HashMap();
+        }
+    }
+
+    public void visitLabel(Label label) {
+        mv.visitLabel(label);
+
+        if (constructor && branches != null) {
+            ArrayList frame = (ArrayList) branches.get(label);
+            if (frame != null) {
+                stackFrame = frame;
+                branches.remove(label);
+            }
+        }
+    }
+
+    public void visitInsn(int opcode) {
+        if (constructor) {
+            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();
+                    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 Opcodes.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:
+                // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    pushValue(o1);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP_X2:
+                // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    Object o3 = popValue();
+                    pushValue(o1);
+                    pushValue(o3);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP2:
+                // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    pushValue(o2);
+                    pushValue(o1);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP2_X1:
+                // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    Object o3 = popValue();
+                    pushValue(o2);
+                    pushValue(o1);
+                    pushValue(o3);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case DUP2_X2:
+                // TODO optimize this
+                {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    Object o3 = popValue();
+                    Object o4 = popValue();
+                    pushValue(o2);
+                    pushValue(o1);
+                    pushValue(o4);
+                    pushValue(o3);
+                    pushValue(o2);
+                    pushValue(o1);
+                }
+                    break;
+
+                case SWAP: {
+                    Object o1 = popValue();
+                    Object o2 = popValue();
+                    pushValue(o1);
+                    pushValue(o2);
+                }
+                    break;
+            }
+        } else {
+            switch (opcode) {
+                case RETURN:
+                case IRETURN:
+                case FRETURN:
+                case ARETURN:
+                case LRETURN:
+                case DRETURN:
+                case ATHROW:
+                    onMethodExit(opcode);
+                    break;
+            }
+        }
+        mv.visitInsn(opcode);
+    }
+
+    public void visitVarInsn(int opcode, 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;
+            }
+        }
+    }
+
+    public void visitFieldInsn(
+        int opcode,
+        String owner,
+        String name,
+        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);
+                    }
+            }
+        }
+    }
+
+    public void visitIntInsn(int opcode, int operand) {
+        mv.visitIntInsn(opcode, operand);
+
+        if (constructor) {
+            switch (opcode) {
+                case BIPUSH:
+                case SIPUSH:
+                    pushValue(OTHER);
+            }
+        }
+    }
+
+    public void visitLdcInsn(Object cst) {
+        mv.visitLdcInsn(cst);
+
+        if (constructor) {
+            pushValue(OTHER);
+            if (cst instanceof Double || cst instanceof Long) {
+                pushValue(OTHER);
+            }
+        }
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+        mv.visitMultiANewArrayInsn(desc, dims);
+
+        if (constructor) {
+            for (int i = 0; i < dims; i++) {
+                popValue();
+            }
+            pushValue(OTHER);
+        }
+    }
+
+    public void visitTypeInsn(int opcode, String name) {
+        mv.visitTypeInsn(opcode, name);
+
+        // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack
+        if (constructor && opcode == NEW) {
+            pushValue(OTHER);
+        }
+    }
+
+    public void visitMethodInsn(
+        int opcode,
+        String owner,
+        String name,
+        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);
+                }
+            }
+        }
+    }
+
+    public void visitJumpInsn(int opcode, 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);
+        }
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
+        mv.visitLookupSwitchInsn(dflt, keys, labels);
+
+        if (constructor) {
+            popValue();
+            addBranches(dflt, labels);
+        }
+    }
+
+    public void visitTableSwitchInsn(
+        int min,
+        int max,
+        Label dflt,
+        Label[] labels)
+    {
+        mv.visitTableSwitchInsn(min, max, dflt, labels);
+
+        if (constructor) {
+            popValue();
+            addBranches(dflt, labels);
+        }
+    }
+
+    private void addBranches(Label dflt, Label[] labels) {
+        addBranch(dflt);
+        for (int i = 0; i < labels.length; i++) {
+            addBranch(labels[i]);
+        }
+    }
+
+    private void addBranch(Label label) {
+        if (branches.containsKey(label)) {
+            return;
+        }
+        ArrayList frame = new ArrayList();
+        frame.addAll(stackFrame);
+        branches.put(label, frame);
+    }
+
+    private Object popValue() {
+        return stackFrame.remove(stackFrame.size()-1);
+    }
+
+    private Object peekValue() {
+        return stackFrame.get(stackFrame.size()-1);
+    }
+    
+    private void pushValue(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 abstract 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(int opcode, Object param) {
+     *     ...
+     * </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 abstract void onMethodExit(int opcode);
+
+    // TODO onException, onMethodCall
+    
+}
+
diff --git a/asmx/src/org/objectweb/asm/commons/EmptyVisitor.java b/asmx/src/org/objectweb/asm/commons/EmptyVisitor.java
new file mode 100644
index 0000000..33523e7
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/EmptyVisitor.java
@@ -0,0 +1,277 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.PrecompiledMethodVisitor;
+import org.objectweb.asm.TypePath;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * An empty implementation of the ASM visitor interfaces.
+ * 
+ * @author Eric Bruneton
+ */
+public class EmptyVisitor implements
+        ClassVisitor,
+        FieldVisitor,
+        PrecompiledMethodVisitor, // changed from MethodVisitor
+        AnnotationVisitor,
+        TypeAnnotationVisitor // jaime
+{
+
+    public void visit(
+        int version,
+        int access,
+        String name,
+        String signature,
+        String superName,
+        String[] interfaces)
+    {
+    }
+
+    public void visitSource(String source, String debug) {
+    }
+
+    public void visitOuterClass(String owner, String name, String desc) {
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return this;
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+        boolean visible,
+        boolean inCode)
+    {
+        return this;
+    }
+    //end jaime
+
+    public void visitAttribute(Attribute attr) {
+    }
+
+    public void visitInnerClass(
+        String name,
+        String outerName,
+        String innerName,
+        int access)
+    {
+    }
+
+    public FieldVisitor visitField(
+        int access,
+        String name,
+        String desc,
+        String signature,
+        Object value)
+    {
+        return this;
+    }
+
+    public MethodVisitor visitMethod(
+        int access,
+        String name,
+        String desc,
+        String signature,
+        String[] exceptions)
+    {
+        return this;
+    }
+
+    public void visitEnd() {
+    }
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        return this;
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+        int parameter,
+        String desc,
+        boolean visible)
+    {
+        return this;
+    }
+
+    public void visitCode() {
+    }
+
+    public void visitCurrentPosition(int position) {
+    }
+
+    public void visitInsn(int opcode) {
+    }
+
+    public void visitIntInsn(int opcode, int operand) {
+    }
+
+    public void visitVarInsn(int opcode, int var) {
+    }
+
+    public void visitTypeInsn(int opcode, String desc) {
+    }
+
+    public void visitFieldInsn(
+        int opcode,
+        String owner,
+        String name,
+        String desc)
+    {
+    }
+
+    public void visitMethodInsn(
+        int opcode,
+        String owner,
+        String name,
+        String desc)
+    {
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+        Object... bsmArgs) {
+    }
+
+    public void visitJumpInsn(int opcode, Label label) {
+    }
+
+    public void visitLabel(Label label) {
+    }
+
+    public void visitLdcInsn(Object cst) {
+    }
+
+    public void visitIincInsn(int var, int increment) {
+    }
+
+    public void visitTableSwitchInsn(
+        int min,
+        int max,
+        Label dflt,
+        Label labels[])
+    {
+    }
+
+    public void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]) {
+    }
+
+    public void visitMultiANewArrayInsn(String desc, int dims) {
+    }
+
+    public void visitInvokeDynamicInsn(int a, int b) {
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(
+        int typeRef,
+        TypePath typePath,
+        String desc,
+        boolean visible)
+    {
+        return this;
+    }
+
+    public void visitTryCatchBlock(
+        Label start,
+        Label end,
+        Label handler,
+        String type)
+    {
+    }
+
+    public void visitLocalVariable(
+        String name,
+        String desc,
+        String signature,
+        Label start,
+        Label end,
+        int index)
+    {
+    }
+
+    public void visitLineNumber(int line, Label start) {
+    }
+
+    public void visitMaxs(int maxStack, int maxLocals) {
+    }
+
+    public void visit(String name, Object value) {
+    }
+
+    public void visitEnum(String name, String desc, String value) {
+    }
+
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+        return this;
+    }
+
+    public AnnotationVisitor visitArray(String name) {
+        return this;
+    }
+
+    //jaime
+    public void visitXTargetType(int target_type) {
+    }
+    public void visitXOffset(int offset) {
+    }
+    public void visitXLocationLength(int location_length) {
+    }
+    public void visitXLocation(TypePathEntry location) {
+    }
+    public void visitXNumEntries(int num_entries) {
+    }
+    public void visitXStartPc(int start_pc) {
+    }
+    public void visitXLength(int length) {
+    }
+    public void visitXIndex(int index) {
+    }
+    public void visitXParamIndex(int param_index) {
+    }
+    public void visitXBoundIndex(int bound_index) {
+    }
+    public void visitXTypeIndex(int type_index) {
+    }
+    //end jaime
+    public void visitXExceptionIndex(int exception_index) {
+    }
+    public void visitXNameAndArgsSize() {
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/commons/GeneratorAdapter.java b/asmx/src/org/objectweb/asm/commons/GeneratorAdapter.java
new file mode 100644
index 0000000..40b4db5
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/GeneratorAdapter.java
@@ -0,0 +1,1454 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link org.objectweb.asm.MethodAdapter} 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(&quot;Hello world!&quot;);
+ *     }
+ * }
+ * </pre>
+ * 
+ * can be generated as follows:
+ * 
+ * <pre>
+ * ClassWriter cw = new ClassWriter(true);
+ * cw.visit(V1_1, ACC_PUBLIC, &quot;Example&quot;, null, &quot;java/lang/Object&quot;, null);
+ * 
+ * Method m = Method.getMethod(&quot;void &lt;init&gt; ()&quot;);
+ * 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(&quot;void main (String[])&quot;);
+ * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw);
+ * mg.getStatic(Type.getType(System.class), &quot;out&quot;, Type.getType(PrintStream.class));
+ * mg.push(&quot;Hello world!&quot;);
+ * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod(&quot;void println (String)&quot;));
+ * mg.returnValue();
+ * mg.endMethod();
+ * 
+ * cw.visitEnd();
+ * </pre>
+ * 
+ * @author Juozas Baliuka
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class GeneratorAdapter extends LocalVariablesSorter {
+
+    private final static Type BYTE_TYPE = Type.getType("Ljava/lang/Byte;");
+
+    private final static Type BOOLEAN_TYPE = Type.getType("Ljava/lang/Boolean;");
+
+    private final static Type SHORT_TYPE = Type.getType("Ljava/lang/Short;");
+
+    private final static Type CHARACTER_TYPE = Type.getType("Ljava/lang/Character;");
+
+    private final static Type INTEGER_TYPE = Type.getType("Ljava/lang/Integer;");
+
+    private final static Type FLOAT_TYPE = Type.getType("Ljava/lang/Float;");
+
+    private final static Type LONG_TYPE = Type.getType("Ljava/lang/Long;");
+
+    private final static Type DOUBLE_TYPE = Type.getType("Ljava/lang/Double;");
+
+    private final static Type NUMBER_TYPE = Type.getType("Ljava/lang/Number;");
+
+    private final static Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;");
+
+    private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
+
+    private final static Method CHAR_VALUE = Method.getMethod("char charValue()");
+
+    private final static Method INT_VALUE = Method.getMethod("int intValue()");
+
+    private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()");
+
+    private final static Method LONG_VALUE = Method.getMethod("long longValue()");
+
+    private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int ADD = Opcodes.IADD;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int SUB = Opcodes.ISUB;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int MUL = Opcodes.IMUL;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int DIV = Opcodes.IDIV;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int REM = Opcodes.IREM;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int NEG = Opcodes.INEG;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int SHL = Opcodes.ISHL;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int SHR = Opcodes.ISHR;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int USHR = Opcodes.IUSHR;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int AND = Opcodes.IAND;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int OR = Opcodes.IOR;
+
+    /**
+     * Constant for the {@link #math math} method.
+     */
+    public final static int XOR = Opcodes.IXOR;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public final static int EQ = Opcodes.IFEQ;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public final static int NE = Opcodes.IFNE;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public final static int LT = Opcodes.IFLT;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public final static int GE = Opcodes.IFGE;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public final static int GT = Opcodes.IFGT;
+
+    /**
+     * Constant for the {@link #ifCmp ifCmp} method.
+     */
+    public final static 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 localTypes;
+
+    /**
+     * Creates a new {@link GeneratorAdapter}.
+     * 
+     * @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(
+        MethodVisitor mv,
+        int access,
+        String name,
+        String desc)
+    {
+        super(access, desc, mv);
+        this.access = access;
+        this.returnType = Type.getReturnType(desc);
+        this.argumentTypes = Type.getArgumentTypes(desc);
+        this.localTypes = new ArrayList();
+    }
+
+    /**
+     * Creates a new {@link GeneratorAdapter}.
+     * 
+     * @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)
+    {
+        super(access, method.getDescriptor(), mv);
+        this.access = access;
+        this.returnType = method.getReturnType();
+        this.argumentTypes = method.getArgumentTypes();
+        this.localTypes = new ArrayList();
+    }
+
+    /**
+     * Creates a new {@link GeneratorAdapter}.
+     * 
+     * @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 {
+            mv.visitLdcInsn(value);
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // 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
+    // ------------------------------------------------------------------------
+
+    /**
+     * 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) {
+        int local = super.newLocal(type.getSize());
+        setLocalType(local, type);
+        return local;
+    }
+
+    /**
+     * Returns the type of the given local variable.
+     * 
+     * @param local a local variable identifier, as returned by {@link #newLocal
+     *        newLocal}.
+     * @return the type of the given local variable.
+     */
+    public Type getLocalType(final int local) {
+        return (Type) localTypes.get(local - firstLocal);
+    }
+
+    /**
+     * Sets the current type of the given local variable.
+     * 
+     * @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
+     */
+    private 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 #newLocal
+     *        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 #newLocal
+     *        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 #newLocal
+     *        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 #newLocal
+     *        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
+    // ------------------------------------------------------------------------
+
+    /**
+     * 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 = type;
+            switch (type.getSort()) {
+                case Type.BYTE:
+                    boxed = BYTE_TYPE;
+                    break;
+                case Type.BOOLEAN:
+                    boxed = BOOLEAN_TYPE;
+                    break;
+                case Type.SHORT:
+                    boxed = SHORT_TYPE;
+                    break;
+                case Type.CHAR:
+                    boxed = CHARACTER_TYPE;
+                    break;
+                case Type.INT:
+                    boxed = INTEGER_TYPE;
+                    break;
+                case Type.FLOAT:
+                    boxed = FLOAT_TYPE;
+                    break;
+                case Type.LONG:
+                    boxed = LONG_TYPE;
+                    break;
+                case Type.DOUBLE:
+                    boxed = DOUBLE_TYPE;
+                    break;
+            }
+            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 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) {
+        int intOp = -1;
+        int jumpMode = mode;
+        switch (mode) {
+            case GE:
+                jumpMode = LT;
+                break;
+            case LE:
+                jumpMode = GT;
+                break;
+        }
+        switch (type.getSort()) {
+            case Type.LONG:
+                mv.visitInsn(Opcodes.LCMP);
+                break;
+            case Type.DOUBLE:
+                mv.visitInsn(Opcodes.DCMPG);
+                break;
+            case Type.FLOAT:
+                mv.visitInsn(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:
+                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(jumpMode, 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 #newLocal
+     *        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);
+    }
+
+    // ------------------------------------------------------------------------
+    // 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) {
+        String desc;
+        if (type.getSort() == Type.ARRAY) {
+            desc = type.getDescriptor();
+        } else {
+            desc = type.getInternalName();
+        }
+        mv.visitTypeInsn(opcode, desc);
+    }
+
+    /**
+     * 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);
+        }
+    }
+
+    /**
+     * 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)
+    {
+        mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName());
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/commons/LocalVariablesSorter.java b/asmx/src/org/objectweb/asm/commons/LocalVariablesSorter.java
new file mode 100644
index 0000000..1253a0b
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/LocalVariablesSorter.java
@@ -0,0 +1,136 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link MethodAdapter} that renumbers local variables in their order of
+ * appearance. This adapter allows one to easily add new local variables to a
+ * method.
+ * 
+ * @author Chris Nokleberg
+ * @author Eric Bruneton
+ */
+public class LocalVariablesSorter extends MethodAdapter {
+
+    /**
+     * 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];
+
+    protected final int firstLocal;
+
+    private int nextLocal;
+
+    public LocalVariablesSorter(
+        final int access,
+        final String desc,
+        final MethodVisitor mv)
+    {
+        super(mv);
+        Type[] args = Type.getArgumentTypes(desc);
+        nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1;
+        for (int i = 0; i < args.length; i++) {
+            nextLocal += args[i].getSize();
+        }
+        firstLocal = nextLocal;
+    }
+
+    public void visitVarInsn(final int opcode, final int var) {
+        int size;
+        switch (opcode) {
+            case Opcodes.LLOAD:
+            case Opcodes.LSTORE:
+            case Opcodes.DLOAD:
+            case Opcodes.DSTORE:
+                size = 2;
+                break;
+            default:
+                size = 1;
+        }
+        mv.visitVarInsn(opcode, remap(var, size));
+    }
+
+    public void visitIincInsn(final int var, final int increment) {
+        mv.visitIincInsn(remap(var, 1), increment);
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        mv.visitMaxs(maxStack, nextLocal);
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1;
+        mv.visitLocalVariable(name, desc, signature, start, end, remap(index, size));
+    }
+
+    // -------------
+
+    protected int newLocal(final int size) {
+        int var = nextLocal;
+        nextLocal += size;
+        return var;
+    }
+
+    private int remap(final int var, final int size) {
+        if (var < firstLocal) {
+            return var;
+        }
+        int key = 2 * var + size - 1;
+        int length = mapping.length;
+        if (key >= length) {
+            int[] newMapping = new int[Math.max(2 * length, key + 1)];
+            System.arraycopy(mapping, 0, newMapping, 0, length);
+            mapping = newMapping;
+        }
+        int value = mapping[key];
+        if (value == 0) {
+            value = nextLocal + 1;
+            mapping[key] = value;
+            nextLocal += size;
+        }
+        return value - 1;
+    }
+    
+}
diff --git a/asmx/src/org/objectweb/asm/commons/Method.java b/asmx/src/org/objectweb/asm/commons/Method.java
new file mode 100644
index 0000000..741e7b5
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/Method.java
@@ -0,0 +1,220 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.objectweb.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 final static Map DESCRIPTORS;
+
+    static {
+        DESCRIPTORS = new HashMap();
+        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));
+    }
+
+    /**
+     * 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", ...).
+     * @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
+    {
+        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();
+        }
+        // TODO: Check validity of returnType, methodName and arguments.
+        String returnType = method.substring(0, space);
+        String methodName = method.substring(space + 1, start - 1).trim();
+        StringBuffer sb = new StringBuffer();
+        sb.append('(');
+        int p;
+        do {
+            p = method.indexOf(',', start);
+            if (p == -1) {
+                sb.append(map(method.substring(start, end).trim()));
+            } else {
+                sb.append(map(method.substring(start, p).trim()));
+                start = p + 1;
+            }
+        } while (p != -1);
+        sb.append(')');
+        sb.append(map(returnType));
+        return new Method(methodName, sb.toString());
+    }
+
+    private static String map(final String type) {
+        if (type.equals("")) {
+            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 = (String) DESCRIPTORS.get(t);
+        if (desc != null) {
+            sb.append(desc);
+        } else {
+            sb.append('L');
+            if (t.indexOf('.') < 0) {
+                sb.append("java/lang/" + 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);
+    }
+
+    public String toString() {
+        return name + desc;
+    }
+
+    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);
+    }
+
+    public int hashCode() {
+        return name.hashCode() ^ desc.hashCode();
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/commons/SerialVersionUIDAdder.java b/asmx/src/org/objectweb/asm/commons/SerialVersionUIDAdder.java
new file mode 100644
index 0000000..800ad4b
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/SerialVersionUIDAdder.java
@@ -0,0 +1,490 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import java.io.ByteArrayOutputStream;
+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 org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A {@link ClassAdapter} 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, &lt;clinit&gt;, 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, &lt;init&gt;, 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] &gt;&gt;&gt; 24) &amp; 0xFF) |
+ * ((sha[0] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 8 |
+ * ((sha[0] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 16 |
+ * ((sha[0] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 24 |
+ * ((sha[1] &gt;&gt;&gt; 24) &amp; 0xFF) &lt;&lt; 32 |
+ * ((sha[1] &gt;&gt;&gt; 16) &amp; 0xFF) &lt;&lt; 40 |
+ * ((sha[1] &gt;&gt;&gt; 8) &amp; 0xFF) &lt;&lt; 48 |
+ * ((sha[1] &gt;&gt;&gt; 0) &amp; 0xFF) &lt;&lt; 56;
+ * </pre>
+ * 
+ * @author Rajendra Inamdar, Vishal Vishnoi
+ */
+public class SerialVersionUIDAdder extends ClassAdapter {
+
+    /**
+     * Flag that indicates if we need to compute SVUID.
+     */
+    protected boolean computeSVUID;
+
+    /**
+     * Set to true if the class already has SVUID.
+     */
+    protected boolean hasSVUID;
+
+    /**
+     * Classes access flags.
+     */
+    protected int access;
+
+    /**
+     * Internal name of the class
+     */
+    protected String name;
+
+    /**
+     * Interfaces implemented by the class.
+     */
+    protected String[] interfaces;
+
+    /**
+     * Collection of fields. (except private static and private transient
+     * fields)
+     */
+    protected Collection svuidFields;
+
+    /**
+     * Set to true if the class has static initializer.
+     */
+    protected boolean hasStaticInitializer;
+
+    /**
+     * Collection of non-private constructors.
+     */
+    protected Collection svuidConstructors;
+
+    /**
+     * Collection of non-private methods.
+     */
+    protected Collection svuidMethods;
+
+    /**
+     * Creates a new {@link SerialVersionUIDAdder}.
+     * 
+     * @param cv a {@link ClassVisitor} to which this visitor will delegate
+     *        calls.
+     */
+    public SerialVersionUIDAdder(final ClassVisitor cv) {
+        super(cv);
+        svuidFields = new ArrayList();
+        svuidConstructors = new ArrayList();
+        svuidMethods = new ArrayList();
+    }
+
+    // ------------------------------------------------------------------------
+    // Overriden methods
+    // ------------------------------------------------------------------------
+
+    /*
+     * Visit class header and get class name, access , and intefraces
+     * informatoin (step 1,2, and 3) for SVUID computation.
+     */
+    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 determince if there is a class initializer (step 6).
+     */
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        if (computeSVUID) {
+            if (name.equals("<clinit>")) {
+                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 (name.equals("<init>")) {
+                    svuidConstructors.add(new Item(name, mods, desc));
+                } else if (!name.equals("<clinit>")) {
+                    svuidMethods.add(new Item(name, mods, desc));
+                }
+            }
+        }
+
+        return cv.visitMethod(access, name, desc, signature, exceptions);
+    }
+
+    /*
+     * Gets class field information for step 4 of the alogrithm. Also determines
+     * if the class already has a SVUID.
+     */
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        if (computeSVUID) {
+            if (name.equals("serialVersionUID")) {
+                // 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.
+             */
+            int mods = access
+                    & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE
+                            | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC
+                            | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT);
+
+            if (((access & Opcodes.ACC_PRIVATE) == 0)
+                    || ((access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0))
+            {
+                svuidFields.add(new Item(name, mods, desc));
+            }
+        }
+
+        return super.visitField(access, name, desc, signature, value);
+    }
+
+    /*
+     * Add the SVUID if class doesn't have one
+     */
+    public void visitEnd() {
+        // compute SVUID and add it to the class
+        if (computeSVUID && !hasSVUID) {
+            try {
+                cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC,
+                        "serialVersionUID",
+                        "J",
+                        null,
+                        new Long(computeSVUID()));
+            } catch (Throwable e) {
+                throw new RuntimeException("Error while computing SVUID for "
+                        + name, e);
+            }
+        }
+
+        super.visitEnd();
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Returns the value of SVUID if the class doesn't have one already. Please
+     * note that 0 is returned if the class already has SVUID, thus use
+     * <code>isHasSVUID</code> to determine if the class already had an SVUID.
+     * 
+     * @return Returns the serial version UID
+     * @throws IOException
+     */
+    protected long computeSVUID() throws IOException {
+        if (hasSVUID) {
+            return 0;
+        }
+
+        ByteArrayOutputStream bos = null;
+        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 signatutes 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(byte[] value) {
+        try {
+            return MessageDigest.getInstance("SHA").digest(value);
+        } catch (Exception e) {
+            throw new UnsupportedOperationException(e);
+        }
+    }
+
+    /**
+     * 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 void writeItems(
+        final Collection itemCollection,
+        final DataOutputStream dos,
+        final boolean dotted) throws IOException
+    {
+        int size = itemCollection.size();
+        Item items[] = (Item[]) 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
+    // ------------------------------------------------------------------------
+
+    static class Item implements Comparable {
+
+        String name;
+
+        int access;
+
+        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 Object o) {
+            Item other = (Item) o;
+            int retVal = name.compareTo(other.name);
+            if (retVal == 0) {
+                retVal = desc.compareTo(other.desc);
+            }
+            return retVal;
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/commons/StaticInitMerger.java b/asmx/src/org/objectweb/asm/commons/StaticInitMerger.java
new file mode 100644
index 0000000..9aabe44
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/StaticInitMerger.java
@@ -0,0 +1,99 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A {@link ClassAdapter} that merges clinit methods into a single one.
+ * 
+ * @author Eric Bruneton
+ */
+public class StaticInitMerger extends ClassAdapter {
+
+    private String name;
+
+    private MethodVisitor clinit;
+
+    private String prefix;
+
+    private int counter;
+
+    public StaticInitMerger(final String prefix, final ClassVisitor cv) {
+        super(cv);
+        this.prefix = prefix;
+    }
+
+    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;
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        MethodVisitor mv;
+        if (name.equals("<clinit>")) {
+            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;
+    }
+
+    public void visitEnd() {
+        if (clinit != null) {
+            clinit.visitInsn(Opcodes.RETURN);
+            clinit.visitMaxs(0, 0);
+        }
+        cv.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/commons/TableSwitchGenerator.java b/asmx/src/org/objectweb/asm/commons/TableSwitchGenerator.java
new file mode 100644
index 0000000..73d8d92
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/commons/TableSwitchGenerator.java
@@ -0,0 +1,55 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import org.objectweb.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/asmx/src/org/objectweb/asm/optimizer/AnnotationConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/AnnotationConstantsCollector.java
new file mode 100644
index 0000000..70b3f14
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/AnnotationConstantsCollector.java
@@ -0,0 +1,155 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Type;
+
+/**
+ * An {@link AnnotationVisitor} that collects the {@link Constant}s of the
+ * annotations it visits.
+ *
+ * @author Eric Bruneton
+ */
+public class AnnotationConstantsCollector implements AnnotationVisitor {
+
+    private final AnnotationVisitor av;
+
+    private final ConstantPool cp;
+
+    public AnnotationConstantsCollector(
+        final AnnotationVisitor av,
+        final ConstantPool cp)
+    {
+        this.av = av;
+        this.cp = cp;
+    }
+
+    @Override
+    public void visit(final String name, final Object value) {
+        if (name != null) {
+            cp.newUTF8(name);
+        }
+        if (value instanceof Byte) {
+            cp.newInteger(((Byte) value).byteValue());
+        } else if (value instanceof Boolean) {
+            cp.newInteger(((Boolean) value).booleanValue() ? 1 : 0);
+        } else if (value instanceof Character) {
+            cp.newInteger(((Character) value).charValue());
+        } else if (value instanceof Short) {
+            cp.newInteger(((Short) value).shortValue());
+        } else if (value instanceof Type) {
+            cp.newUTF8(((Type) value).getDescriptor());
+        } else if (value instanceof byte[]) {
+            byte[] v = (byte[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newInteger(v[i]);
+            }
+        } else if (value instanceof boolean[]) {
+            boolean[] v = (boolean[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newInteger(v[i] ? 1 : 0);
+            }
+        } else if (value instanceof short[]) {
+            short[] v = (short[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newInteger(v[i]);
+            }
+        } else if (value instanceof char[]) {
+            char[] v = (char[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newInteger(v[i]);
+            }
+        } else if (value instanceof int[]) {
+            int[] v = (int[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newInteger(v[i]);
+            }
+        } else if (value instanceof long[]) {
+            long[] v = (long[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newLong(v[i]);
+            }
+        } else if (value instanceof float[]) {
+            float[] v = (float[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newFloat(v[i]);
+            }
+        } else if (value instanceof double[]) {
+            double[] v = (double[]) value;
+            for (int i = 0; i < v.length; i++) {
+                cp.newDouble(v[i]);
+            }
+        } else {
+            cp.newConst(value);
+        }
+        av.visit(name, value);
+    }
+
+    @Override
+    public void visitEnum(
+        final String name,
+        final String desc,
+        final String value)
+    {
+        if (name != null) {
+            cp.newUTF8(name);
+        }
+        cp.newUTF8(desc);
+        cp.newUTF8(value);
+        av.visitEnum(name, desc, value);
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(
+        final String name,
+        final String desc)
+    {
+        if (name != null) {
+            cp.newUTF8(name);
+        }
+        cp.newUTF8(desc);
+        return new AnnotationConstantsCollector(av.visitAnnotation(name, desc),
+                cp);
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(final String name) {
+        if (name != null) {
+            cp.newUTF8(name);
+        }
+        return new AnnotationConstantsCollector(av.visitArray(name), cp);
+    }
+
+    @Override
+    public void visitEnd() {
+        av.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/ClassConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/ClassConstantsCollector.java
new file mode 100644
index 0000000..9cfc6de
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/ClassConstantsCollector.java
@@ -0,0 +1,212 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A {@link ClassVisitor} that collects the {@link Constant}s of the classes it
+ * visits.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassConstantsCollector extends ClassAdapter {
+
+    private ConstantPool cp;
+
+    public ClassConstantsCollector(final ClassVisitor cv, final ConstantPool cp)
+    {
+        super(cv);
+        this.cp = cp;
+    }
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            cp.newUTF8("Deprecated");
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            cp.newUTF8("Synthetic");
+        }
+        cp.newClass(name);
+        if (signature != null) {
+            cp.newUTF8("Signature");
+            cp.newUTF8(signature);
+        }
+        if (superName != null) {
+            cp.newClass(superName);
+        }
+        if (interfaces != null) {
+            for (int i = 0; i < interfaces.length; ++i) {
+                cp.newClass(interfaces[i]);
+            }
+        }
+        cv.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    public void visitSource(final String source, final String debug) {
+        if (source != null) {
+            cp.newUTF8("SourceFile");
+            cp.newUTF8(source);
+        }
+        if (debug != null) {
+            cp.newUTF8("SourceDebugExtension");
+        }
+        cv.visitSource(source, debug);
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        cp.newUTF8("EnclosingMethod");
+        cp.newClass(owner);
+        if (name != null && desc != null) {
+            cp.newNameType(name, desc);
+        }
+        cv.visitOuterClass(owner, name, desc);
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        cp.newUTF8(desc);
+        if (visible) {
+            cp.newUTF8("RuntimeVisibleAnnotations");
+        } else {
+            cp.newUTF8("RuntimeInvisibleAnnotations");
+        }
+        return new AnnotationConstantsCollector(cv.visitAnnotation(desc,
+                visible), cp);
+    }
+
+    public void visitAttribute(final Attribute attr) {
+        // can do nothing
+        cv.visitAttribute(attr);
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        cp.newUTF8("InnerClasses");
+        if (name != null) {
+            cp.newClass(name);
+        }
+        if (outerName != null) {
+            cp.newClass(outerName);
+        }
+        if (innerName != null) {
+            cp.newClass(innerName);
+        }
+        cv.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            cp.newUTF8("Synthetic");
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            cp.newUTF8("Deprecated");
+        }
+        cp.newUTF8(name);
+        cp.newUTF8(desc);
+        if (signature != null) {
+            cp.newUTF8("Signature");
+            cp.newUTF8(signature);
+        }
+        if (value != null) {
+            cp.newConst(value);
+        }
+        return new FieldConstantsCollector(cv.visitField(access,
+                name,
+                desc,
+                signature,
+                value), cp);
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            cp.newUTF8("Synthetic");
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            cp.newUTF8("Deprecated");
+        }
+        cp.newUTF8(name);
+        cp.newUTF8(desc);
+        if (signature != null) {
+            cp.newUTF8("Signature");
+            cp.newUTF8(signature);
+        }
+        if (exceptions != null) {
+            cp.newUTF8("Exceptions");
+            for (int i = 0; i < exceptions.length; ++i) {
+                cp.newClass(exceptions[i]);
+            }
+        }
+        return new MethodConstantsCollector(cv.visitMethod(access,
+                name,
+                desc,
+                signature,
+                exceptions), cp);
+    }
+
+    public void visitEnd() {
+        cv.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/ClassOptimizer.java b/asmx/src/org/objectweb/asm/optimizer/ClassOptimizer.java
new file mode 100644
index 0000000..153b07a
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/ClassOptimizer.java
@@ -0,0 +1,182 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A {@link ClassAdapter} that renames fields and methods, and removes debug
+ * info.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassOptimizer extends ClassAdapter {
+
+    private NameMapping mapping;
+
+    private String className;
+
+    private String pkgName;
+
+    public ClassOptimizer(final ClassVisitor cv, final NameMapping mapping) {
+        super(cv);
+        this.mapping = mapping;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    // ------------------------------------------------------------------------
+    // Overriden methods
+    // ------------------------------------------------------------------------
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+        className = name;
+        pkgName = name.substring(0, name.lastIndexOf('/'));
+        cv.visit(version,
+                access,
+                mapping.map(name),
+                null,
+                mapping.map(superName),
+                interfaces);
+    }
+
+    public void visitSource(final String source, final String debug) {
+        // remove debug info
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        // remove debug info
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public void visitAttribute(final Attribute attr) {
+        // remove non standard attribute
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        // remove debug info
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        String s = className + "." + name;
+        if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
+            if ((access & Opcodes.ACC_FINAL) != 0
+                    && (access & Opcodes.ACC_STATIC) != 0 && desc.equals("I"))
+            {
+                return null;
+            }
+            if (pkgName.equals("org/objectweb/asm")
+                    && mapping.map(s).equals(name))
+            {
+                System.out.println("INFO: " + s + " could be renamed");
+            }
+            cv.visitField(access,
+                    mapping.map(s),
+                    mapping.fix(desc),
+                    null,
+                    value);
+        } else {
+            if (!mapping.map(s).equals(name)) {
+                throw new RuntimeException("The public or protected field " + s
+                        + " must not be renamed.");
+            }
+            cv.visitField(access, name, desc, null, value);
+        }
+        return null; // remove debug info
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        String s = className + "." + name + desc;
+        if ((access & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED)) == 0) {
+            if (pkgName.equals("org/objectweb/asm") && !name.startsWith("<")
+                    && mapping.map(s).equals(name))
+            {
+                System.out.println("INFO: " + s + " could be renamed");
+            }
+            return new MethodOptimizer(cv.visitMethod(access,
+                    mapping.map(s),
+                    mapping.fix(desc),
+                    null,
+                    exceptions), mapping);
+        } else {
+            if (!mapping.map(s).equals(name)) {
+                throw new RuntimeException("The public or protected method "
+                        + s + " must not be renamed.");
+            }
+            return new MethodOptimizer(cv.visitMethod(access,
+                    name,
+                    desc,
+                    null,
+                    exceptions), mapping);
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/Constant.java b/asmx/src/org/objectweb/asm/optimizer/Constant.java
new file mode 100644
index 0000000..b07b7c2
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/Constant.java
@@ -0,0 +1,265 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.ClassWriter;
+
+/**
+ * A constant pool item.
+ * 
+ * @author Eric Bruneton
+ */
+class Constant {
+
+    /**
+     * 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 I, J, F, D, S, s, C, T, G, M, or N
+     * (for Constant Integer, Long, Float, Double, STR, UTF8, Class, NameType,
+     * Fieldref, Methodref, or InterfaceMethodref constant pool items
+     * respectively).
+     */
+    char type;
+
+    /**
+     * Value of this item, for an integer item.
+     */
+    int intVal;
+
+    /**
+     * Value of this item, for a long item.
+     */
+    long longVal;
+
+    /**
+     * Value of this item, for a float item.
+     */
+    float floatVal;
+
+    /**
+     * Value of this item, for a double item.
+     */
+    double doubleVal;
+
+    /**
+     * 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;
+
+    public Constant() {
+    }
+
+    public Constant(final Constant i) {
+        type = i.type;
+        intVal = i.intVal;
+        longVal = i.longVal;
+        floatVal = i.floatVal;
+        doubleVal = i.doubleVal;
+        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 = 'I';
+        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 = 'J';
+        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 = 'F';
+        this.floatVal = 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 = 'D';
+        this.doubleVal = 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 char 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 's':
+            case 'S':
+            case 'C':
+                hashCode = 0x7FFFFFFF & (type + strVal1.hashCode());
+                return;
+            case 'T':
+                hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+                        * strVal2.hashCode());
+                return;
+            // case 'G':
+            // case 'M':
+            // case 'N':
+            default:
+                hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()
+                        * strVal2.hashCode() * strVal3.hashCode());
+        }
+    }
+
+    void write(final ClassWriter cw) {
+        switch (type) {
+            case 'I':
+                cw.newConst(new Integer(intVal));
+                break;
+            case 'J':
+                cw.newConst(new Long(longVal));
+                break;
+            case 'F':
+                cw.newConst(new Float(floatVal));
+                break;
+            case 'D':
+                cw.newConst(new Double(doubleVal));
+                break;
+            case 'S':
+                cw.newConst(strVal1);
+                break;
+            case 's':
+                cw.newUTF8(strVal1);
+                break;
+            case 'C':
+                cw.newClass(strVal1);
+                break;
+            case 'T':
+                cw.newNameType(strVal1, strVal2);
+                break;
+            case 'G':
+                cw.newField(strVal1, strVal2, strVal3);
+                break;
+            case 'M':
+                cw.newMethod(strVal1, strVal2, strVal3, false);
+                break;
+            case 'N':
+                cw.newMethod(strVal1, strVal2, strVal3, true);
+                break;
+        }
+    }
+
+    public boolean equals(final Object o) {
+        if (!(o instanceof Constant)) {
+            return false;
+        }
+        Constant c = (Constant) o;
+        if (c.type == type) {
+            switch (type) {
+                case 'I':
+                    return c.intVal == intVal;
+                case 'J':
+                    return c.longVal == longVal;
+                case 'F':
+                    return c.floatVal == floatVal;
+                case 'D':
+                    return c.doubleVal == doubleVal;
+                case 's':
+                case 'S':
+                case 'C':
+                    return c.strVal1.equals(strVal1);
+                case 'T':
+                    return c.strVal1.equals(strVal1)
+                            && c.strVal2.equals(strVal2);
+                // case 'G':
+                // case 'M':
+                // case 'N':
+                default:
+                    return c.strVal1.equals(strVal1)
+                            && c.strVal2.equals(strVal2)
+                            && c.strVal3.equals(strVal3);
+            }
+        }
+        return false;
+    }
+
+    public int hashCode() {
+        return hashCode;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/ConstantPool.java b/asmx/src/org/objectweb/asm/optimizer/ConstantPool.java
new file mode 100644
index 0000000..c918bef
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/ConstantPool.java
@@ -0,0 +1,198 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import java.util.HashMap;
+
+import org.objectweb.asm.Type;
+
+/**
+ * A constant pool.
+ * 
+ * @author Eric Bruneton
+ */
+public class ConstantPool extends HashMap {
+
+    private Constant key1 = new Constant();
+
+    private Constant key2 = new Constant();
+
+    private Constant key3 = new Constant();
+
+    public Constant newInteger(final int value) {
+        key1.set(value);
+        Constant result = get(key1);
+        if (result == null) {
+            result = new Constant(key1);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newFloat(final float value) {
+        key1.set(value);
+        Constant result = get(key1);
+        if (result == null) {
+            result = new Constant(key1);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newLong(final long value) {
+        key1.set(value);
+        Constant result = get(key1);
+        if (result == null) {
+            result = new Constant(key1);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newDouble(final double value) {
+        key1.set(value);
+        Constant result = get(key1);
+        if (result == null) {
+            result = new Constant(key1);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newUTF8(final String value) {
+        key1.set('s', value, null, null);
+        Constant result = get(key1);
+        if (result == null) {
+            result = new Constant(key1);
+            put(result);
+        }
+        return result;
+    }
+
+    private Constant newString(final String value) {
+        key2.set('S', value, null, null);
+        Constant result = get(key2);
+        if (result == null) {
+            newUTF8(value);
+            result = new Constant(key2);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newClass(final String value) {
+        key2.set('C', value, null, null);
+        Constant result = get(key2);
+        if (result == null) {
+            newUTF8(value);
+            result = new Constant(key2);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newConst(final Object cst) {
+        if (cst instanceof Integer) {
+            int val = ((Integer) cst).intValue();
+            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;
+            return newClass(t.getSort() == Type.OBJECT
+                    ? t.getInternalName()
+                    : t.getDescriptor());
+        } else {
+            throw new IllegalArgumentException("value " + cst);
+        }
+    }
+
+    public Constant newField(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        key3.set('G', owner, name, desc);
+        Constant result = get(key3);
+        if (result == null) {
+            newClass(owner);
+            newNameType(name, desc);
+            result = new Constant(key3);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newMethod(
+        final String owner,
+        final String name,
+        final String desc,
+        final boolean itf)
+    {
+        key3.set(itf ? 'N' : 'M', owner, name, desc);
+        Constant result = get(key3);
+        if (result == null) {
+            newClass(owner);
+            newNameType(name, desc);
+            result = new Constant(key3);
+            put(result);
+        }
+        return result;
+    }
+
+    public Constant newNameType(final String name, final String desc) {
+        key2.set('T', name, desc, null);
+        Constant result = get(key2);
+        if (result == null) {
+            newUTF8(name);
+            newUTF8(desc);
+            result = new Constant(key2);
+            put(result);
+        }
+        return result;
+    }
+
+    private Constant get(final Constant key) {
+        return (Constant) get((Object) key);
+    }
+
+    private void put(final Constant cst) {
+        put(cst, cst);
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/optimizer/FieldConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/FieldConstantsCollector.java
new file mode 100644
index 0000000..fa86e44
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/FieldConstantsCollector.java
@@ -0,0 +1,92 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+
+/**
+ * A {@link FieldVisitor} that collects the {@link Constant}s of the fields it
+ * visits.
+ * 
+ * @author Eric Bruneton
+ */
+public class FieldConstantsCollector implements FieldVisitor {
+
+    private FieldVisitor fv;
+
+    private ConstantPool cp;
+
+    public FieldConstantsCollector(final FieldVisitor fv, final ConstantPool cp)
+    {
+        this.fv = fv;
+        this.cp = cp;
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        cp.newUTF8(desc);
+        if (visible) {
+            cp.newUTF8("RuntimeVisibleAnnotations");
+        } else {
+            cp.newUTF8("RuntimeInvisibleAnnotations");
+        }
+        return new AnnotationConstantsCollector(
+            fv.visitAnnotation(desc, visible), cp);
+    }
+
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible,
+        final boolean inCode)
+    {
+        cp.newUTF8(desc);
+        if(visible) {
+          cp.newUTF8("RuntimeVisibleTypeAnnotations");
+        } else {
+          cp.newUTF8("RuntimeInvisibleTypeAnnotations");
+        }
+        return new TypeAnnotationConstantsCollector(
+            fv.visitTypeAnnotation(desc, visible, inCode), cp);
+    }
+    
+    public void visitAttribute(final Attribute attr) {
+        // can do nothing
+        fv.visitAttribute(attr);
+    }
+
+    public void visitEnd() {
+        fv.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/JarOptimizer.java b/asmx/src/org/objectweb/asm/optimizer/JarOptimizer.java
new file mode 100644
index 0000000..8302d99
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/JarOptimizer.java
@@ -0,0 +1,87 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * A Jar file optimizer.
+ * 
+ * @author Eric Bruneton
+ */
+public class JarOptimizer {
+
+    public static void main(final String[] args) throws IOException {
+        File f = new File(args[0]);
+        optimize(f);
+    }
+
+    static void optimize(final File f) throws IOException {
+        if (f.isDirectory()) {
+            File[] files = f.listFiles();
+            for (int i = 0; i < files.length; ++i) {
+                optimize(files[i]);
+            }
+        } else if (f.getName().endsWith(".jar")) {
+            File g = new File(f.getParentFile(), f.getName() + ".new");
+            ZipFile zf = new ZipFile(f);
+            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(g));
+            Enumeration e = zf.entries();
+            byte[] buf = new byte[10000];
+            while (e.hasMoreElements()) {
+                ZipEntry ze = (ZipEntry) e.nextElement();
+                if (ze.isDirectory()) {
+                    continue;
+                }
+                out.putNextEntry(ze);
+                InputStream is = zf.getInputStream(ze);
+                int n;
+                do {
+                    n = is.read(buf, 0, buf.length);
+                    if (n != -1) {
+                        out.write(buf, 0, n);
+                    }
+                } while (n != -1);
+                out.closeEntry();
+            }
+            out.close();
+            zf.close();
+            f.delete();
+            g.renameTo(f);
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/MethodConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/MethodConstantsCollector.java
new file mode 100644
index 0000000..dc7b1c1
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/MethodConstantsCollector.java
@@ -0,0 +1,168 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * An {@link MethodVisitor} that collects the {@link Constant}s of the methods
+ * it visits.
+ * 
+ * @author Eric Bruneton
+ */
+public class MethodConstantsCollector extends MethodAdapter {
+
+    private ConstantPool cp;
+
+    public MethodConstantsCollector(
+        final MethodVisitor mv,
+        final ConstantPool cp)
+    {
+        super(mv);
+        this.cp = cp;
+    }
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        cp.newUTF8("AnnotationDefault");
+        return new AnnotationConstantsCollector(mv.visitAnnotationDefault(), cp);
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        cp.newUTF8(desc);
+        if (visible) {
+            cp.newUTF8("RuntimeVisibleAnnotations");
+        } else {
+            cp.newUTF8("RuntimeInvisibleAnnotations");
+        }
+        return new AnnotationConstantsCollector(mv.visitAnnotation(desc,
+                visible), cp);
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        cp.newUTF8(desc);
+        if (visible) {
+            cp.newUTF8("RuntimeVisibleParameterAnnotations");
+        } else {
+            cp.newUTF8("RuntimeInvisibleParameterAnnotations");
+        }
+        return new AnnotationConstantsCollector(mv.visitParameterAnnotation(parameter,
+                desc,
+                visible),
+                cp);
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        cp.newClass(desc);
+        mv.visitTypeInsn(opcode, desc);
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        cp.newField(owner, name, desc);
+        mv.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        boolean itf = opcode == Opcodes.INVOKEINTERFACE;
+        cp.newMethod(owner, name, desc, itf);
+        mv.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        cp.newConst(cst);
+        mv.visitLdcInsn(cst);
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        cp.newClass(desc);
+        mv.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        if (type != null) {
+            cp.newClass(type);
+        }
+        mv.visitTryCatchBlock(start, end, handler, type);
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        if (signature != null) {
+            cp.newUTF8("LocalVariableTypeTable");
+            cp.newUTF8(name);
+            cp.newUTF8(signature);
+        }
+        cp.newUTF8("LocalVariableTable");
+        cp.newUTF8(name);
+        cp.newUTF8(desc);
+        mv.visitLocalVariable(name, desc, signature, start, end, index);
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        cp.newUTF8("LineNumberTable");
+        mv.visitLineNumber(line, start);
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        cp.newUTF8("Code");
+        mv.visitMaxs(maxStack, maxLocals);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/MethodOptimizer.java b/asmx/src/org/objectweb/asm/optimizer/MethodOptimizer.java
new file mode 100644
index 0000000..91fa98b
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/MethodOptimizer.java
@@ -0,0 +1,108 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A {@link MethodAdapter} that renames fields and methods, and removes debug
+ * info.
+ * 
+ * @author Eric Bruneton
+ */
+public class MethodOptimizer extends MethodAdapter {
+
+    private NameMapping mapping;
+
+    public MethodOptimizer(final MethodVisitor mv, final NameMapping mapping) {
+        super(mv);
+        this.mapping = mapping;
+    }
+
+    // ------------------------------------------------------------------------
+    // Overriden methods
+    // ------------------------------------------------------------------------
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        throw new UnsupportedOperationException();
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        mv.visitTypeInsn(opcode, desc.startsWith("[")
+                ? mapping.fix(desc)
+                : mapping.map(desc));
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitFieldInsn(opcode, mapping.map(owner), mapping.map(owner + "."
+                + name), mapping.fix(desc));
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        mv.visitMethodInsn(opcode, mapping.map(owner), mapping.map(owner + "."
+                + name + desc), mapping.fix(desc));
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        // remove debug info
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        // remove debug info
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/NameMapping.java b/asmx/src/org/objectweb/asm/optimizer/NameMapping.java
new file mode 100644
index 0000000..9cefb1f
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/NameMapping.java
@@ -0,0 +1,101 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import org.objectweb.asm.Type;
+
+/**
+ * A mapping from names to names, used to rename classes, fields and methods.
+ * 
+ * @author Eric Bruneton
+ */
+public class NameMapping extends Properties {
+
+    public final Set unused;
+    
+    public NameMapping(final String file) throws IOException {
+        load(new FileInputStream(file));
+        unused = new HashSet(keySet());
+    }
+
+    public String map(final String name) {
+        String s = (String) get(name);
+        if (s == null) {
+            int p = name.indexOf('.');
+            if (p != -1) {
+                int q = name.indexOf('(');
+                if (q != -1) {
+                    s = name.substring(p + 1, q);
+                } else {
+                    s = name.substring(p + 1);
+                }
+            } else {
+                s = name;
+            }
+        } else {
+            unused.remove(name);
+        }
+        return s;
+    }
+
+    public String fix(final String desc) {
+        if (desc.startsWith("(")) {
+            Type[] arguments = Type.getArgumentTypes(desc);
+            Type result = Type.getReturnType(desc);
+            for (int i = 0; i < arguments.length; ++i) {
+                arguments[i] = fix(arguments[i]);
+            }
+            result = fix(result);
+            return Type.getMethodDescriptor(result, arguments);
+        } else {
+            return fix(Type.getType(desc)).getDescriptor();
+        }
+    }
+
+    private Type fix(final Type t) {
+        if (t.getSort() == Type.OBJECT) {
+            return Type.getType("L" + map(t.getInternalName()) + ";");
+        } else if (t.getSort() == Type.ARRAY) {
+            String s = fix(t.getElementType()).getDescriptor();
+            for (int i = 0; i < t.getDimensions(); ++i) {
+                s = "[" + s;
+            }
+            return Type.getType(s);
+        } else {
+            return t;
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/Shrinker.java b/asmx/src/org/objectweb/asm/optimizer/Shrinker.java
new file mode 100644
index 0000000..94e4068
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/Shrinker.java
@@ -0,0 +1,168 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+/**
+ * A class file shrinker utility.
+ * 
+ * @author Eric Bruneton
+ */
+public class Shrinker {
+
+    public static void main(final String[] args) throws IOException {
+        NameMapping mapping = new NameMapping(args[0]);
+        File f = new File(args[1]);
+        File d = new File(args[2]);
+        optimize(f, d, mapping);
+        Iterator i = mapping.unused.iterator();
+        while (i.hasNext()) {
+            System.out.println("INFO: unused mapping " + i.next());
+        }
+    }
+
+    static void optimize(final File f, final File d, final NameMapping mapping)
+            throws IOException
+    {
+        if (f.isDirectory()) {
+            File[] files = f.listFiles();
+            for (int i = 0; i < files.length; ++i) {
+                optimize(files[i], d, mapping);
+            }
+        } else if (f.getName().endsWith(".class")) {
+            ConstantPool cp = new ConstantPool();
+            ClassReader cr = new ClassReader(new FileInputStream(f));
+            ClassWriter cw = new ClassWriter(false);
+            ClassConstantsCollector ccc = new ClassConstantsCollector(cw, cp);
+            ClassOptimizer co = new ClassOptimizer(ccc, mapping);
+            cr.accept(co, true);
+
+            Set constants = new TreeSet(new ConstantComparator());
+            constants.addAll(cp.values());
+
+            cr = new ClassReader(cw.toByteArray());
+            cw = new ClassWriter(false);
+            Iterator i = constants.iterator();
+            while (i.hasNext()) {
+                Constant c = (Constant) i.next();
+                c.write(cw);
+            }
+            cr.accept(cw, true);
+
+            String n = mapping.map(co.getClassName());
+            File g = new File(d, n + ".class");
+            if (!g.exists() || g.lastModified() < f.lastModified()) {
+                g.getParentFile().mkdirs();
+                OutputStream os = new FileOutputStream(g);
+                os.write(cw.toByteArray());
+                os.close();
+            }
+        }
+    }
+
+    static class ConstantComparator implements Comparator {
+
+        public int compare(final Object o1, final Object o2) {
+            Constant c1 = (Constant) o1;
+            Constant c2 = (Constant) o2;
+            int d = getSort(c1) - getSort(c2);
+            if (d == 0) {
+                switch (c1.type) {
+                    case 'I':
+                        return new Integer(c1.intVal).compareTo(new Integer(c2.intVal));
+                    case 'J':
+                        return new Long(c1.longVal).compareTo(new Long(c2.longVal));
+                    case 'F':
+                        return new Float(c1.floatVal).compareTo(new Float(c2.floatVal));
+                    case 'D':
+                        return new Double(c1.doubleVal).compareTo(new Double(c2.doubleVal));
+                    case 's':
+                    case 'S':
+                    case 'C':
+                        return c1.strVal1.compareTo(c2.strVal1);
+                    case 'T':
+                        d = c1.strVal1.compareTo(c2.strVal1);
+                        if (d == 0) {
+                            d = c1.strVal2.compareTo(c2.strVal2);
+                        }
+                        break;
+                    default:
+                        d = c1.strVal1.compareTo(c2.strVal1);
+                        if (d == 0) {
+                            d = c1.strVal2.compareTo(c2.strVal2);
+                            if (d == 0) {
+                                d = c1.strVal3.compareTo(c2.strVal3);
+                            }
+                        }
+                }
+            }
+            return d;
+        }
+
+        private int getSort(Constant c) {
+            switch (c.type) {
+                case 'I':
+                    return 0;
+                case 'J':
+                    return 1;
+                case 'F':
+                    return 2;
+                case 'D':
+                    return 3;
+                case 's':
+                    return 4;
+                case 'S':
+                    return 5;
+                case 'C':
+                    return 6;
+                case 'T':
+                    return 7;
+                case 'G':
+                    return 8;
+                case 'M':
+                    return 9;
+                default:
+                    return 10;
+            }
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/TypeAnnotationConstantsCollector.java b/asmx/src/org/objectweb/asm/optimizer/TypeAnnotationConstantsCollector.java
new file mode 100644
index 0000000..13a6d66
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/TypeAnnotationConstantsCollector.java
@@ -0,0 +1,107 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.optimizer;
+
+import org.objectweb.asm.TypeAnnotationVisitor;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * An {@link TypeAnnotationVisitor} that collects the 
+ * {@link Constant}s of the extended annotations it visits.
+ * 
+ * @author jaimeq
+ */
+public class TypeAnnotationConstantsCollector 
+  extends AnnotationConstantsCollector 
+  implements TypeAnnotationVisitor {
+
+    private TypeAnnotationVisitor xav;
+
+    public TypeAnnotationConstantsCollector(
+        final TypeAnnotationVisitor xav,
+        final ConstantPool cp)
+    {
+        super(xav, cp);
+        this.xav = xav;
+    }
+
+    public void visitXTargetType(int target_type) {
+        xav.visitXTargetType(target_type);
+    }
+
+    public void visitXOffset(int offset) {
+        xav.visitXOffset(offset);
+    }
+
+    public void visitXLocationLength(int location_length) {
+        xav.visitXLocationLength(location_length);
+    }
+
+    public void visitXLocation(TypePathEntry location) {
+        xav.visitXLocation(location);
+    }
+
+    public void visitXNumEntries(int num_entries) {
+        xav.visitXNumEntries(num_entries);
+    }
+
+    public void visitXStartPc(int start_pc) {
+        xav.visitXStartPc(start_pc);
+    }
+
+    public void visitXLength(int length) {
+        xav.visitXLength(length);
+    }
+
+    public void visitXIndex(int index) {
+        xav.visitXIndex(index);
+    }
+
+    public void visitXParamIndex(int param_index) {
+        xav.visitXParamIndex(param_index);
+    }
+
+    public void visitXBoundIndex(int bound_index) {
+        xav.visitXBoundIndex(bound_index);
+    }
+
+    public void visitXTypeIndex(int type_index) {
+        xav.visitXTypeIndex(type_index);
+    }
+
+    public void visitXExceptionIndex(int exception_index) {
+        xav.visitXExceptionIndex(exception_index);
+    }
+
+    public void visitXNameAndArgsSize() {
+        xav.visitXNameAndArgsSize();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/optimizer/shrink.properties b/asmx/src/org/objectweb/asm/optimizer/shrink.properties
new file mode 100644
index 0000000..e076a04
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/optimizer/shrink.properties
@@ -0,0 +1,279 @@
+# class mappings
+
+#org/objectweb/asm/Edge=org/objectweb/asm/a
+#org/objectweb/asm/Item=org/objectweb/asm/b
+#org/objectweb/asm/FieldWriter=org/objectweb/asm/c
+#org/objectweb/asm/MethodWriter=org/objectweb/asm/d
+#org/objectweb/asm/AnnotationWriter=org/objectweb/asm/e
+
+# field mappings
+
+org/objectweb/asm/AnnotationWriter.cw=a
+org/objectweb/asm/AnnotationWriter.size=b
+org/objectweb/asm/AnnotationWriter.named=c
+org/objectweb/asm/AnnotationWriter.bv=d
+org/objectweb/asm/AnnotationWriter.parent=e
+org/objectweb/asm/AnnotationWriter.offset=f
+org/objectweb/asm/AnnotationWriter.next=g
+org/objectweb/asm/AnnotationWriter.prev=h
+
+org/objectweb/asm/Attribute.next=a
+org/objectweb/asm/Attribute.value=b
+
+org/objectweb/asm/ByteVector.data=a
+org/objectweb/asm/ByteVector.length=b
+
+org/objectweb/asm/ClassReader.items=a
+org/objectweb/asm/ClassReader.strings=c
+org/objectweb/asm/ClassReader.maxStringLength=d
+#org/objectweb/asm/ClassReader.header=e
+
+org/objectweb/asm/ClassWriter.TYPE=a
+org/objectweb/asm/ClassWriter.version=b
+org/objectweb/asm/ClassWriter.index=c
+org/objectweb/asm/ClassWriter.pool=d
+org/objectweb/asm/ClassWriter.items=e
+org/objectweb/asm/ClassWriter.threshold=f
+org/objectweb/asm/ClassWriter.key=g
+org/objectweb/asm/ClassWriter.key2=h
+org/objectweb/asm/ClassWriter.key3=i
+org/objectweb/asm/ClassWriter.access=j
+org/objectweb/asm/ClassWriter.name=k
+org/objectweb/asm/ClassWriter.signature=l
+org/objectweb/asm/ClassWriter.superName=m
+org/objectweb/asm/ClassWriter.interfaceCount=n
+org/objectweb/asm/ClassWriter.interfaces=o
+org/objectweb/asm/ClassWriter.sourceFile=p
+org/objectweb/asm/ClassWriter.sourceDebug=q
+org/objectweb/asm/ClassWriter.enclosingMethodOwner=r
+org/objectweb/asm/ClassWriter.enclosingMethod=s
+org/objectweb/asm/ClassWriter.anns=t
+org/objectweb/asm/ClassWriter.ianns=u
+org/objectweb/asm/ClassWriter.attrs=v
+org/objectweb/asm/ClassWriter.innerClassesCount=w
+org/objectweb/asm/ClassWriter.innerClasses=x
+org/objectweb/asm/ClassWriter.firstField=y
+org/objectweb/asm/ClassWriter.lastField=z
+org/objectweb/asm/ClassWriter.firstMethod=A
+org/objectweb/asm/ClassWriter.lastMethod=B
+org/objectweb/asm/ClassWriter.computeMaxs=C
+org/objectweb/asm/ClassWriter.typeCount=D
+org/objectweb/asm/ClassWriter.typeTable=E
+org/objectweb/asm/ClassWriter.thisName=F
+org/objectweb/asm/ClassWriter.computeFrames=G
+org/objectweb/asm/ClassWriter.computeMaxs=H
+org/objectweb/asm/ClassWriter.invalidFrames=I
+org/objectweb/asm/ClassWriter.cr=J
+    
+org/objectweb/asm/Edge.info=a
+org/objectweb/asm/Edge.successor=b
+org/objectweb/asm/Edge.next=c
+
+org/objectweb/asm/Handler.start=a
+org/objectweb/asm/Handler.end=b
+org/objectweb/asm/Handler.handler=c
+org/objectweb/asm/Handler.desc=d
+org/objectweb/asm/Handler.type=e
+org/objectweb/asm/Handler.next=f
+
+org/objectweb/asm/FieldWriter.next=a
+org/objectweb/asm/FieldWriter.cw=b
+org/objectweb/asm/FieldWriter.access=c
+org/objectweb/asm/FieldWriter.name=d
+org/objectweb/asm/FieldWriter.desc=e
+org/objectweb/asm/FieldWriter.signature=f
+org/objectweb/asm/FieldWriter.value=g
+org/objectweb/asm/FieldWriter.anns=h
+org/objectweb/asm/FieldWriter.ianns=i
+org/objectweb/asm/FieldWriter.attrs=j
+
+org/objectweb/asm/Item.index=a
+org/objectweb/asm/Item.type=b
+org/objectweb/asm/Item.intVal=c
+org/objectweb/asm/Item.longVal=d
+org/objectweb/asm/Item.strVal1=g
+org/objectweb/asm/Item.strVal2=h
+org/objectweb/asm/Item.strVal3=i
+org/objectweb/asm/Item.hashCode=j
+org/objectweb/asm/Item.next=k
+
+org/objectweb/asm/Label.status=a
+org/objectweb/asm/Label.line=b
+org/objectweb/asm/Label.position=c
+org/objectweb/asm/Label.referenceCount=d
+org/objectweb/asm/Label.srcAndRefPositions=e
+org/objectweb/asm/Label.inputStackTop=f
+org/objectweb/asm/Label.outputStackMax=g
+org/objectweb/asm/Label.frame=h
+org/objectweb/asm/Label.successor=i
+org/objectweb/asm/Label.successors=j
+org/objectweb/asm/Label.next=k
+
+org/objectweb/asm/Frame.SIZE=a
+org/objectweb/asm/Frame.owner=b
+org/objectweb/asm/Frame.inputLocals=c
+org/objectweb/asm/Frame.inputStack=d
+org/objectweb/asm/Frame.outputLocals=e
+org/objectweb/asm/Frame.outputStack=f
+org/objectweb/asm/Frame.outputStackTop=g
+org/objectweb/asm/Frame.initializationCount=h
+org/objectweb/asm/Frame.initializations=i
+
+org/objectweb/asm/MethodWriter.next=a
+org/objectweb/asm/MethodWriter.cw=b
+org/objectweb/asm/MethodWriter.access=c
+org/objectweb/asm/MethodWriter.name=d
+org/objectweb/asm/MethodWriter.desc=e
+org/objectweb/asm/MethodWriter.descriptor=f
+org/objectweb/asm/MethodWriter.signature=g
+org/objectweb/asm/MethodWriter.classReaderOffset=h
+org/objectweb/asm/MethodWriter.classReaderLength=i
+org/objectweb/asm/MethodWriter.exceptionCount=j
+org/objectweb/asm/MethodWriter.exceptions=k
+org/objectweb/asm/MethodWriter.annd=l
+org/objectweb/asm/MethodWriter.anns=m
+org/objectweb/asm/MethodWriter.ianns=n
+org/objectweb/asm/MethodWriter.panns=o
+org/objectweb/asm/MethodWriter.ipanns=p
+org/objectweb/asm/MethodWriter.attrs=q
+org/objectweb/asm/MethodWriter.code=r
+org/objectweb/asm/MethodWriter.maxStack=s
+org/objectweb/asm/MethodWriter.maxLocals=t
+org/objectweb/asm/MethodWriter.frameCount=u
+org/objectweb/asm/MethodWriter.stackMap=v
+org/objectweb/asm/MethodWriter.previousFrameOffset=w
+org/objectweb/asm/MethodWriter.previousFrame=x
+org/objectweb/asm/MethodWriter.frameIndex=y
+org/objectweb/asm/MethodWriter.frame=z
+org/objectweb/asm/MethodWriter.handlerCount=A
+org/objectweb/asm/MethodWriter.firstHandler=B
+org/objectweb/asm/MethodWriter.lastHandler=C
+org/objectweb/asm/MethodWriter.localVarCount=D
+org/objectweb/asm/MethodWriter.localVar=E
+org/objectweb/asm/MethodWriter.localVarTypeCount=F
+org/objectweb/asm/MethodWriter.localVarType=G
+org/objectweb/asm/MethodWriter.lineNumberCount=H
+org/objectweb/asm/MethodWriter.lineNumber=I
+org/objectweb/asm/MethodWriter.cattrs=J
+org/objectweb/asm/MethodWriter.resize=K
+org/objectweb/asm/MethodWriter.jsr=L
+org/objectweb/asm/MethodWriter.compute=M
+org/objectweb/asm/MethodWriter.labels=N
+org/objectweb/asm/MethodWriter.previousBlock=O
+org/objectweb/asm/MethodWriter.currentBlock=P
+org/objectweb/asm/MethodWriter.stackSize=Q
+org/objectweb/asm/MethodWriter.maxStackSize=R
+
+org/objectweb/asm/Type.sort=a
+org/objectweb/asm/Type.buf=b
+org/objectweb/asm/Type.off=c
+org/objectweb/asm/Type.len=d
+
+org/objectweb/asm/signature/SignatureReader.signature=a
+
+org/objectweb/asm/signature/SignatureWriter.buf=a
+org/objectweb/asm/signature/SignatureWriter.hasFormals=b
+org/objectweb/asm/signature/SignatureWriter.hasParameters=c
+org/objectweb/asm/signature/SignatureWriter.argumentStack=d
+
+# method mappings
+
+org/objectweb/asm/AnnotationWriter.getSize()I=a
+org/objectweb/asm/AnnotationWriter.put([Lorg/objectweb/asm/AnnotationWriter;Lorg/objectweb/asm/ByteVector;)V=a
+org/objectweb/asm/AnnotationWriter.put(Lorg/objectweb/asm/ByteVector;)V=a
+
+org/objectweb/asm/Attribute.getCount()I=a
+org/objectweb/asm/Attribute.getSize(Lorg/objectweb/asm/ClassWriter;[BIII)I=a
+org/objectweb/asm/Attribute.put(Lorg/objectweb/asm/ClassWriter;[BIIILorg/objectweb/asm/ByteVector;)V=a
+
+org/objectweb/asm/ByteVector.enlarge(I)V=a
+org/objectweb/asm/ByteVector.put11(II)Lorg/objectweb/asm/ByteVector;=a
+org/objectweb/asm/ByteVector.put12(II)Lorg/objectweb/asm/ByteVector;=b
+
+org/objectweb/asm/ClassReader.copyPool(Lorg/objectweb/asm/ClassWriter;)V=a
+org/objectweb/asm/ClassReader.readAnnotationValue(I[CLjava/lang/String;Lorg/objectweb/asm/AnnotationVisitor;)I=a
+org/objectweb/asm/ClassReader.readAnnotationValues(I[CLorg/objectweb/asm/AnnotationVisitor;)I=a
+org/objectweb/asm/ClassReader.readAttribute([Lorg/objectweb/asm/Attribute;Ljava/lang/String;II[CI[Lorg/objectweb/asm/Label;)Lorg/objectweb/asm/Attribute;=a
+org/objectweb/asm/ClassReader.readClass(Ljava/io/InputStream;)[B=a
+org/objectweb/asm/ClassReader.readParameterAnnotations(I[CZLorg/objectweb/asm/MethodVisitor;)V=a
+org/objectweb/asm/ClassReader.readUTF(II[C)Ljava/lang/String;=a
+org/objectweb/asm/ClassReader.readFrameType([Ljava/lang/Object;II[C[Lorg/objectweb/asm/Label;)I=a
+
+org/objectweb/asm/ClassWriter.get(Lorg/objectweb/asm/Item;)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newClassItem(Ljava/lang/String;)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newConstItem(Ljava/lang/Object;)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newDouble(D)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newFloat(F)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newInteger(I)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newLong(J)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newMethodItem(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.newString(Ljava/lang/String;)Lorg/objectweb/asm/Item;=b
+org/objectweb/asm/ClassWriter.put122(III)V=a
+org/objectweb/asm/ClassWriter.put(Lorg/objectweb/asm/Item;)V=b
+org/objectweb/asm/ClassWriter.newFieldItem(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Lorg/objectweb/asm/Item;=a
+org/objectweb/asm/ClassWriter.addType(Ljava/lang/String;)I=c
+org/objectweb/asm/ClassWriter.addUninitializedType(Ljava/lang/String;I)I=a
+org/objectweb/asm/ClassWriter.addType(Lorg/objectweb/asm/Item;)Lorg/objectweb/asm/Item;=c
+org/objectweb/asm/ClassWriter.getMergedType(II)I=a
+
+org/objectweb/asm/FieldWriter.getSize()I=a
+org/objectweb/asm/FieldWriter.put(Lorg/objectweb/asm/ByteVector;)V=a
+
+org/objectweb/asm/Item.isEqualTo(Lorg/objectweb/asm/Item;)Z=a
+org/objectweb/asm/Item.set(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V=a
+org/objectweb/asm/Item.set(D)V=a
+org/objectweb/asm/Item.set(F)V=a
+org/objectweb/asm/Item.set(I)V=a
+org/objectweb/asm/Item.set(J)V=a
+
+org/objectweb/asm/Label.addReference(II)V=a
+org/objectweb/asm/Label.put(Lorg/objectweb/asm/MethodWriter;Lorg/objectweb/asm/ByteVector;IZ)V=a
+org/objectweb/asm/Label.resolve(Lorg/objectweb/asm/MethodWriter;I[B)Z=a
+org/objectweb/asm/Label.getFirst()Lorg/objectweb/asm/Label;=a
+
+org/objectweb/asm/Frame.get(I)I=a
+org/objectweb/asm/Frame.set(II)V=a
+org/objectweb/asm/Frame.push(I)V=b
+org/objectweb/asm/Frame.push(Lorg/objectweb/asm/ClassWriter;Ljava/lang/String;)V=a
+org/objectweb/asm/Frame.type(Lorg/objectweb/asm/ClassWriter;Ljava/lang/String;)I=b
+org/objectweb/asm/Frame.pop()I=a
+org/objectweb/asm/Frame.pop(Ljava/lang/String;)V=a
+org/objectweb/asm/Frame.pop(I)V=c
+org/objectweb/asm/Frame.init(I)V=d
+org/objectweb/asm/Frame.init(Lorg/objectweb/asm/ClassWriter;I)I=a
+org/objectweb/asm/Frame.initInputFrame(Lorg/objectweb/asm/ClassWriter;I[Lorg/objectweb/asm/Type;I)V=a
+org/objectweb/asm/Frame.execute(IILorg/objectweb/asm/ClassWriter;Lorg/objectweb/asm/Item;)V=a
+org/objectweb/asm/Frame.merge(Lorg/objectweb/asm/ClassWriter;Lorg/objectweb/asm/Frame;I)Z=a
+org/objectweb/asm/Frame.merge(Lorg/objectweb/asm/ClassWriter;I[II)Z=a
+
+org/objectweb/asm/MethodWriter.visitSwitchInsn(Lorg/objectweb/asm/Label;[Lorg/objectweb/asm/Label;)V=a
+org/objectweb/asm/MethodWriter.addSuccessor(ILorg/objectweb/asm/Label;)V=a
+org/objectweb/asm/MethodWriter.getArgumentsAndReturnSizes(Ljava/lang/String;)I=a
+org/objectweb/asm/MethodWriter.getNewOffset([I[III)I=a
+org/objectweb/asm/MethodWriter.getSize()I=a
+org/objectweb/asm/MethodWriter.put(Lorg/objectweb/asm/ByteVector;)V=a
+org/objectweb/asm/MethodWriter.readInt([BI)I=a
+org/objectweb/asm/MethodWriter.readShort([BI)S=b
+org/objectweb/asm/MethodWriter.readUnsignedShort([BI)I=c
+org/objectweb/asm/MethodWriter.writeShort([BII)V=a
+org/objectweb/asm/MethodWriter.visitFrame(Lorg/objectweb/asm/Frame;)V=b
+org/objectweb/asm/MethodWriter.startFrame(III)V=a
+org/objectweb/asm/MethodWriter.endFrame()V=b
+org/objectweb/asm/MethodWriter.writeFrame()V=c
+org/objectweb/asm/MethodWriter.resizeInstructions()V=d
+org/objectweb/asm/MethodWriter.noSuccessor()V=e
+org/objectweb/asm/MethodWriter.writeFrameTypes(II)V=a
+org/objectweb/asm/MethodWriter.writeFrameType(Ljava/lang/Object;)V=a
+org/objectweb/asm/MethodWriter.getNewOffset([I[ILorg/objectweb/asm/Label;)V=a
+org/objectweb/asm/MethodWriter.findSubroutine(Lorg/objectweb/asm/Label;I)V=a
+org/objectweb/asm/MethodWriter.findSubroutineSuccessors(I[Lorg/objectweb/asm/Label;I)V=a
+
+org/objectweb/asm/Type.getType([CI)Lorg/objectweb/asm/Type;=a
+org/objectweb/asm/Type.getDescriptor(Ljava/lang/StringBuffer;)V=a
+org/objectweb/asm/Type.getDescriptor(Ljava/lang/StringBuffer;Ljava/lang/Class;)V=a
+
+org/objectweb/asm/signature/SignatureReader.parseType(Ljava/lang/String;ILorg/objectweb/asm/signature/SignatureVisitor;)I=a
+
+org/objectweb/asm/signature/SignatureWriter.endFormals()V=a
+org/objectweb/asm/signature/SignatureWriter.endArguments()V=b
+     
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/signature/SignatureReader.java b/asmx/src/org/objectweb/asm/signature/SignatureReader.java
new file mode 100644
index 0000000..363692d
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/signature/SignatureReader.java
@@ -0,0 +1,233 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.signature;
+
+/**
+ * A type signature parser to make a signature visitor visit an existing
+ * signature.
+ * 
+ * @author Thomas Hallgren
+ * @author Eric Bruneton
+ */
+public class SignatureReader {
+
+    /**
+     * The signature to be read.
+     */
+    private final String signature;
+
+    /**
+     * Constructs a {@link SignatureReader} for the given signature.
+     * 
+     * @param signature A <i>ClassSignature</i>, <i>MethodTypeSignature</i>,
+     *        or <i>FieldTypeSignature</i>.
+     */
+    public SignatureReader(final String signature) {
+        this.signature = signature;
+    }
+
+    /**
+     * Makes the given visitor visit the signature of this
+     * {@link SignatureReader}. This signature is the one specified in the
+     * constructor (see {@link #SignatureReader(String) SignatureReader}). This
+     * method is intended to be called on a {@link SignatureReader} that was
+     * created using a <i>ClassSignature</i> (such as the
+     * <code>signature</code> parameter of the
+     * {@link org.objectweb.asm.ClassVisitor#visit ClassVisitor.visit} method)
+     * or a <i>MethodTypeSignature</i> (such as the <code>signature</code>
+     * parameter of the
+     * {@link org.objectweb.asm.ClassVisitor#visitMethod ClassVisitor.visitMethod}
+     * method).
+     * 
+     * @param v the visitor that must visit this signature.
+     */
+    public void accept(final SignatureVisitor v) {
+        String signature = this.signature;
+        int len = signature.length();
+        int pos;
+        char c;
+
+        if (signature.charAt(0) == '<') {
+            pos = 2;
+            do {
+                int end = signature.indexOf(':', pos);
+                v.visitFormalTypeParameter(signature.substring(pos - 1, end));
+                pos = end + 1;
+
+                c = signature.charAt(pos);
+                if (c == 'L' || c == '[' || c == 'T') {
+                    pos = parseType(signature, pos, v.visitClassBound());
+                }
+
+                for (;;) {
+                    if ((c = signature.charAt(pos++)) == ':') {
+                        pos = parseType(signature, pos, v.visitInterfaceBound());
+                    } else {
+                        break;
+                    }
+                }
+            } while (c != '>');
+        } else {
+            pos = 0;
+        }
+
+        if (signature.charAt(pos) == '(') {
+            pos = pos + 1;
+            while (signature.charAt(pos) != ')') {
+                pos = parseType(signature, pos, v.visitParameterType());
+            }
+            pos = parseType(signature, pos + 1, v.visitReturnType());
+            while (pos < len) {
+                pos = parseType(signature, pos + 1, v.visitExceptionType());
+            }
+        } else {
+            pos = parseType(signature, pos, v.visitSuperclass());
+            while (pos < len) {
+                pos = parseType(signature, pos, v.visitInterface());
+            }
+        }
+    }
+
+    /**
+     * Makes the given visitor visit the signature of this
+     * {@link SignatureReader}. This signature is the one specified in the
+     * constructor (see {@link #SignatureReader(String) SignatureReader}). This
+     * method is intended to be called on a {@link SignatureReader} that was
+     * created using a <i>FieldTypeSignature</i>, such as the
+     * <code>signature</code> parameter of the
+     * {@link org.objectweb.asm.ClassVisitor#visitField 
+     * ClassVisitor.visitField} or {@link 
+     * org.objectweb.asm.MethodVisitor#visitLocalVariable
+     * MethodVisitor.visitLocalVariable} methods.
+     * 
+     * @param v the visitor that must visit this signature.
+     */
+    public void acceptType(final SignatureVisitor v) {
+        parseType(this.signature, 0, v);
+    }
+
+    /**
+     * Parses a field type signature and makes the given visitor visit it.
+     * 
+     * @param signature a string containing the signature that must be parsed.
+     * @param pos index of the first character of the signature to parsed.
+     * @param v the visitor that must visit this signature.
+     * @return the index of the first character after the parsed signature.
+     */
+    private static int parseType(
+        final String signature,
+        int pos,
+        final SignatureVisitor v)
+    {
+        char c;
+        int start, end;
+        boolean visited, inner;
+        String name;
+
+        switch (c = signature.charAt(pos++)) {
+            case 'Z':
+            case 'C':
+            case 'B':
+            case 'S':
+            case 'I':
+            case 'F':
+            case 'J':
+            case 'D':
+            case 'V':
+                v.visitBaseType(c);
+                return pos;
+
+            case '[':
+                return parseType(signature, pos, v.visitArrayType());
+
+            case 'T':
+                end = signature.indexOf(';', pos);
+                v.visitTypeVariable(signature.substring(pos, end));
+                return end + 1;
+
+            default: // case 'L':
+                start = pos;
+                visited = false;
+                inner = false;
+                for (;;) {
+                    switch (c = signature.charAt(pos++)) {
+                        case '.':
+                        case ';':
+                            if (!visited) {
+                                name = signature.substring(start, pos - 1);
+                                if (inner) {
+                                    v.visitInnerClassType(name);
+                                } else {
+                                    v.visitClassType(name);
+                                }
+                            }
+                            if (c == ';') {
+                                v.visitEnd();
+                                return pos;
+                            }
+                            start = pos;
+                            visited = false;
+                            inner = true;
+                            break;
+
+                        case '<':
+                            name = signature.substring(start, pos - 1);
+                            if (inner) {
+                                v.visitInnerClassType(name);
+                            } else {
+                                v.visitClassType(name);
+                            }
+                            visited = true;
+                            top: for (;;) {
+                                switch (c = signature.charAt(pos)) {
+                                    case '>':
+                                        break top;
+                                    case '*':
+                                        ++pos;
+                                        v.visitTypeArgument();
+                                        break;
+                                    case '+':
+                                    case '-':
+                                        pos = parseType(signature,
+                                                pos + 1,
+                                                v.visitTypeArgument(c));
+                                        break;
+                                    default:
+                                        pos = parseType(signature,
+                                                pos,
+                                                v.visitTypeArgument('='));
+                                        break;
+                                }
+                            }
+                    }
+                }
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/signature/SignatureVisitor.java b/asmx/src/org/objectweb/asm/signature/SignatureVisitor.java
new file mode 100644
index 0000000..8f087bd
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/signature/SignatureVisitor.java
@@ -0,0 +1,185 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.signature;
+
+/**
+ * A visitor to visit a generic signature. The methods of this interface must be
+ * called in one of the three following orders (the last one is the only valid
+ * order for a {@link SignatureVisitor} that is returned by a method of this
+ * interface): <ul> <li><i>ClassSignature</i> = (
+ * <tt>visitFormalTypeParameter</tt> 
+ *   <tt>visitClassBound</tt>?
+ * <tt>visitInterfaceBound</tt>* )* ( <tt>visitSuperClass</tt> 
+ *   <tt>visitInterface</tt>* )</li>
+ * <li><i>MethodSignature</i> = ( <tt>visitFormalTypeParameter</tt> 
+ *   <tt>visitClassBound</tt>?
+ * <tt>visitInterfaceBound</tt>* )* ( <tt>visitParameterType</tt>*
+ * <tt>visitReturnType</tt> 
+ *   <tt>visitExceptionType</tt>* )</li> <li><i>TypeSignature</i> =
+ * <tt>visitBaseType</tt> | <tt>visitTypeVariable</tt> |
+ * <tt>visitArrayType</tt> | (
+ * <tt>visitClassType</tt> <tt>visitTypeArgument</tt>* (
+ * <tt>visitInnerClassType</tt> <tt>visitTypeArgument</tt>* )*
+ * <tt>visitEnd</tt> ) )</li> </ul>
+ * 
+ * @author Thomas Hallgren
+ * @author Eric Bruneton
+ */
+public interface SignatureVisitor {
+
+    /**
+     * Wildcard for an "extends" type argument.
+     */
+    char EXTENDS = '+';
+
+    /**
+     * Wildcard for a "super" type argument.
+     */
+    char SUPER = '-';
+
+    /**
+     * Wildcard for a normal type argument.
+     */
+    char INSTANCEOF = '=';
+
+    /**
+     * Visits a formal type parameter.
+     * 
+     * @param name the name of the formal parameter.
+     */
+    void visitFormalTypeParameter(String name);
+
+    /**
+     * Visits the class bound of the last visited formal type parameter.
+     * 
+     * @return a non null visitor to visit the signature of the class bound.
+     */
+    SignatureVisitor visitClassBound();
+
+    /**
+     * Visits an interface bound of the last visited formal type parameter.
+     * 
+     * @return a non null visitor to visit the signature of the interface bound.
+     */
+    SignatureVisitor visitInterfaceBound();
+
+    /**
+     * Visits the type of the super class.
+     * 
+     * @return a non null visitor to visit the signature of the super class
+     *         type.
+     */
+    SignatureVisitor visitSuperclass();
+
+    /**
+     * Visits the type of an interface implemented by the class.
+     * 
+     * @return a non null visitor to visit the signature of the interface type.
+     */
+    SignatureVisitor visitInterface();
+
+    /**
+     * Visits the type of a method parameter.
+     * 
+     * @return a non null visitor to visit the signature of the parameter type.
+     */
+    SignatureVisitor visitParameterType();
+
+    /**
+     * Visits the return type of the method.
+     * 
+     * @return a non null visitor to visit the signature of the return type.
+     */
+    SignatureVisitor visitReturnType();
+
+    /**
+     * Visits the type of a method exception.
+     * 
+     * @return a non null visitor to visit the signature of the exception type.
+     */
+    SignatureVisitor visitExceptionType();
+
+    /**
+     * Visits a signature corresponding to a primitive type.
+     * 
+     * @param descriptor the descriptor of the primitive type, or 'V' for
+     *        <tt>void</tt>.
+     */
+    void visitBaseType(char descriptor);
+
+    /**
+     * Visits a signature corresponding to a type variable.
+     * 
+     * @param name the name of the type variable.
+     */
+    void visitTypeVariable(String name);
+
+    /**
+     * Visits a signature corresponding to an array type.
+     * 
+     * @return a non null visitor to visit the signature of the array element
+     *         type.
+     */
+    SignatureVisitor visitArrayType();
+
+    /**
+     * Starts the visit of a signature corresponding to a class or interface
+     * type.
+     * 
+     * @param name the internal name of the class or interface.
+     */
+    void visitClassType(String name);
+
+    /**
+     * Visits an inner class.
+     * 
+     * @param name the local name of the inner class in its enclosing class.
+     */
+    void visitInnerClassType(String name);
+
+    /**
+     * Visits an unbounded type argument of the last visited class or inner
+     * class type.
+     */
+    void visitTypeArgument();
+
+    /**
+     * Visits a type argument of the last visited class or inner class type.
+     * 
+     * @param wildcard '+', '-' or '='.
+     * @return a non null visitor to visit the signature of the type argument.
+     */
+    SignatureVisitor visitTypeArgument(char wildcard);
+
+    /**
+     * Ends the visit of a signature corresponding to a class or interface type.
+     */
+    void visitEnd();
+}
diff --git a/asmx/src/org/objectweb/asm/signature/SignatureWriter.java b/asmx/src/org/objectweb/asm/signature/SignatureWriter.java
new file mode 100644
index 0000000..b78d666
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/signature/SignatureWriter.java
@@ -0,0 +1,207 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.signature;
+
+/**
+ * A signature visitor that generates signatures in string format.
+ * 
+ * @author Thomas Hallgren
+ * @author Eric Bruneton
+ */
+public class SignatureWriter implements SignatureVisitor {
+
+    /**
+     * Buffer used to construct the signature.
+     */
+    private final StringBuffer buf = new StringBuffer();
+
+    /**
+     * Indicates if the signature contains formal type parameters.
+     */
+    private boolean hasFormals;
+
+    /**
+     * Indicates if the signature contains method parameter types.
+     */
+    private boolean hasParameters;
+
+    /**
+     * Stack used to keep track of class types that have arguments. Each element
+     * of this stack is a boolean encoded in one bit. The top of the stack is
+     * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
+     * /2.
+     */
+    private int argumentStack;
+
+    /**
+     * Constructs a new {@link SignatureWriter} object.
+     */
+    public SignatureWriter() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the SignatureVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visitFormalTypeParameter(String name) {
+        if (!hasFormals) {
+            hasFormals = true;
+            buf.append('<');
+        }
+        buf.append(name);
+        buf.append(':');
+    }
+
+    public SignatureVisitor visitClassBound() {
+        return this;
+    }
+
+    public SignatureVisitor visitInterfaceBound() {
+        buf.append(':');
+        return this;
+    }
+
+    public SignatureVisitor visitSuperclass() {
+        endFormals();
+        return this;
+    }
+
+    public SignatureVisitor visitInterface() {
+        return this;
+    }
+
+    public SignatureVisitor visitParameterType() {
+        endFormals();
+        if (!hasParameters) {
+            hasParameters = true;
+            buf.append('(');
+        }
+        return this;
+    }
+
+    public SignatureVisitor visitReturnType() {
+        endFormals();
+        if (!hasParameters) {
+            buf.append('(');
+        }
+        buf.append(')');
+        return this;
+    }
+
+    public SignatureVisitor visitExceptionType() {
+        buf.append('^');
+        return this;
+    }
+
+    public void visitBaseType(char descriptor) {
+        buf.append(descriptor);
+    }
+
+    public void visitTypeVariable(String name) {
+        buf.append('T');
+        buf.append(name);
+        buf.append(';');
+    }
+
+    public SignatureVisitor visitArrayType() {
+        buf.append('[');
+        return this;
+    }
+
+    public void visitClassType(String name) {
+        buf.append('L');
+        buf.append(name);
+        argumentStack *= 2;
+    }
+
+    public void visitInnerClassType(String name) {
+        endArguments();
+        buf.append('.');
+        buf.append(name);
+        argumentStack *= 2;
+    }
+
+    public void visitTypeArgument() {
+        if (argumentStack % 2 == 0) {
+            ++argumentStack;
+            buf.append('<');
+        }
+        buf.append('*');
+    }
+
+    public SignatureVisitor visitTypeArgument(char wildcard) {
+        if (argumentStack % 2 == 0) {
+            ++argumentStack;
+            buf.append('<');
+        }
+        if (wildcard != '=') {
+            buf.append(wildcard);
+        }
+        return this;
+    }
+
+    public void visitEnd() {
+        endArguments();
+        buf.append(';');
+    }
+
+    /**
+     * Returns the signature that was built by this signature writer.
+     * 
+     * @return the signature that was built by this signature writer.
+     */
+    public String toString() {
+        return buf.toString();
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Ends the formal type parameters section of the signature.
+     */
+    private void endFormals() {
+        if (hasFormals) {
+            hasFormals = false;
+            buf.append('>');
+        }
+    }
+
+    /**
+     * Ends the type arguments of a class or inner class type.
+     */
+    private void endArguments() {
+        if (argumentStack % 2 == 1) {
+            buf.append('>');
+        }
+        argumentStack /= 2;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/AbstractInsnNode.java b/asmx/src/org/objectweb/asm/tree/AbstractInsnNode.java
new file mode 100644
index 0000000..5a03eab
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/AbstractInsnNode.java
@@ -0,0 +1,206 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import java.util.List;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a bytecode instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public abstract class AbstractInsnNode {
+
+    /**
+     * The type of {@link InsnNode} instructions.
+     */
+    public static final int INSN = 0;
+
+    /**
+     * The type of {@link IntInsnNode} instructions.
+     */
+    public static final int INT_INSN = 1;
+
+    /**
+     * The type of {@link VarInsnNode} instructions.
+     */
+    public static final int VAR_INSN = 2;
+
+    /**
+     * The type of {@link TypeInsnNode} instructions.
+     */
+    public static final int TYPE_INSN = 3;
+
+    /**
+     * The type of {@link FieldInsnNode} instructions.
+     */
+    public static final int FIELD_INSN = 4;
+
+    /**
+     * The type of {@link MethodInsnNode} instructions.
+     */
+    public static final int METHOD_INSN = 5;
+
+    /**
+     * The type of {@link InvokeDynamicInsnNode} instructions.
+     */
+    public static final int INVOKE_DYNAMIC_INSN = 6;
+
+    /**
+     * The type of {@link JumpInsnNode} instructions.
+     */
+    public static final int JUMP_INSN = 7;
+
+    /**
+     * The type of {@link LabelNode} "instructions".
+     */
+    public static final int LABEL = 8;
+
+    /**
+     * The type of {@link LdcInsnNode} instructions.
+     */
+    public static final int LDC_INSN = 9;
+
+    /**
+     * The type of {@link IincInsnNode} instructions.
+     */
+    public static final int IINC_INSN = 10;
+
+    /**
+     * The type of {@link TableSwitchInsnNode} instructions.
+     */
+    public static final int TABLESWITCH_INSN = 11;
+
+    /**
+     * The type of {@link LookupSwitchInsnNode} instructions.
+     */
+    public static final int LOOKUPSWITCH_INSN = 12;
+
+    /**
+     * The type of {@link MultiANewArrayInsnNode} instructions.
+     */
+    public static final int MULTIANEWARRAY_INSN = 13;
+
+    /**
+     * The type of {@link FrameNode} "instructions".
+     */
+    public static final int FRAME = 14;
+
+    /**
+     * The type of {@link LineNumberNode} "instructions".
+     */
+    public static final int LINE = 15;
+
+    /**
+     * The opcode of this instruction.
+     */
+    protected int opcode;
+
+    /**
+     * The runtime visible type annotations of this instruction. This field is
+     * only used for real instructions (i.e. not for labels, frames, or line
+     * number nodes). This list is a list of {@link TypeAnnotationNode} objects.
+     * May be <tt>null</tt>.
+     * 
+     * @associates org.objectweb.asm.tree.TypeAnnotationNode
+     * @label visible
+     */
+    public List<TypeAnnotationNode> visibleTypeAnnotations;
+
+    /**
+     * The runtime invisible type annotations of this instruction. This field is
+     * only used for real instructions (i.e. not for labels, frames, or line
+     * number nodes). This list is a list of {@link TypeAnnotationNode} objects.
+     * May be <tt>null</tt>.
+     * 
+     * @associates org.objectweb.asm.tree.TypeAnnotationNode
+     * @label invisible
+     */
+    public List<TypeAnnotationNode> invisibleTypeAnnotations;
+
+    /**
+     * Constructs a new {@link AbstractInsnNode}.
+     * 
+     * @param opcode the opcode of the instruction to be constructed.
+     */
+    protected AbstractInsnNode(final int opcode) {
+        this.opcode = opcode;
+    }
+
+    /**
+     * Returns the opcode of this instruction.
+     * 
+     * @return the opcode of this instruction.
+     */
+    public int getOpcode() {
+        return opcode;
+    }
+
+    /**
+     * Returns the type of this instruction.
+     * 
+     * @return the type of this instruction, i.e. one the constants defined in
+     *         this class.
+     */
+    public abstract int getType();
+
+    /**
+     * Makes the given code visitor visit this instruction.
+     * 
+     * @param cv a code visitor.
+     */
+    public abstract void accept(final MethodVisitor cv);
+
+    /**
+     * Makes the given visitor visit the annotations of this instruction.
+     * 
+     * @param mv
+     *            a method visitor.
+     */
+    protected final void acceptAnnotations(final MethodVisitor mv) {
+        // TODO: needed?
+        //int n = visibleTypeAnnotations == null ? 0 : visibleTypeAnnotations
+        //        .size();
+        //for (int i = 0; i < n; ++i) {
+        //    TypeAnnotationNode an = visibleTypeAnnotations.get(i);
+        //    an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc,
+        //            true));
+        //}
+        //n = invisibleTypeAnnotations == null ? 0 : invisibleTypeAnnotations
+        //        .size();
+        //for (int i = 0; i < n; ++i) {
+        //    TypeAnnotationNode an = invisibleTypeAnnotations.get(i);
+        //    an.accept(mv.visitInsnAnnotation(an.typeRef, an.typePath, an.desc,
+        //            false));
+        //}
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/AnnotationNode.java b/asmx/src/org/objectweb/asm/tree/AnnotationNode.java
new file mode 100644
index 0000000..4911e3e
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/AnnotationNode.java
@@ -0,0 +1,187 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.AnnotationVisitor;
+
+/**
+ * A node that represents an annotationn.
+ * 
+ * @author Eric Bruneton
+ */
+public class AnnotationNode implements AnnotationVisitor {
+
+    /**
+     * The class descriptor of the annotation class.
+     */
+    public String desc;
+
+    /**
+     * The name value pairs of this annotation. Each name value pair is stored
+     * as two consecutive elements in the list. The name is a {@link String},
+     * and the value may be a {@link Byte}, {@link Boolean}, {@link Character},
+     * {@link Short}, {@link Integer}, {@link Long}, {@link Float},
+     * {@link Double}, {@link String} or {@link org.objectweb.asm.Type}, or an
+     * two elements String array (for enumeration values), a
+     * {@link AnnotationNode}, or a {@link List} of values of one of the
+     * preceding types. The list may be <tt>null</tt> if there is no name
+     * value pair.
+     */
+    public List values;
+
+    /**
+     * Constructs a new {@link AnnotationNode}.
+     * 
+     * @param desc the class descriptor of the annotation class.
+     */
+    public AnnotationNode(final String desc) {
+        this.desc = desc;
+    }
+
+    /**
+     * Constructs a new {@link AnnotationNode} to visit an array value.
+     * 
+     * @param values where the visited values must be stored.
+     */
+    AnnotationNode(final List values) {
+        this.values = values;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the AnnotationVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visit(final String name, final Object value) {
+        if (values == null) {
+            values = new ArrayList(this.desc != null ? 2 : 1);
+        }
+        if (this.desc != null) {
+            values.add(name);
+        }
+        values.add(value);
+    }
+
+    public void visitEnum(
+        final String name,
+        final String desc,
+        final String value)
+    {
+        if (values == null) {
+            values = new ArrayList(this.desc != null ? 2 : 1);
+        }
+        if (this.desc != null) {
+            values.add(name);
+        }
+        values.add(new String[] { desc, value });
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String name,
+        final String desc)
+    {
+        if (values == null) {
+            values = new ArrayList(this.desc != null ? 2 : 1);
+        }
+        if (this.desc != null) {
+            values.add(name);
+        }
+        AnnotationNode annotation = new AnnotationNode(desc);
+        values.add(annotation);
+        return annotation;
+    }
+
+    public AnnotationVisitor visitArray(final String name) {
+        if (values == null) {
+            values = new ArrayList(this.desc != null ? 2 : 1);
+        }
+        if (this.desc != null) {
+            values.add(name);
+        }
+        List array = new ArrayList();
+        values.add(array);
+        return new AnnotationNode(array);
+    }
+
+    public void visitEnd() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Accept methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Makes the given visitor visit this annotation.
+     * 
+     * @param av an annotation visitor.
+     */
+    public void accept(final AnnotationVisitor av) {
+        if (values != null) {
+            for (int i = 0; i < values.size(); i += 2) {
+                String name = (String) values.get(i);
+                Object value = values.get(i + 1);
+                accept(av, name, value);
+            }
+        }
+        av.visitEnd();
+    }
+
+    /**
+     * Makes the given visitor visit a given annotation value.
+     * 
+     * @param av an annotation visitor.
+     * @param name the value name.
+     * @param value the actual value.
+     */
+    static void accept(
+        final AnnotationVisitor av,
+        final String name,
+        final Object value)
+    {
+        if (value instanceof String[]) {
+            String[] typeconst = (String[]) value;
+            av.visitEnum(name, typeconst[0], typeconst[1]);
+        } else if (value instanceof AnnotationNode) {
+            AnnotationNode an = (AnnotationNode) value;
+            an.accept(av.visitAnnotation(name, an.desc));
+        } else if (value instanceof List) {
+            AnnotationVisitor v = av.visitArray(name);
+            List array = (List) value;
+            for (int j = 0; j < array.size(); ++j) {
+                accept(v, null, array.get(j));
+            }
+            v.visitEnd();
+        } else {
+            av.visit(name, value);
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/ClassNode.java b/asmx/src/org/objectweb/asm/tree/ClassNode.java
new file mode 100644
index 0000000..d21745b
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/ClassNode.java
@@ -0,0 +1,289 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a class.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassNode extends MemberNode implements ClassVisitor {
+
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        String desc, boolean visible, boolean inCode) {
+        throw new RuntimeException("Jaime did not implement yet");
+    }
+
+    /**
+     * The class version.
+     */
+    public int version;
+
+    /**
+     * The class's access flags (see {@link org.objectweb.asm.Opcodes}). This
+     * field also indicates if the class is deprecated.
+     */
+    public int access;
+
+    /**
+     * The internal name of the class (see
+     * {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     */
+    public String name;
+
+    /**
+     * The signature of the class. Mayt be <tt>null</tt>.
+     */
+    public String signature;
+
+    /**
+     * The internal of name of the super class (see
+     * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). For
+     * interfaces, the super class is {@link Object}. May be <tt>null</tt>,
+     * but only for the {@link Object} class.
+     */
+    public String superName;
+
+    /**
+     * The internal names of the class's interfaces (see
+     * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). This
+     * list is a list of {@link String} objects.
+     */
+    public List interfaces;
+
+    /**
+     * The name of the source file from which this class was compiled. May be
+     * <tt>null</tt>.
+     */
+    public String sourceFile;
+
+    /**
+     * Debug information to compute the correspondance between source and
+     * compiled elements of the class. May be <tt>null</tt>.
+     */
+    public String sourceDebug;
+
+    /**
+     * The internal name of the enclosing class of the class. May be
+     * <tt>null</tt>.
+     */
+    public String outerClass;
+
+    /**
+     * The name of the method that contains the class, or <tt>null</tt> if the
+     * class is not enclosed in a method.
+     */
+    public String outerMethod;
+
+    /**
+     * The descriptor of the method that contains the class, or <tt>null</tt>
+     * if the class is not enclosed in a method.
+     */
+    public String outerMethodDesc;
+
+    /**
+     * Informations about the inner classes of this class. This list is a list
+     * of {@link InnerClassNode} objects.
+     * 
+     * @associates org.objectweb.asm.tree.InnerClassNode
+     */
+    public List innerClasses;
+
+    /**
+     * The fields of this class. This list is a list of {@link FieldNode}
+     * objects.
+     * 
+     * @associates org.objectweb.asm.tree.FieldNode
+     */
+    public List fields;
+
+    /**
+     * The methods of this class. This list is a list of {@link MethodNode}
+     * objects.
+     * 
+     * @associates org.objectweb.asm.tree.MethodNode
+     */
+    public List methods;
+
+    /**
+     * Constructs a new {@link ClassNode}.
+     */
+    public ClassNode() {
+        this.interfaces = new ArrayList();
+        this.innerClasses = new ArrayList();
+        this.fields = new ArrayList();
+        this.methods = new ArrayList();
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the ClassVisitor interface
+    // ------------------------------------------------------------------------
+
+    public 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 = name;
+        this.signature = signature;
+        this.superName = superName;
+        if (interfaces != null) {
+            this.interfaces.addAll(Arrays.asList(interfaces));
+        }
+    }
+
+    public void visitSource(final String file, final String debug) {
+        sourceFile = file;
+        sourceDebug = debug;
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        outerClass = owner;
+        outerMethod = name;
+        outerMethodDesc = desc;
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        InnerClassNode icn = new InnerClassNode(name,
+                outerName,
+                innerName,
+                access);
+        innerClasses.add(icn);
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        FieldNode fn = new FieldNode(access, name, desc, signature, value);
+        fields.add(fn);
+        return fn;
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        MethodNode mn = new MethodNode(access,
+                name,
+                desc,
+                signature,
+                exceptions);
+        methods.add(mn);
+        return mn;
+    }
+
+    public void visitEnd() {
+    }
+
+    // ------------------------------------------------------------------------
+    // Accept method
+    // ------------------------------------------------------------------------
+
+    /**
+     * Makes the given class visitor visit this class.
+     * 
+     * @param cv a class visitor.
+     */
+    public void accept(final ClassVisitor cv) {
+        // visits header
+        String[] interfaces = new String[this.interfaces.size()];
+        this.interfaces.toArray(interfaces);
+        cv.visit(version, access, name, signature, superName, interfaces);
+        // visits source
+        if (sourceFile != null || sourceDebug != null) {
+            cv.visitSource(sourceFile, sourceDebug);
+        }
+        // visits outer class
+        if (outerClass != null) {
+            cv.visitOuterClass(outerClass, outerMethod, outerMethodDesc);
+        }
+        // visits attributes
+        int i, n;
+        n = visibleAnnotations == null ? 0 : visibleAnnotations.size();
+        for (i = 0; i < n; ++i) {
+            AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i);
+            an.accept(cv.visitAnnotation(an.desc, true));
+        }
+        n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size();
+        for (i = 0; i < n; ++i) {
+            AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i);
+            an.accept(cv.visitAnnotation(an.desc, false));
+        }
+        n = attrs == null ? 0 : attrs.size();
+        for (i = 0; i < n; ++i) {
+            cv.visitAttribute((Attribute) attrs.get(i));
+        }
+        // visits inner classes
+        for (i = 0; i < innerClasses.size(); ++i) {
+            ((InnerClassNode) innerClasses.get(i)).accept(cv);
+        }
+        // visits fields
+        for (i = 0; i < fields.size(); ++i) {
+            ((FieldNode) fields.get(i)).accept(cv);
+        }
+        // visits methods
+        for (i = 0; i < methods.size(); ++i) {
+            ((MethodNode) methods.get(i)).accept(cv);
+        }
+        // visits end
+        cv.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/FieldInsnNode.java b/asmx/src/org/objectweb/asm/tree/FieldInsnNode.java
new file mode 100644
index 0000000..4399e3a
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/FieldInsnNode.java
@@ -0,0 +1,97 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a field instruction. A field instruction is an
+ * instruction that loads or stores the value of a field of an object.
+ * 
+ * @author Eric Bruneton
+ */
+public class FieldInsnNode extends AbstractInsnNode {
+
+    /**
+     * The internal name of the field's owner class (see
+     * {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     */
+    public String owner;
+
+    /**
+     * The field's name.
+     */
+    public String name;
+
+    /**
+     * The field's descriptor (see {@link org.objectweb.asm.Type}).
+     */
+    public String desc;
+
+    /**
+     * Constructs a new {@link FieldInsnNode}.
+     * 
+     * @param opcode the opcode of the type instruction to be constructed. This
+     *        opcode must be GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD.
+     * @param owner the internal name of the field's owner class (see
+     *        {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     * @param name the field's name.
+     * @param desc the field's descriptor (see {@link org.objectweb.asm.Type}).
+     */
+    public FieldInsnNode(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        super(opcode);
+        this.owner = owner;
+        this.name = name;
+        this.desc = desc;
+    }
+
+    /**
+     * Sets the opcode of this instruction.
+     * 
+     * @param opcode the new instruction opcode. This opcode must be GETSTATIC,
+     *        PUTSTATIC, GETFIELD or PUTFIELD.
+     */
+    public void setOpcode(final int opcode) {
+        this.opcode = opcode;
+    }
+
+    public void accept(final MethodVisitor cv) {
+        cv.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    public int getType() {
+        return FIELD_INSN;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/FieldNode.java b/asmx/src/org/objectweb/asm/tree/FieldNode.java
new file mode 100644
index 0000000..af69382
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/FieldNode.java
@@ -0,0 +1,133 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+
+/**
+ * A node that represents a field.
+ * 
+ * @author Eric Bruneton
+ */
+public class FieldNode extends MemberNode implements FieldVisitor {
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+        boolean visible,
+        boolean inCode)
+    {
+        throw new RuntimeException("Jaime did not implement yet");
+    }
+    //end jaime
+
+    /**
+     * The field's access flags (see {@link org.objectweb.asm.Opcodes}). This
+     * field also indicates if the field is synthetic and/or deprecated.
+     */
+    public int access;
+
+    /**
+     * The field's name.
+     */
+    public String name;
+
+    /**
+     * The field's descriptor (see {@link org.objectweb.asm.Type}).
+     */
+    public String desc;
+
+    /**
+     * The field's signature. May be <tt>null</tt>.
+     */
+    public String signature;
+
+    /**
+     * The field's initial value. This field, 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}.
+     */
+    public Object value;
+
+    /**
+     * Constructs a new {@link FieldNode}.
+     * 
+     * @param access the field's access flags (see
+     *        {@link org.objectweb.asm.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 org.objectweb.asm.Type}).
+     * @param signature the field's signature.
+     * @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}.
+     */
+    public FieldNode(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        this.access = access;
+        this.name = name;
+        this.desc = desc;
+        this.signature = signature;
+        this.value = value;
+    }
+
+    /**
+     * Makes the given class visitor visit this field.
+     * 
+     * @param cv a class visitor.
+     */
+    public void accept(final ClassVisitor cv) {
+        FieldVisitor fv = cv.visitField(access, name, desc, signature, value);
+        int i, n;
+        n = visibleAnnotations == null ? 0 : visibleAnnotations.size();
+        for (i = 0; i < n; ++i) {
+            AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i);
+            an.accept(fv.visitAnnotation(an.desc, true));
+        }
+        n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size();
+        for (i = 0; i < n; ++i) {
+            AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i);
+            an.accept(fv.visitAnnotation(an.desc, false));
+        }
+        n = attrs == null ? 0 : attrs.size();
+        for (i = 0; i < n; ++i) {
+            fv.visitAttribute((Attribute) attrs.get(i));
+        }
+        fv.visitEnd();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/IincInsnNode.java b/asmx/src/org/objectweb/asm/tree/IincInsnNode.java
new file mode 100644
index 0000000..e7e79dc
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/IincInsnNode.java
@@ -0,0 +1,71 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A node that represents an IINC instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public class IincInsnNode extends AbstractInsnNode {
+
+    /**
+     * Index of the local variable to be incremented.
+     */
+    public int var;
+
+    /**
+     * Amount to increment the local variable by.
+     */
+    public int incr;
+
+    /**
+     * Constructs a new {@link IincInsnNode}.
+     * 
+     * @param var index of the local variable to be incremented.
+     * @param incr increment amount to increment the local variable by.
+     */
+    public IincInsnNode(final int var, final int incr) {
+        super(Opcodes.IINC);
+        this.var = var;
+        this.incr = incr;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitIincInsn(var, incr);
+    }
+
+    public int getType() {
+        return IINC_INSN;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/InnerClassNode.java b/asmx/src/org/objectweb/asm/tree/InnerClassNode.java
new file mode 100644
index 0000000..a325317
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/InnerClassNode.java
@@ -0,0 +1,101 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.ClassVisitor;
+
+/**
+ * A node that represents an inner class.
+ * 
+ * @author Eric Bruneton
+ */
+public class InnerClassNode {
+
+    /**
+     * The internal name of an inner class (see
+     * {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     */
+    public String name;
+
+    /**
+     * The internal name of the class to which the inner class belongs (see
+     * {@link org.objectweb.asm.Type#getInternalName() getInternalName}). May
+     * be <tt>null</tt>.
+     */
+    public String outerName;
+
+    /**
+     * The (simple) name of the inner class inside its enclosing class. May be
+     * <tt>null</tt> for anonymous inner classes.
+     */
+    public String innerName;
+
+    /**
+     * The access flags of the inner class as originally declared in the
+     * enclosing class.
+     */
+    public int access;
+
+    /**
+     * Constructs a new {@link InnerClassNode}.
+     * 
+     * @param name the internal name of an inner class (see
+     *        {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     * @param outerName the internal name of the class to which the inner class
+     *        belongs (see
+     *        {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     *        May be <tt>null</tt>.
+     * @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 InnerClassNode(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        this.name = name;
+        this.outerName = outerName;
+        this.innerName = innerName;
+        this.access = access;
+    }
+
+    /**
+     * Makes the given class visitor visit this inner class.
+     * 
+     * @param cv a class visitor.
+     */
+    public void accept(final ClassVisitor cv) {
+        cv.visitInnerClass(name, outerName, innerName, access);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/InsnNode.java b/asmx/src/org/objectweb/asm/tree/InsnNode.java
new file mode 100644
index 0000000..434dd5b
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/InsnNode.java
@@ -0,0 +1,96 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a zero operand instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public class InsnNode extends AbstractInsnNode {
+
+    private final static InsnNode[] INSNS;
+
+    static {
+        INSNS = new InsnNode[255];
+        for (int i = 0; i < INSNS.length; ++i) {
+            INSNS[i] = new InsnNode(i);
+        }
+    }
+
+    /**
+     * Returns the {@link InsnNode} corresponding to the given opcode.
+     * 
+     * @deprecated uses the constructor instead.
+     * 
+     * @param opcode an instruction opcode.
+     * @return the {@link InsnNode} corresponding to the given opcode.
+     */
+    public final static InsnNode getByOpcode(final int opcode) {
+        return INSNS[opcode];
+    }
+
+    /**
+     * Constructs a new {@link InsnNode}.
+     * 
+     * @param opcode the opcode of the instruction to be constructed. This
+     *        opcode must be 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 InsnNode(final int opcode) {
+        super(opcode);
+    }
+
+    /**
+     * Makes the given visitor visit this instruction.
+     * 
+     * @param mv a method visitor.
+     */
+    public void accept(final MethodVisitor mv) {
+        mv.visitInsn(opcode);
+    }
+
+    public int getType() {
+        return INSN;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/IntInsnNode.java b/asmx/src/org/objectweb/asm/tree/IntInsnNode.java
new file mode 100644
index 0000000..2c200a5
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/IntInsnNode.java
@@ -0,0 +1,75 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents an instruction with a single int operand.
+ * 
+ * @author Eric Bruneton
+ */
+public class IntInsnNode extends AbstractInsnNode {
+
+    /**
+     * The operand of this instruction.
+     */
+    public int operand;
+
+    /**
+     * Constructs a new {@link IntInsnNode}.
+     * 
+     * @param opcode the opcode of the instruction to be constructed. This
+     *        opcode must be BIPUSH, SIPUSH or NEWARRAY.
+     * @param operand the operand of the instruction to be constructed.
+     */
+    public IntInsnNode(final int opcode, final int operand) {
+        super(opcode);
+        this.operand = operand;
+    }
+
+    /**
+     * Sets the opcode of this instruction.
+     * 
+     * @param opcode the new instruction opcode. This opcode must be BIPUSH,
+     *        SIPUSH or NEWARRAY.
+     */
+    public void setOpcode(final int opcode) {
+        this.opcode = opcode;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitIntInsn(opcode, operand);
+    }
+
+    public int getType() {
+        return INT_INSN;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/InvokeDynamicInsnNode.java b/asmx/src/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
new file mode 100644
index 0000000..c1f7a3f
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/InvokeDynamicInsnNode.java
@@ -0,0 +1,63 @@
+package org.objectweb.asm.tree;
+
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A node that represents an INVOKEDYNAMIC instruction.
+ */
+public class InvokeDynamicInsnNode extends AbstractInsnNode {
+
+    /**
+     * Invokedynamic name.
+     */
+    public String name;
+
+    /**
+     * Invokedynamic descriptor.
+     */
+    public String desc;
+
+    /**
+     * Bootstrap method
+     */
+    public Handle bsm;
+
+    /**
+     * Bootstrap constant arguments
+     */
+    public Object[] bsmArgs;
+
+    /**
+     * Constructs a new {@link InvokeDynamicInsnNode}.
+     * 
+     * @param name
+     *            invokedynamic name.
+     * @param desc
+     *            invokedynamic descriptor (see {@link org.objectweb.asm.Type}).
+     * @param bsm
+     *            the bootstrap method.
+     * @param bsmArgs
+     *            the boostrap constant arguments.
+     */
+    public InvokeDynamicInsnNode(final String name, final String desc,
+            final Handle bsm, final Object... bsmArgs) {
+        super(Opcodes.INVOKEDYNAMIC);
+        this.name = name;
+        this.desc = desc;
+        this.bsm = bsm;
+        this.bsmArgs = bsmArgs;
+    }
+
+    @Override
+    public int getType() {
+        return INVOKE_DYNAMIC_INSN;
+    }
+
+    @Override
+    public void accept(final MethodVisitor mv) {
+        mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+        acceptAnnotations(mv);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/JumpInsnNode.java b/asmx/src/org/objectweb/asm/tree/JumpInsnNode.java
new file mode 100644
index 0000000..5ab6e11
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/JumpInsnNode.java
@@ -0,0 +1,84 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a jump instruction. A jump instruction is an
+ * instruction that may jump to another instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public class JumpInsnNode extends AbstractInsnNode {
+
+    /**
+     * The operand of this instruction. This operand is a label that designates
+     * the instruction to which this instruction may jump.
+     */
+    public Label label;
+
+    /**
+     * Constructs a new {@link JumpInsnNode}.
+     * 
+     * @param opcode the opcode of the type instruction to be constructed. This
+     *        opcode must be 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 constructed. This
+     *        operand is a label that designates the instruction to which the
+     *        jump instruction may jump.
+     */
+    public JumpInsnNode(final int opcode, final Label label) {
+        super(opcode);
+        this.label = label;
+    }
+
+    /**
+     * Sets the opcode of this instruction.
+     * 
+     * @param opcode the new instruction opcode. This opcode must be 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.
+     */
+    public void setOpcode(final int opcode) {
+        this.opcode = opcode;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitJumpInsn(opcode, label);
+    }
+
+    public int getType() {
+        return JUMP_INSN;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/LabelNode.java b/asmx/src/org/objectweb/asm/tree/LabelNode.java
new file mode 100644
index 0000000..5eead6e
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/LabelNode.java
@@ -0,0 +1,54 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * An {@link AbstractInsnNode} that encapsulates a {@link Label}.
+ */
+public class LabelNode extends AbstractInsnNode {
+
+    public Label label;
+
+    public LabelNode(final Label label) {
+        super(-1);
+        this.label = label;
+    }
+
+    public void accept(final MethodVisitor cv) {
+        cv.visitLabel(label);
+    }
+
+    public int getType() {
+        return LABEL;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/LdcInsnNode.java b/asmx/src/org/objectweb/asm/tree/LdcInsnNode.java
new file mode 100644
index 0000000..ef95513
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/LdcInsnNode.java
@@ -0,0 +1,68 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * A node that represents an LDC instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public class LdcInsnNode extends AbstractInsnNode {
+
+    /**
+     * 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} or a {@link org.objectweb.asm.Type}.
+     */
+    public Object cst;
+
+    /**
+     * Constructs a new {@link LdcInsnNode}.
+     * 
+     * @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} or a {@link String}.
+     */
+    public LdcInsnNode(final Object cst) {
+        super(Opcodes.LDC);
+        this.cst = cst;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitLdcInsn(cst);
+    }
+
+    public int getType() {
+        return LDC_INSN;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/LineNumberNode.java b/asmx/src/org/objectweb/asm/tree/LineNumberNode.java
new file mode 100644
index 0000000..0d49747
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/LineNumberNode.java
@@ -0,0 +1,73 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a line number declaration.
+ * 
+ * @author Eric Bruneton
+ */
+public class LineNumberNode {
+
+    /**
+     * A line number. This number refers to the source file from which the class
+     * was compiled.
+     */
+    public int line;
+
+    /**
+     * The first instruction corresponding to this line number.
+     */
+    public Label start;
+
+    /**
+     * Constructs a new {@link LineNumberNode}.
+     * 
+     * @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.
+     */
+    public LineNumberNode(final int line, final Label start) {
+        this.line = line;
+        this.start = start;
+    }
+
+    /**
+     * Makes the given visitor visit this line number declaration.
+     * 
+     * @param mv a method visitor.
+     */
+    public void accept(final MethodVisitor mv) {
+        mv.visitLineNumber(line, start);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/LocalVariableNode.java b/asmx/src/org/objectweb/asm/tree/LocalVariableNode.java
new file mode 100644
index 0000000..b9efe44
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/LocalVariableNode.java
@@ -0,0 +1,111 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Label;
+
+/**
+ * A node that represents a local variable declaration.
+ * 
+ * @author Eric Bruneton
+ */
+public class LocalVariableNode {
+
+    /**
+     * The name of a local variable.
+     */
+    public String name;
+
+    /**
+     * The type descriptor of this local variable.
+     */
+    public String desc;
+
+    /**
+     * The signature of this local variable. May be <tt>null</tt>.
+     */
+    public String signature;
+
+    /**
+     * The first instruction corresponding to the scope of this local variable
+     * (inclusive).
+     */
+    public Label start;
+
+    /**
+     * The last instruction corresponding to the scope of this local variable
+     * (exclusive).
+     */
+    public Label end;
+
+    /**
+     * The local variable's index.
+     */
+    public int index;
+
+    /**
+     * Constructs a new {@link LocalVariableNode}.
+     * 
+     * @param name the name of a local variable.
+     * @param desc the type descriptor of this local variable.
+     * @param signature the signature of this local variable. May be
+     *        <tt>null</tt>.
+     * @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.
+     */
+    public LocalVariableNode(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        this.name = name;
+        this.desc = desc;
+        this.signature = signature;
+        this.start = start;
+        this.end = end;
+        this.index = index;
+    }
+
+    /**
+     * Makes the given visitor visit this local variable declaration.
+     * 
+     * @param mv a method visitor.
+     */
+    public void accept(final MethodVisitor mv) {
+        mv.visitLocalVariable(name, desc, signature, start, end, index);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/LookupSwitchInsnNode.java b/asmx/src/org/objectweb/asm/tree/LookupSwitchInsnNode.java
new file mode 100644
index 0000000..1c8e69f
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/LookupSwitchInsnNode.java
@@ -0,0 +1,103 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * A node that represents a LOOKUPSWITCH instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public class LookupSwitchInsnNode extends AbstractInsnNode {
+
+    /**
+     * Beginning of the default handler block.
+     */
+    public Label dflt;
+
+    /**
+     * The values of the keys. This list is a list of {@link Integer} objects.
+     */
+    public List keys;
+
+    /**
+     * Beginnings of the handler blocks. This list is a list of {@link Label}
+     * objects.
+     */
+    public List labels;
+
+    /**
+     * Constructs a new {@link LookupSwitchInsnNode}.
+     * 
+     * @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 LookupSwitchInsnNode(
+        final Label dflt,
+        final int[] keys,
+        final Label[] labels)
+    {
+        super(Opcodes.LOOKUPSWITCH);
+        this.dflt = dflt;
+        this.keys = new ArrayList(keys == null ? 0 : keys.length);
+        this.labels = new ArrayList(labels == null ? 0 : labels.length);
+        if (keys != null) {
+            for (int i = 0; i < keys.length; ++i) {
+                this.keys.add(new Integer(keys[i]));
+            }
+        }
+        if (labels != null) {
+            this.labels.addAll(Arrays.asList(labels));
+        }
+    }
+
+    public void accept(final MethodVisitor mv) {
+        int[] keys = new int[this.keys.size()];
+        for (int i = 0; i < keys.length; ++i) {
+            keys[i] = ((Integer) this.keys.get(i)).intValue();
+        }
+        Label[] labels = new Label[this.labels.size()];
+        this.labels.toArray(labels);
+        mv.visitLookupSwitchInsn(dflt, keys, labels);
+    }
+
+    public int getType() {
+        return LOOKUPSWITCH_INSN;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/MemberNode.java b/asmx/src/org/objectweb/asm/tree/MemberNode.java
new file mode 100644
index 0000000..86b1c5c
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/MemberNode.java
@@ -0,0 +1,120 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+
+/**
+ * An abstract class, field or method node.
+ * 
+ * @author Eric Bruneton
+ */
+public abstract class MemberNode {
+
+    /**
+     * The runtime visible annotations of this class, field or method. This list
+     * is a list of {@link AnnotationNode} objects. May be <tt>null</tt>.
+     * 
+     * @associates org.objectweb.asm.tree.AnnotationNode
+     * @label visible
+     */
+    public List visibleAnnotations;
+
+    /**
+     * The runtime invisible annotations of this class, field or method. This
+     * list is a list of {@link AnnotationNode} objects. May be <tt>null</tt>.
+     * 
+     * @associates org.objectweb.asm.tree.AnnotationNode
+     * @label invisible
+     */
+    public List invisibleAnnotations;
+
+    /**
+     * The non standard attributes of this class, field or method. This list is
+     * a list of {@link Attribute} objects. May be <tt>null</tt>.
+     * 
+     * @associates org.objectweb.asm.Attribute
+     */
+    public List attrs;
+
+    /**
+     * Constructs a new {@link MemberNode}.
+     */
+    public MemberNode() {
+    }
+
+    /**
+     * Visits an annotation of this class, field or 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.
+     */
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        AnnotationNode an = new AnnotationNode(desc);
+        if (visible) {
+            if (visibleAnnotations == null) {
+                visibleAnnotations = new ArrayList(1);
+            }
+            visibleAnnotations.add(an);
+        } else {
+            if (invisibleAnnotations == null) {
+                invisibleAnnotations = new ArrayList(1);
+            }
+            invisibleAnnotations.add(an);
+        }
+        return an;
+    }
+
+    /**
+     * Visits a non standard attribute of this class, field or method.
+     * 
+     * @param attr an attribute.
+     */
+    public void visitAttribute(final Attribute attr) {
+        if (attrs == null) {
+            attrs = new ArrayList(1);
+        }
+        attrs.add(attr);
+    }
+
+    /**
+     * Visits the end of this class, field or method.
+     */
+    public void visitEnd() {
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/MethodInsnNode.java b/asmx/src/org/objectweb/asm/tree/MethodInsnNode.java
new file mode 100644
index 0000000..714aabe
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/MethodInsnNode.java
@@ -0,0 +1,98 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a method instruction. A method instruction is an
+ * instruction that invokes a method.
+ * 
+ * @author Eric Bruneton
+ */
+public class MethodInsnNode extends AbstractInsnNode {
+
+    /**
+     * The internal name of the method's owner class (see
+     * {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     */
+    public String owner;
+
+    /**
+     * The method's name.
+     */
+    public String name;
+
+    /**
+     * The method's descriptor (see {@link org.objectweb.asm.Type}).
+     */
+    public String desc;
+
+    /**
+     * Constructs a new {@link MethodInsnNode}.
+     * 
+     * @param opcode the opcode of the type instruction to be constructed. This
+     *        opcode must be INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or
+     *        INVOKEINTERFACE.
+     * @param owner the internal name of the method's owner class (see
+     *        {@link org.objectweb.asm.Type#getInternalName() getInternalName}).
+     * @param name the method's name.
+     * @param desc the method's descriptor (see {@link org.objectweb.asm.Type}).
+     */
+    public MethodInsnNode(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        super(opcode);
+        this.owner = owner;
+        this.name = name;
+        this.desc = desc;
+    }
+
+    /**
+     * Sets the opcode of this instruction.
+     * 
+     * @param opcode the new instruction opcode. This opcode must be
+     *        INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or INVOKEINTERFACE.
+     */
+    public void setOpcode(final int opcode) {
+        this.opcode = opcode;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    public int getType() {
+        return METHOD_INSN;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/MethodNode.java b/asmx/src/org/objectweb/asm/tree/MethodNode.java
new file mode 100644
index 0000000..4711717
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/MethodNode.java
@@ -0,0 +1,468 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+
+/**
+ * A node that represents a method.
+ * 
+ * @author Eric Bruneton
+ */
+public class MethodNode extends MemberNode implements MethodVisitor {
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, 
+        boolean visible,
+        boolean inCode)
+    {
+        return null;
+    }
+    //end jaime
+
+    /**
+     * The method's access flags (see {@link Opcodes}). This field also
+     * indicates if the method is synthetic and/or deprecated.
+     */
+    public int access;
+
+    /**
+     * The method's name.
+     */
+    public String name;
+
+    /**
+     * The method's descriptor (see {@link Type}).
+     */
+    public String desc;
+
+    /**
+     * The method's signature. May be <tt>null</tt>.
+     */
+    public String signature;
+
+    /**
+     * The internal names of the method's exception classes (see
+     * {@link Type#getInternalName() getInternalName}). This list is a list of
+     * {@link String} objects.
+     */
+    public List exceptions;
+
+    /**
+     * The default value of this annotation interface method. This field must be
+     * a {@link Byte}, {@link Boolean}, {@link Character}, {@link Short},
+     * {@link Integer}, {@link Long}, {@link Float}, {@link Double},
+     * {@link String} or {@link Type}, or an two elements String array (for
+     * enumeration values), a {@link AnnotationNode}, or a {@link List} of
+     * values of one of the preceding types. May be <tt>null</tt>.
+     */
+    public Object annotationDefault;
+
+    /**
+     * The runtime visible parameter annotations of this method. These lists are
+     * lists of {@link AnnotationNode} objects. May be <tt>null</tt>.
+     * 
+     * @associates org.objectweb.asm.tree.AnnotationNode
+     * @label invisible parameters
+     */
+    public List[] visibleParameterAnnotations;
+
+    /**
+     * The runtime invisible parameter annotations of this method. These lists
+     * are lists of {@link AnnotationNode} objects. May be <tt>null</tt>.
+     * 
+     * @associates org.objectweb.asm.tree.AnnotationNode
+     * @label visible parameters
+     */
+    public List[] invisibleParameterAnnotations;
+
+    /**
+     * The instructions of this method. This list is a list of
+     * {@link AbstractInsnNode} objects.
+     * 
+     * @associates org.objectweb.asm.tree.AbstractInsnNode
+     * @label instructions
+     */
+    public List instructions;
+
+    /**
+     * The try catch blocks of this method. This list is a list of
+     * {@link TryCatchBlockNode} objects.
+     * 
+     * @associates org.objectweb.asm.tree.TryCatchBlockNode
+     */
+    public List tryCatchBlocks;
+
+    /**
+     * The maximum stack size of this method.
+     */
+    public int maxStack;
+
+    /**
+     * The maximum number of local variables of this method.
+     */
+    public int maxLocals;
+
+    /**
+     * The local variables of this method. This list is a list of
+     * {@link LocalVariableNode} objects. May be <tt>null</tt>
+     * 
+     * @associates org.objectweb.asm.tree.LocalVariableNode
+     */
+    public List localVariables;
+
+    /**
+     * The line numbers of this method. This list is a list of
+     * {@link LineNumberNode} objects. May be <tt>null</tt>
+     * 
+     * @associates org.objectweb.asm.tree.LineNumberNode
+     */
+    public List lineNumbers;
+
+    /**
+     * Constructs a new {@link MethodNode}.
+     * 
+     * @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}).
+     * @param signature the method's signature. May be <tt>null</tt>.
+     * @param exceptions the internal names of the method's exception classes
+     *        (see {@link Type#getInternalName() getInternalName}). May be
+     *        <tt>null</tt>.
+     */
+    public MethodNode(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        this.access = access;
+        this.name = name;
+        this.desc = desc;
+        this.signature = signature;
+        this.exceptions = new ArrayList(exceptions == null
+                ? 0
+                : exceptions.length);
+        boolean isAbstract = (access & Opcodes.ACC_ABSTRACT) != 0;
+        this.instructions = new ArrayList(isAbstract ? 0 : 24);
+        if (!isAbstract) {
+            this.localVariables = new ArrayList(5);
+            this.lineNumbers = new ArrayList(5);
+        }
+        this.tryCatchBlocks = new ArrayList();
+        if (exceptions != null) {
+            this.exceptions.addAll(Arrays.asList(exceptions));
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the MethodVisitor interface
+    // ------------------------------------------------------------------------
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        return new AnnotationNode(new ArrayList(0) {
+            public boolean add(Object o) {
+                annotationDefault = o;
+                return super.add(o);
+            }
+        });
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        AnnotationNode an = new AnnotationNode(desc);
+        if (visible) {
+            if (visibleParameterAnnotations == null) {
+                int params = Type.getArgumentTypes(this.desc).length;
+                visibleParameterAnnotations = new List[params];
+            }
+            if (visibleParameterAnnotations[parameter] == null) {
+                visibleParameterAnnotations[parameter] = new ArrayList(1);
+            }
+            visibleParameterAnnotations[parameter].add(an);
+        } else {
+            if (invisibleParameterAnnotations == null) {
+                int params = Type.getArgumentTypes(this.desc).length;
+                invisibleParameterAnnotations = new List[params];
+            }
+            if (invisibleParameterAnnotations[parameter] == null) {
+                invisibleParameterAnnotations[parameter] = new ArrayList(1);
+            }
+            invisibleParameterAnnotations[parameter].add(an);
+        }
+        return an;
+    }
+
+    public void visitCode() {
+    }
+
+    public void visitInsn(final int opcode) {
+        instructions.add(new InsnNode(opcode));
+    }
+
+    public void visitIntInsn(final int opcode, final int operand) {
+        instructions.add(new IntInsnNode(opcode, operand));
+    }
+
+    public void visitVarInsn(final int opcode, final int var) {
+        instructions.add(new VarInsnNode(opcode, var));
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        instructions.add(new TypeInsnNode(opcode, desc));
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        instructions.add(new FieldInsnNode(opcode, owner, name, desc));
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        instructions.add(new MethodInsnNode(opcode, owner, name, desc));
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(
+        String name,
+        String desc,
+        Handle bsm,
+        Object... bsmArgs)
+    {
+        instructions.add(new InvokeDynamicInsnNode(name, desc, bsm, bsmArgs));
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+        instructions.add(new JumpInsnNode(opcode, label));
+    }
+
+    public void visitLabel(final Label label) {
+        instructions.add(new LabelNode(label));
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        instructions.add(new LdcInsnNode(cst));
+    }
+
+    public void visitIincInsn(final int var, final int increment) {
+        instructions.add(new IincInsnNode(var, increment));
+    }
+
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label[] labels)
+    {
+        instructions.add(new TableSwitchInsnNode(min, max, dflt, labels));
+    }
+
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int[] keys,
+        final Label[] labels)
+    {
+        instructions.add(new LookupSwitchInsnNode(dflt, keys, labels));
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        instructions.add(new MultiANewArrayInsnNode(desc, dims));
+    }
+
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        tryCatchBlocks.add(new TryCatchBlockNode(start, end, handler, type));
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        localVariables.add(new LocalVariableNode(name,
+                desc,
+                signature,
+                start,
+                end,
+                index));
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        lineNumbers.add(new LineNumberNode(line, start));
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        this.maxStack = maxStack;
+        this.maxLocals = maxLocals;
+    }
+
+    // ------------------------------------------------------------------------
+    // Accept method
+    // ------------------------------------------------------------------------
+
+    /**
+     * Makes the given class visitor visit this method.
+     * 
+     * @param cv a class visitor.
+     */
+    public void accept(final ClassVisitor cv) {
+        String[] exceptions = new String[this.exceptions.size()];
+        this.exceptions.toArray(exceptions);
+        MethodVisitor mv = cv.visitMethod(access,
+                name,
+                desc,
+                signature,
+                exceptions);
+        if (mv != null) {
+            accept(mv);
+        }
+    }
+    
+    /**
+     * Makes the given method visitor visit this method.
+     * 
+     * @param mv a method visitor.
+     */
+    public void accept(final MethodVisitor mv) {
+        // visits the method attributes
+        int i, j, n;
+        if (annotationDefault != null) {
+            AnnotationVisitor av = mv.visitAnnotationDefault();
+            AnnotationNode.accept(av, null, annotationDefault);
+            av.visitEnd();
+        }
+        n = visibleAnnotations == null ? 0 : visibleAnnotations.size();
+        for (i = 0; i < n; ++i) {
+            AnnotationNode an = (AnnotationNode) visibleAnnotations.get(i);
+            an.accept(mv.visitAnnotation(an.desc, true));
+        }
+        n = invisibleAnnotations == null ? 0 : invisibleAnnotations.size();
+        for (i = 0; i < n; ++i) {
+            AnnotationNode an = (AnnotationNode) invisibleAnnotations.get(i);
+            an.accept(mv.visitAnnotation(an.desc, false));
+        }
+        n = visibleParameterAnnotations == null
+                ? 0
+                : visibleParameterAnnotations.length;
+        for (i = 0; i < n; ++i) {
+            List l = visibleParameterAnnotations[i];
+            if (l == null) {
+                continue;
+            }
+            for (j = 0; j < l.size(); ++j) {
+                AnnotationNode an = (AnnotationNode) l.get(j);
+                an.accept(mv.visitParameterAnnotation(i, an.desc, true));
+            }
+        }
+        n = invisibleParameterAnnotations == null
+                ? 0
+                : invisibleParameterAnnotations.length;
+        for (i = 0; i < n; ++i) {
+            List l = invisibleParameterAnnotations[i];
+            if (l == null) {
+                continue;
+            }
+            for (j = 0; j < l.size(); ++j) {
+                AnnotationNode an = (AnnotationNode) l.get(j);
+                an.accept(mv.visitParameterAnnotation(i, an.desc, false));
+            }
+        }
+        n = attrs == null ? 0 : attrs.size();
+        for (i = 0; i < n; ++i) {
+            mv.visitAttribute((Attribute) attrs.get(i));
+        }
+        // visits the method's code
+        if (instructions.size() > 0) {
+            mv.visitCode();
+            // visits try catch blocks
+            for (i = 0; i < tryCatchBlocks.size(); ++i) {
+                ((TryCatchBlockNode) tryCatchBlocks.get(i)).accept(mv);
+            }
+            // visits instructions
+            for (i = 0; i < instructions.size(); ++i) {
+                ((AbstractInsnNode) instructions.get(i)).accept(mv);
+            }
+            // visits local variables
+            n = localVariables == null ? 0 : localVariables.size();
+            for (i = 0; i < n; ++i) {
+                ((LocalVariableNode) localVariables.get(i)).accept(mv);
+            }
+            // visits line numbers
+            n = lineNumbers == null ? 0 : lineNumbers.size();
+            for (i = 0; i < n; ++i) {
+                ((LineNumberNode) lineNumbers.get(i)).accept(mv);
+            }
+            // visits maxs
+            mv.visitMaxs(maxStack, maxLocals);
+        }
+        mv.visitEnd();
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(int typeRef,
+        TypePath typePath, String desc, boolean visible) {
+      // TODO Auto-generated method stub
+      return null;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/MultiANewArrayInsnNode.java b/asmx/src/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
new file mode 100644
index 0000000..a9cb3c1
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/MultiANewArrayInsnNode.java
@@ -0,0 +1,71 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a MULTIANEWARRAY instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public class MultiANewArrayInsnNode extends AbstractInsnNode {
+
+    /**
+     * An array type descriptor (see {@link org.objectweb.asm.Type}).
+     */
+    public String desc;
+
+    /**
+     * Number of dimensions of the array to allocate.
+     */
+    public int dims;
+
+    /**
+     * Constructs a new {@link MultiANewArrayInsnNode}.
+     * 
+     * @param desc an array type descriptor (see {@link org.objectweb.asm.Type}).
+     * @param dims number of dimensions of the array to allocate.
+     */
+    public MultiANewArrayInsnNode(final String desc, final int dims) {
+        super(Opcodes.MULTIANEWARRAY);
+        this.desc = desc;
+        this.dims = dims;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    public int getType() {
+        return MULTIANEWARRAY_INSN;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/TableSwitchInsnNode.java b/asmx/src/org/objectweb/asm/tree/TableSwitchInsnNode.java
new file mode 100644
index 0000000..fa5e3f8
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/TableSwitchInsnNode.java
@@ -0,0 +1,102 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A node that represents a TABLESWITCH instruction.
+ * 
+ * @author Eric Bruneton
+ */
+public class TableSwitchInsnNode extends AbstractInsnNode {
+
+    /**
+     * The minimum key value.
+     */
+    public int min;
+
+    /**
+     * The maximum key value.
+     */
+    public int max;
+
+    /**
+     * Beginning of the default handler block.
+     */
+    public Label dflt;
+
+    /**
+     * Beginnings of the handler blocks. This list is a list of {@link Label}
+     * objects.
+     */
+    public List labels;
+
+    /**
+     * Constructs a new {@link TableSwitchInsnNode}.
+     * 
+     * @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 TableSwitchInsnNode(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label[] labels)
+    {
+        super(Opcodes.TABLESWITCH);
+        this.min = min;
+        this.max = max;
+        this.dflt = dflt;
+        this.labels = new ArrayList();
+        if (labels != null) {
+            this.labels.addAll(Arrays.asList(labels));
+        }
+    }
+
+    public void accept(final MethodVisitor mv) {
+        Label[] labels = new Label[this.labels.size()];
+        this.labels.toArray(labels);
+        mv.visitTableSwitchInsn(min, max, dflt, labels);
+    }
+
+    public int getType() {
+        return TABLESWITCH_INSN;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/TryCatchBlockNode.java b/asmx/src/org/objectweb/asm/tree/TryCatchBlockNode.java
new file mode 100644
index 0000000..18cd310
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/TryCatchBlockNode.java
@@ -0,0 +1,93 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a try catch block.
+ * 
+ * @author Eric Bruneton
+ */
+public class TryCatchBlockNode {
+
+    /**
+     * Beginning of the exception handler's scope (inclusive).
+     */
+    public Label start;
+
+    /**
+     * End of the exception handler's scope (exclusive).
+     */
+    public Label end;
+
+    /**
+     * Beginning of the exception handler's code.
+     */
+    public Label handler;
+
+    /**
+     * Internal name of the type of exceptions handled by the handler. May be
+     * <tt>null</tt> to catch any exceptions (for "finally" blocks).
+     */
+    public String type;
+
+    /**
+     * Constructs a new {@link TryCatchBlockNode}.
+     * 
+     * @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).
+     */
+    public TryCatchBlockNode(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        this.start = start;
+        this.end = end;
+        this.handler = handler;
+        this.type = type;
+    }
+
+    /**
+     * Makes the given visitor visit this try catch block.
+     * 
+     * @param mv a method visitor.
+     */
+    public void accept(final MethodVisitor mv) {
+        mv.visitTryCatchBlock(start, end, handler, type);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/TypeAnnotationNode.java b/asmx/src/org/objectweb/asm/tree/TypeAnnotationNode.java
new file mode 100644
index 0000000..b582146
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/TypeAnnotationNode.java
@@ -0,0 +1,10 @@
+package org.objectweb.asm.tree;

+

+import org.objectweb.asm.TypePath;

+

+public class TypeAnnotationNode extends AnnotationNode {

+    public TypeAnnotationNode(final int typeRef,

+            final TypePath typePath, final String desc) {

+        super(desc);

+    }

+}

diff --git a/asmx/src/org/objectweb/asm/tree/TypeInsnNode.java b/asmx/src/org/objectweb/asm/tree/TypeInsnNode.java
new file mode 100644
index 0000000..67c6b84
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/TypeInsnNode.java
@@ -0,0 +1,78 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a type instruction. A type instruction is an
+ * instruction that takes a type descriptor as parameter.
+ * 
+ * @author Eric Bruneton
+ */
+public class TypeInsnNode extends AbstractInsnNode {
+
+    /**
+     * The operand of this instruction. This operand is a type descriptor (see
+     * {@link org.objectweb.asm.Type}).
+     */
+    public String desc;
+
+    /**
+     * Constructs a new {@link TypeInsnNode}.
+     * 
+     * @param opcode the opcode of the type instruction to be constructed. This
+     *        opcode must be NEW, ANEWARRAY, CHECKCAST or INSTANCEOF.
+     * @param desc the operand of the instruction to be constructed. This
+     *        operand is a type descriptor (see {@link org.objectweb.asm.Type}).
+     */
+    public TypeInsnNode(final int opcode, final String desc) {
+        super(opcode);
+        this.desc = desc;
+    }
+
+    /**
+     * Sets the opcode of this instruction.
+     * 
+     * @param opcode the new instruction opcode. This opcode must be NEW,
+     *        ANEWARRAY, CHECKCAST or INSTANCEOF.
+     */
+    public void setOpcode(final int opcode) {
+        this.opcode = opcode;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitTypeInsn(opcode, desc);
+    }
+
+    public int getType() {
+        return TYPE_INSN;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/VarInsnNode.java b/asmx/src/org/objectweb/asm/tree/VarInsnNode.java
new file mode 100644
index 0000000..2fe3a95
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/VarInsnNode.java
@@ -0,0 +1,81 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * A node that represents a local variable instruction. A local variable
+ * instruction is an instruction that loads or stores the value of a local
+ * variable.
+ * 
+ * @author Eric Bruneton
+ */
+public class VarInsnNode extends AbstractInsnNode {
+
+    /**
+     * The operand of this instruction. This operand is the index of a local
+     * variable.
+     */
+    public int var;
+
+    /**
+     * Constructs a new {@link VarInsnNode}.
+     * 
+     * @param opcode the opcode of the local variable instruction to be
+     *        constructed. This opcode must be ILOAD, LLOAD, FLOAD, DLOAD,
+     *        ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET.
+     * @param var the operand of the instruction to be constructed. This operand
+     *        is the index of a local variable.
+     */
+    public VarInsnNode(final int opcode, final int var) {
+        super(opcode);
+        this.var = var;
+    }
+
+    /**
+     * Sets the opcode of this instruction.
+     * 
+     * @param opcode the new instruction opcode. This opcode must be ILOAD,
+     *        LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, ASTORE
+     *        or RET.
+     */
+    public void setOpcode(final int opcode) {
+        this.opcode = opcode;
+    }
+
+    public void accept(final MethodVisitor mv) {
+        mv.visitVarInsn(opcode, var);
+    }
+
+    public int getType() {
+        return VAR_INSN;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Analyzer.java b/asmx/src/org/objectweb/asm/tree/analysis/Analyzer.java
new file mode 100644
index 0000000..9fd4028
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/Analyzer.java
@@ -0,0 +1,416 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.IincInsnNode;
+import org.objectweb.asm.tree.JumpInsnNode;
+import org.objectweb.asm.tree.LabelNode;
+import org.objectweb.asm.tree.LookupSwitchInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TableSwitchInsnNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
+import org.objectweb.asm.tree.VarInsnNode;
+
+/**
+ * A semantic bytecode analyzer.
+ * 
+ * @author Eric Bruneton
+ */
+public class Analyzer implements Opcodes {
+
+    private Interpreter interpreter;
+
+    private int n;
+
+    private IntMap indexes;
+
+    private List[] handlers;
+
+    private Frame[] frames;
+
+    private Subroutine[] subroutines;
+
+    private boolean[] queued;
+
+    private int[] queue;
+
+    private int top;
+
+    private boolean jsr;
+
+    /**
+     * Constructs a new {@link Analyzer}.
+     * 
+     * @param interpreter the interpreter to be used to symbolically interpret
+     *        the bytecode instructions.
+     */
+    public Analyzer(final Interpreter interpreter) {
+        this.interpreter = interpreter;
+    }
+
+    /**
+     * Analyzes the given method.
+     * 
+     * @param owner the internal name of the class to which the method belongs.
+     * @param m the method to be analyzed.
+     * @return the symbolic state of the execution stack frame at each bytecode
+     *         instruction of the method. The size of the returned array is
+     *         equal to the number of instructions (and labels) of the method. A
+     *         given frame is <tt>null</tt> if and only if the corresponding
+     *         instruction cannot be reached (dead code).
+     * @throws AnalyzerException if a problem occurs during the analysis.
+     */
+    public Frame[] analyze(final String owner, final MethodNode m)
+            throws AnalyzerException
+    {
+        n = m.instructions.size();
+        indexes = new IntMap(2 * n);
+        handlers = new List[n];
+        frames = new Frame[n];
+        subroutines = new Subroutine[n];
+        queued = new boolean[n];
+        queue = new int[n];
+        top = 0;
+
+        // computes instruction indexes
+        for (int i = 0; i < n; ++i) {
+            Object insn = m.instructions.get(i);
+            if (insn instanceof LabelNode) {
+                insn = ((LabelNode) insn).label;
+            }
+            indexes.put(insn, i);
+        }
+
+        // computes exception handlers for each instruction
+        for (int i = 0; i < m.tryCatchBlocks.size(); ++i) {
+            TryCatchBlockNode tcb = (TryCatchBlockNode) m.tryCatchBlocks.get(i);
+            int begin = indexes.get(tcb.start);
+            int end = indexes.get(tcb.end);
+            for (int j = begin; j < end; ++j) {
+                List insnHandlers = handlers[j];
+                if (insnHandlers == null) {
+                    insnHandlers = new ArrayList();
+                    handlers[j] = insnHandlers;
+                }
+                insnHandlers.add(tcb);
+            }
+        }
+
+        // initializes the data structures for the control flow analysis
+        // algorithm
+        Frame current = newFrame(m.maxLocals, m.maxStack);
+        Frame handler = newFrame(m.maxLocals, m.maxStack);
+        Type[] args = Type.getArgumentTypes(m.desc);
+        int local = 0;
+        if ((m.access & ACC_STATIC) == 0) {
+            Type ctype = Type.getType("L" + owner + ";");
+            current.setLocal(local++, interpreter.newValue(ctype));
+        }
+        for (int i = 0; i < args.length; ++i) {
+            current.setLocal(local++, interpreter.newValue(args[i]));
+            if (args[i].getSize() == 2) {
+                current.setLocal(local++, interpreter.newValue(null));
+            }
+        }
+        while (local < m.maxLocals) {
+            current.setLocal(local++, interpreter.newValue(null));
+        }
+        merge(0, current, null);
+
+        // control flow analysis
+        while (top > 0) {
+            int insn = queue[--top];
+            Frame f = frames[insn];
+            Subroutine subroutine = subroutines[insn];
+            queued[insn] = false;
+
+            try {
+                Object o = m.instructions.get(insn);
+                jsr = false;
+
+                if (o instanceof LabelNode) {
+                    merge(insn + 1, f, subroutine);
+                } else {
+                    AbstractInsnNode insnNode = (AbstractInsnNode) o;
+                    int insnOpcode = insnNode.getOpcode();
+
+                    current.init(f).execute(insnNode, interpreter);
+                    subroutine = subroutine == null ? null : subroutine.copy();
+
+                    if (insnNode instanceof JumpInsnNode) {
+                        JumpInsnNode j = (JumpInsnNode) insnNode;
+                        if (insnOpcode != GOTO && insnOpcode != JSR) {
+                            merge(insn + 1, current, subroutine);
+                        }
+                        if (insnOpcode == JSR) {
+                            jsr = true;
+                            merge(indexes.get(j.label),
+                                    current,
+                                    new Subroutine(j.label, m.maxLocals, j));
+                        } else {
+                            merge(indexes.get(j.label), current, subroutine);
+                        }
+                    } else if (insnNode instanceof LookupSwitchInsnNode) {
+                        LookupSwitchInsnNode lsi = (LookupSwitchInsnNode) insnNode;
+                        merge(indexes.get(lsi.dflt), current, subroutine);
+                        for (int j = 0; j < lsi.labels.size(); ++j) {
+                            Label label = (Label) lsi.labels.get(j);
+                            merge(indexes.get(label), current, subroutine);
+                        }
+                    } else if (insnNode instanceof TableSwitchInsnNode) {
+                        TableSwitchInsnNode tsi = (TableSwitchInsnNode) insnNode;
+                        merge(indexes.get(tsi.dflt), current, subroutine);
+                        for (int j = 0; j < tsi.labels.size(); ++j) {
+                            Label label = (Label) tsi.labels.get(j);
+                            merge(indexes.get(label), current, subroutine);
+                        }
+                    } else if (insnOpcode == RET) {
+                        if (subroutine == null) {
+                            throw new AnalyzerException("RET instruction outside of a sub routine");
+                        }
+                        for (int i = 0; i < subroutine.callers.size(); ++i) {
+                            int caller = indexes.get(subroutine.callers.get(i));
+                            merge(caller + 1,
+                                    frames[caller],
+                                    current,
+                                    subroutines[caller],
+                                    subroutine.access);
+                        }
+                    } else if (insnOpcode != ATHROW
+                            && (insnOpcode < IRETURN || insnOpcode > RETURN))
+                    {
+                        if (subroutine != null) {
+                            if (insnNode instanceof VarInsnNode) {
+                                int var = ((VarInsnNode) insnNode).var;
+                                subroutine.access[var] = true;
+                                if (insnOpcode == LLOAD || insnOpcode == DLOAD
+                                        || insnOpcode == LSTORE
+                                        || insnOpcode == DSTORE)
+                                {
+                                    subroutine.access[var + 1] = true;
+                                }
+                            } else if (insnNode instanceof IincInsnNode) {
+                                int var = ((IincInsnNode) insnNode).var;
+                                subroutine.access[var] = true;
+                            }
+                        }
+                        merge(insn + 1, current, subroutine);
+                    }
+                }
+
+                List insnHandlers = handlers[insn];
+                if (insnHandlers != null) {
+                    for (int i = 0; i < insnHandlers.size(); ++i) {
+                        TryCatchBlockNode tcb = (TryCatchBlockNode) insnHandlers.get(i);
+                        Type type;
+                        if (tcb.type == null) {
+                            type = Type.getType("Ljava/lang/Throwable;");
+                        } else {
+                            type = Type.getType("L" + tcb.type + ";");
+                        }
+                        handler.init(f);
+                        handler.clearStack();
+                        handler.push(interpreter.newValue(type));
+                        merge(indexes.get(tcb.handler), handler, subroutine);
+                    }
+                }
+            } catch (AnalyzerException e) {
+                throw new AnalyzerException("Error at instruction " + insn
+                        + ": " + e.getMessage(), e);
+            } catch(Exception e) {
+                throw new AnalyzerException("Error at instruction " + insn
+                        + ": " + e.getMessage(), e);
+            }
+        }
+
+        return frames;
+    }
+
+    /**
+     * Returns the symbolic stack frame for each instruction of the last
+     * recently analyzed method.
+     * 
+     * @return the symbolic state of the execution stack frame at each bytecode
+     *         instruction of the method. The size of the returned array is
+     *         equal to the number of instructions (and labels) of the method. A
+     *         given frame is <tt>null</tt> if the corresponding instruction
+     *         cannot be reached, or if an error occured during the analysis of
+     *         the method.
+     */
+    public Frame[] getFrames() {
+        return frames;
+    }
+
+    /**
+     * Returns the index of the given instruction.
+     * 
+     * @param insn a {@link Label} or {@link AbstractInsnNode} of the last
+     *        recently analyzed method.
+     * @return the index of the given instruction of the last recently analyzed
+     *         method.
+     */
+    public int getIndex(final Object insn) {
+        return indexes.get(insn);
+    }
+
+    /**
+     * Returns the exception handlers for the given instruction.
+     * 
+     * @param insn the index of an instruction of the last recently analyzed
+     *        method.
+     * @return a list of {@link TryCatchBlockNode} objects.
+     */
+    public List getHandlers(final int insn) {
+        return handlers[insn];
+    }
+
+    /**
+     * Constructs a new frame with the given size.
+     * 
+     * @param nLocals the maximum number of local variables of the frame.
+     * @param nStack the maximum stack size of the frame.
+     * @return the created frame.
+     */
+    protected Frame newFrame(final int nLocals, final int nStack) {
+        return new Frame(nLocals, nStack);
+    }
+
+    /**
+     * Constructs a new frame that is identical to the given frame.
+     * 
+     * @param src a frame.
+     * @return the created frame.
+     */
+    protected Frame newFrame(final Frame src) {
+        return new Frame(src);
+    }
+
+    /**
+     * Creates a control flow graph edge. The default implementation of this
+     * method does nothing. It can be overriden in order to construct the
+     * control flow graph of a method (this method is called by the
+     * {@link #analyze analyze} method during its visit of the method's code).
+     * 
+     * @param frame the frame corresponding to an instruction.
+     * @param successor the frame corresponding to a successor instruction.
+     */
+    protected void newControlFlowEdge(final Frame frame, final Frame successor)
+    {
+    }
+
+    // -------------------------------------------------------------------------
+
+    private void merge(
+        final int insn,
+        final Frame frame,
+        final Subroutine subroutine) throws AnalyzerException
+    {
+        if (insn > n - 1) {
+            throw new AnalyzerException("Execution can fall off end of the code");
+        }
+
+        Frame oldFrame = frames[insn];
+        Subroutine oldSubroutine = subroutines[insn];
+        boolean changes = false;
+
+        if (oldFrame == null) {
+            frames[insn] = newFrame(frame);
+            changes = true;
+        } else {
+            changes |= oldFrame.merge(frame, interpreter);
+        }
+
+        newControlFlowEdge(frame, oldFrame);
+
+        if (oldSubroutine == null) {
+            if (subroutine != null) {
+                subroutines[insn] = subroutine.copy();
+                changes = true;
+            }
+        } else {
+            if (subroutine != null) {
+                changes |= oldSubroutine.merge(subroutine, !jsr);
+            }
+        }
+        if (changes && !queued[insn]) {
+            queued[insn] = true;
+            queue[top++] = insn;
+        }
+    }
+
+    private void merge(
+        final int insn,
+        final Frame beforeJSR,
+        final Frame afterRET,
+        final Subroutine subroutineBeforeJSR,
+        final boolean[] access) throws AnalyzerException
+    {
+        if (insn > n - 1) {
+            throw new AnalyzerException("Execution can fall off end of the code");
+        }
+
+        Frame oldFrame = frames[insn];
+        Subroutine oldSubroutine = subroutines[insn];
+        boolean changes = false;
+
+        afterRET.merge(beforeJSR, access);
+
+        if (oldFrame == null) {
+            frames[insn] = newFrame(afterRET);
+            changes = true;
+        } else {
+            changes |= oldFrame.merge(afterRET, access);
+        }
+
+        newControlFlowEdge(afterRET, oldFrame);
+
+        if (oldSubroutine == null) {
+            if (subroutineBeforeJSR != null) {
+                subroutines[insn] = subroutineBeforeJSR.copy();
+                changes = true;
+            }
+        } else {
+            if (subroutineBeforeJSR != null) {
+                changes |= oldSubroutine.merge(subroutineBeforeJSR, !jsr);
+            }
+        }
+        if (changes && !queued[insn]) {
+            queued[insn] = true;
+            queue[top++] = insn;
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/AnalyzerException.java b/asmx/src/org/objectweb/asm/tree/analysis/AnalyzerException.java
new file mode 100644
index 0000000..3e0afac
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/AnalyzerException.java
@@ -0,0 +1,56 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+/**
+ * Thrown if a problem occurs during the analysis of a method.
+ * 
+ * @author Bing Ran
+ * @author Eric Bruneton
+ */
+public class AnalyzerException extends Exception {
+
+    public AnalyzerException(final String msg) {
+        super(msg);
+    }
+
+    public AnalyzerException(final String msg, final Throwable exception) {
+        super(msg, exception);
+    }
+
+    public AnalyzerException(
+        final String msg,
+        final Object expected,
+        final Value encountered)
+    {
+        super((msg == null ? "Expected " : msg + ": expected ") + expected
+                + ", but found " + encountered);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/BasicInterpreter.java b/asmx/src/org/objectweb/asm/tree/analysis/BasicInterpreter.java
new file mode 100644
index 0000000..b92b57c
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/BasicInterpreter.java
@@ -0,0 +1,335 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.List;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.IntInsnNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MultiANewArrayInsnNode;
+import org.objectweb.asm.tree.TypeInsnNode;
+
+/**
+ * An {@link Interpreter} for {@link BasicValue} values.
+ * 
+ * @author Eric Bruneton
+ * @author Bing Ran
+ */
+public class BasicInterpreter implements Opcodes, Interpreter {
+
+    public Value newValue(final Type type) {
+        if (type == null) {
+            return BasicValue.UNINITIALIZED_VALUE;
+        }
+        switch (type.getSort()) {
+            case Type.VOID:
+                return null;
+            case Type.BOOLEAN:
+            case Type.CHAR:
+            case Type.BYTE:
+            case Type.SHORT:
+            case Type.INT:
+                return BasicValue.INT_VALUE;
+            case Type.FLOAT:
+                return BasicValue.FLOAT_VALUE;
+            case Type.LONG:
+                return BasicValue.LONG_VALUE;
+            case Type.DOUBLE:
+                return BasicValue.DOUBLE_VALUE;
+            case Type.ARRAY:
+            case Type.OBJECT:
+                return BasicValue.REFERENCE_VALUE;
+            default:
+                throw new RuntimeException("Internal error.");
+        }
+    }
+
+    public Value newOperation(final AbstractInsnNode insn) {
+        switch (insn.getOpcode()) {
+            case ACONST_NULL:
+                return newValue(Type.getType("Lnull;"));
+            case ICONST_M1:
+            case ICONST_0:
+            case ICONST_1:
+            case ICONST_2:
+            case ICONST_3:
+            case ICONST_4:
+            case ICONST_5:
+                return BasicValue.INT_VALUE;
+            case LCONST_0:
+            case LCONST_1:
+                return BasicValue.LONG_VALUE;
+            case FCONST_0:
+            case FCONST_1:
+            case FCONST_2:
+                return BasicValue.FLOAT_VALUE;
+            case DCONST_0:
+            case DCONST_1:
+                return BasicValue.DOUBLE_VALUE;
+            case BIPUSH:
+            case SIPUSH:
+                return BasicValue.INT_VALUE;
+            case LDC:
+                Object cst = ((LdcInsnNode) insn).cst;
+                if (cst instanceof Integer) {
+                    return BasicValue.INT_VALUE;
+                } else if (cst instanceof Float) {
+                    return BasicValue.FLOAT_VALUE;
+                } else if (cst instanceof Long) {
+                    return BasicValue.LONG_VALUE;
+                } else if (cst instanceof Double) {
+                    return BasicValue.DOUBLE_VALUE;
+                } else if (cst instanceof Type) {
+                    return newValue(Type.getType("Ljava/lang/Class;"));
+                } else {
+                    return newValue(Type.getType(cst.getClass()));
+                }
+            case JSR:
+                return BasicValue.RETURNADDRESS_VALUE;
+            case GETSTATIC:
+                return newValue(Type.getType(((FieldInsnNode) insn).desc));
+            case NEW:
+                return newValue(Type.getType("L" + ((TypeInsnNode) insn).desc
+                        + ";"));
+            default:
+                throw new RuntimeException("Internal error.");
+        }
+    }
+
+    public Value copyOperation(final AbstractInsnNode insn, final Value value)
+            throws AnalyzerException
+    {
+        return value;
+    }
+
+    public Value unaryOperation(final AbstractInsnNode insn, final Value value)
+            throws AnalyzerException
+    {
+        switch (insn.getOpcode()) {
+            case INEG:
+            case IINC:
+            case L2I:
+            case F2I:
+            case D2I:
+            case I2B:
+            case I2C:
+            case I2S:
+                return BasicValue.INT_VALUE;
+            case FNEG:
+            case I2F:
+            case L2F:
+            case D2F:
+                return BasicValue.FLOAT_VALUE;
+            case LNEG:
+            case I2L:
+            case F2L:
+            case D2L:
+                return BasicValue.LONG_VALUE;
+            case DNEG:
+            case I2D:
+            case L2D:
+            case F2D:
+                return BasicValue.DOUBLE_VALUE;
+            case IFEQ:
+            case IFNE:
+            case IFLT:
+            case IFGE:
+            case IFGT:
+            case IFLE:
+            case TABLESWITCH:
+            case LOOKUPSWITCH:
+            case IRETURN:
+            case LRETURN:
+            case FRETURN:
+            case DRETURN:
+            case ARETURN:
+            case PUTSTATIC:
+                return null;
+            case GETFIELD:
+                return newValue(Type.getType(((FieldInsnNode) insn).desc));
+            case NEWARRAY:
+                switch (((IntInsnNode) insn).operand) {
+                    case T_BOOLEAN:
+                        return newValue(Type.getType("[Z"));
+                    case T_CHAR:
+                        return newValue(Type.getType("[C"));
+                    case T_BYTE:
+                        return newValue(Type.getType("[B"));
+                    case T_SHORT:
+                        return newValue(Type.getType("[S"));
+                    case T_INT:
+                        return newValue(Type.getType("[I"));
+                    case T_FLOAT:
+                        return newValue(Type.getType("[F"));
+                    case T_DOUBLE:
+                        return newValue(Type.getType("[D"));
+                    case T_LONG:
+                        return newValue(Type.getType("[J"));
+                    default:
+                        throw new AnalyzerException("Invalid array type");
+                }
+            case ANEWARRAY:
+                String desc = ((TypeInsnNode) insn).desc;
+                if (desc.charAt(0) == '[') {
+                    return newValue(Type.getType("[" + desc));
+                } else {
+                    return newValue(Type.getType("[L" + desc + ";"));
+                }
+            case ARRAYLENGTH:
+                return BasicValue.INT_VALUE;
+            case ATHROW:
+                return null;
+            case CHECKCAST:
+                desc = ((TypeInsnNode) insn).desc;
+                if (desc.charAt(0) == '[') {
+                    return newValue(Type.getType(desc));
+                } else {
+                    return newValue(Type.getType("L" + desc + ";"));
+                }
+            case INSTANCEOF:
+                return BasicValue.INT_VALUE;
+            case MONITORENTER:
+            case MONITOREXIT:
+            case IFNULL:
+            case IFNONNULL:
+                return null;
+            default:
+                throw new RuntimeException("Internal error.");
+        }
+    }
+
+    public Value binaryOperation(
+        final AbstractInsnNode insn,
+        final Value value1,
+        final Value value2) throws AnalyzerException
+    {
+        switch (insn.getOpcode()) {
+            case IALOAD:
+            case BALOAD:
+            case CALOAD:
+            case SALOAD:
+            case IADD:
+            case ISUB:
+            case IMUL:
+            case IDIV:
+            case IREM:
+            case ISHL:
+            case ISHR:
+            case IUSHR:
+            case IAND:
+            case IOR:
+            case IXOR:
+                return BasicValue.INT_VALUE;
+            case FALOAD:
+            case FADD:
+            case FSUB:
+            case FMUL:
+            case FDIV:
+            case FREM:
+                return BasicValue.FLOAT_VALUE;
+            case LALOAD:
+            case LADD:
+            case LSUB:
+            case LMUL:
+            case LDIV:
+            case LREM:
+            case LSHL:
+            case LSHR:
+            case LUSHR:
+            case LAND:
+            case LOR:
+            case LXOR:
+                return BasicValue.LONG_VALUE;
+            case DALOAD:
+            case DADD:
+            case DSUB:
+            case DMUL:
+            case DDIV:
+            case DREM:
+                return BasicValue.DOUBLE_VALUE;
+            case AALOAD:
+                Type t = ((BasicValue) value1).getType();
+                if (t != null && t.getSort() == Type.ARRAY) {
+                    return newValue(t.getElementType());
+                } else {
+                    return BasicValue.REFERENCE_VALUE;
+                }
+            case LCMP:
+            case FCMPL:
+            case FCMPG:
+            case DCMPL:
+            case DCMPG:
+                return BasicValue.INT_VALUE;
+            case IF_ICMPEQ:
+            case IF_ICMPNE:
+            case IF_ICMPLT:
+            case IF_ICMPGE:
+            case IF_ICMPGT:
+            case IF_ICMPLE:
+            case IF_ACMPEQ:
+            case IF_ACMPNE:
+            case PUTFIELD:
+                return null;
+            default:
+                throw new RuntimeException("Internal error.");
+        }
+    }
+
+    public Value ternaryOperation(
+        final AbstractInsnNode insn,
+        final Value value1,
+        final Value value2,
+        final Value value3) throws AnalyzerException
+    {
+        return null;
+    }
+
+    public Value naryOperation(final AbstractInsnNode insn, final List values)
+            throws AnalyzerException
+    {
+        if (insn.getOpcode() == MULTIANEWARRAY) {
+            return newValue(Type.getType(((MultiANewArrayInsnNode) insn).desc));
+        } else {
+            return newValue(Type.getReturnType(((MethodInsnNode) insn).desc));
+        }
+    }
+
+    public Value merge(final Value v, final Value w) {
+        if (!v.equals(w)) {
+            return BasicValue.UNINITIALIZED_VALUE;
+        }
+        return v;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/BasicValue.java b/asmx/src/org/objectweb/asm/tree/analysis/BasicValue.java
new file mode 100644
index 0000000..19cdb33
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/BasicValue.java
@@ -0,0 +1,105 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link Value} that is represented by its type in a seven types type sytem.
+ * This type system distinguishes the UNINITIALZED, INT, FLOAT, LONG, DOUBLE,
+ * REFERENCE and RETURNADDRESS types.
+ * 
+ * @author Eric Bruneton
+ */
+public class BasicValue implements Value {
+
+    public final static Value UNINITIALIZED_VALUE = new BasicValue(null);
+
+    public final static Value INT_VALUE = new BasicValue(Type.INT_TYPE);
+
+    public final static Value FLOAT_VALUE = new BasicValue(Type.FLOAT_TYPE);
+
+    public final static Value LONG_VALUE = new BasicValue(Type.LONG_TYPE);
+
+    public final static Value DOUBLE_VALUE = new BasicValue(Type.DOUBLE_TYPE);
+
+    public final static Value REFERENCE_VALUE = new BasicValue(Type.getType("Ljava/lang/Object;"));
+
+    public final static Value RETURNADDRESS_VALUE = new BasicValue(null);
+
+    private Type type;
+
+    public BasicValue(final Type type) {
+        this.type = type;
+    }
+
+    public Type getType() {
+        return type;
+    }
+
+    public int getSize() {
+        return type == Type.LONG_TYPE || type == Type.DOUBLE_TYPE ? 2 : 1;
+    }
+
+    public boolean isReference() {
+        return type != null
+                && (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY);
+    }
+
+    public boolean equals(final Object value) {
+        if (value == this) {
+            return true;
+        } else if (value instanceof BasicValue) {
+            if (type == null) {
+                return ((BasicValue) value).type == null;
+            } else {
+                return type.equals(((BasicValue) value).type);
+            }
+        } else {
+            return false;
+        }
+    }
+
+    public int hashCode() {
+        return type == null ? 0 : type.hashCode();
+    }
+
+    public String toString() {
+        if (this == UNINITIALIZED_VALUE) {
+            return ".";
+        } else if (this == RETURNADDRESS_VALUE) {
+            return "A";
+        } else if (this == REFERENCE_VALUE) {
+            return "R";
+        } else {
+            return type.getDescriptor();
+        }
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/BasicVerifier.java b/asmx/src/org/objectweb/asm/tree/analysis/BasicVerifier.java
new file mode 100644
index 0000000..0a797d4
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/BasicVerifier.java
@@ -0,0 +1,428 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.List;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+
+/**
+ * An extended {@link BasicInterpreter} that checks that bytecode instructions
+ * are correctly used.
+ * 
+ * @author Eric Bruneton
+ * @author Bing Ran
+ */
+public class BasicVerifier extends BasicInterpreter {
+
+    public Value copyOperation(final AbstractInsnNode insn, final Value value)
+            throws AnalyzerException
+    {
+        Value expected;
+        switch (insn.getOpcode()) {
+            case ILOAD:
+            case ISTORE:
+                expected = BasicValue.INT_VALUE;
+                break;
+            case FLOAD:
+            case FSTORE:
+                expected = BasicValue.FLOAT_VALUE;
+                break;
+            case LLOAD:
+            case LSTORE:
+                expected = BasicValue.LONG_VALUE;
+                break;
+            case DLOAD:
+            case DSTORE:
+                expected = BasicValue.DOUBLE_VALUE;
+                break;
+            case ALOAD:
+                if (!((BasicValue) value).isReference()) {
+                    throw new AnalyzerException(null,
+                            "an object reference",
+                            value);
+                }
+                return value;
+            case ASTORE:
+                if (!((BasicValue) value).isReference()
+                        && value != BasicValue.RETURNADDRESS_VALUE)
+                {
+                    throw new AnalyzerException(null,
+                            "an object reference or a return address",
+                            value);
+                }
+                return value;
+            default:
+                return value;
+        }
+        // type is necessarily a primitive type here,
+        // so value must be == to expected value
+        if (value != expected) {
+            throw new AnalyzerException(null, expected, value);
+        }
+        return value;
+    }
+
+    public Value unaryOperation(final AbstractInsnNode insn, Value value)
+            throws AnalyzerException
+    {
+        Value expected;
+        switch (insn.getOpcode()) {
+            case INEG:
+            case IINC:
+            case I2F:
+            case I2L:
+            case I2D:
+            case I2B:
+            case I2C:
+            case I2S:
+            case IFEQ:
+            case IFNE:
+            case IFLT:
+            case IFGE:
+            case IFGT:
+            case IFLE:
+            case TABLESWITCH:
+            case LOOKUPSWITCH:
+            case IRETURN:
+            case NEWARRAY:
+            case ANEWARRAY:
+                expected = BasicValue.INT_VALUE;
+                break;
+            case FNEG:
+            case F2I:
+            case F2L:
+            case F2D:
+            case FRETURN:
+                expected = BasicValue.FLOAT_VALUE;
+                break;
+            case LNEG:
+            case L2I:
+            case L2F:
+            case L2D:
+            case LRETURN:
+                expected = BasicValue.LONG_VALUE;
+                break;
+            case DNEG:
+            case D2I:
+            case D2F:
+            case D2L:
+            case DRETURN:
+                expected = BasicValue.DOUBLE_VALUE;
+                break;
+            case GETFIELD:
+                expected = newValue(Type.getType("L"
+                        + ((FieldInsnNode) insn).owner + ";"));
+                break;
+            case CHECKCAST:
+                if (!((BasicValue) value).isReference()) {
+                    throw new AnalyzerException(null,
+                            "an object reference",
+                            value);
+                }
+                return super.unaryOperation(insn, value);
+            case ARRAYLENGTH:
+                if (!isArrayValue(value)) {
+                    throw new AnalyzerException(null,
+                            "an array reference",
+                            value);
+                }
+                return super.unaryOperation(insn, value);
+            case ARETURN:
+            case ATHROW:
+            case INSTANCEOF:
+            case MONITORENTER:
+            case MONITOREXIT:
+            case IFNULL:
+            case IFNONNULL:
+                if (!((BasicValue) value).isReference()) {
+                    throw new AnalyzerException(null,
+                            "an object reference",
+                            value);
+                }
+                return super.unaryOperation(insn, value);
+            case PUTSTATIC:
+                expected = newValue(Type.getType(((FieldInsnNode) insn).desc));
+                break;
+            default:
+                throw new RuntimeException("Internal error.");
+        }
+        if (!isSubTypeOf(value, expected)) {
+            throw new AnalyzerException(null, expected, value);
+        }
+        return super.unaryOperation(insn, value);
+    }
+
+    public Value binaryOperation(
+        final AbstractInsnNode insn,
+        final Value value1,
+        final Value value2) throws AnalyzerException
+    {
+        Value expected1;
+        Value expected2;
+        switch (insn.getOpcode()) {
+            case IALOAD:
+                expected1 = newValue(Type.getType("[I"));
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case BALOAD:
+                if (!isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
+                    expected1 = newValue(Type.getType("[B"));
+                } else {
+                    expected1 = newValue(Type.getType("[Z"));
+                }
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case CALOAD:
+                expected1 = newValue(Type.getType("[C"));
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case SALOAD:
+                expected1 = newValue(Type.getType("[S"));
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case LALOAD:
+                expected1 = newValue(Type.getType("[J"));
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case FALOAD:
+                expected1 = newValue(Type.getType("[F"));
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case DALOAD:
+                expected1 = newValue(Type.getType("[D"));
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case AALOAD:
+                expected1 = newValue(Type.getType("[Ljava/lang/Object;"));
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case IADD:
+            case ISUB:
+            case IMUL:
+            case IDIV:
+            case IREM:
+            case ISHL:
+            case ISHR:
+            case IUSHR:
+            case IAND:
+            case IOR:
+            case IXOR:
+            case IF_ICMPEQ:
+            case IF_ICMPNE:
+            case IF_ICMPLT:
+            case IF_ICMPGE:
+            case IF_ICMPGT:
+            case IF_ICMPLE:
+                expected1 = BasicValue.INT_VALUE;
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case FADD:
+            case FSUB:
+            case FMUL:
+            case FDIV:
+            case FREM:
+            case FCMPL:
+            case FCMPG:
+                expected1 = BasicValue.FLOAT_VALUE;
+                expected2 = BasicValue.FLOAT_VALUE;
+                break;
+            case LADD:
+            case LSUB:
+            case LMUL:
+            case LDIV:
+            case LREM:
+            case LAND:
+            case LOR:
+            case LXOR:
+            case LCMP:
+                expected1 = BasicValue.LONG_VALUE;
+                expected2 = BasicValue.LONG_VALUE;
+                break;
+            case LSHL:
+            case LSHR:
+            case LUSHR:
+                expected1 = BasicValue.LONG_VALUE;
+                expected2 = BasicValue.INT_VALUE;
+                break;
+            case DADD:
+            case DSUB:
+            case DMUL:
+            case DDIV:
+            case DREM:
+            case DCMPL:
+            case DCMPG:
+                expected1 = BasicValue.DOUBLE_VALUE;
+                expected2 = BasicValue.DOUBLE_VALUE;
+                break;
+            case IF_ACMPEQ:
+            case IF_ACMPNE:
+                expected1 = BasicValue.REFERENCE_VALUE;
+                expected2 = BasicValue.REFERENCE_VALUE;
+                break;
+            case PUTFIELD:
+                FieldInsnNode fin = (FieldInsnNode) insn;
+                expected1 = newValue(Type.getType("L" + fin.owner + ";"));
+                expected2 = newValue(Type.getType(fin.desc));
+                break;
+            default:
+                throw new RuntimeException("Internal error.");
+        }
+        if (!isSubTypeOf(value1, expected1)) {
+            throw new AnalyzerException("First argument", expected1, value1);
+        } else if (!isSubTypeOf(value2, expected2)) {
+            throw new AnalyzerException("Second argument", expected2, value2);
+        }
+        if (insn.getOpcode() == AALOAD) {
+            return getElementValue(value1);
+        } else {
+            return super.binaryOperation(insn, value1, value2);
+        }
+    }
+
+    public Value ternaryOperation(
+        final AbstractInsnNode insn,
+        final Value value1,
+        final Value value2,
+        final Value value3) throws AnalyzerException
+    {
+        Value expected1;
+        Value expected3;
+        switch (insn.getOpcode()) {
+            case IASTORE:
+                expected1 = newValue(Type.getType("[I"));
+                expected3 = BasicValue.INT_VALUE;
+                break;
+            case BASTORE:
+                if (!isSubTypeOf(value1, newValue(Type.getType("[Z")))) {
+                    expected1 = newValue(Type.getType("[B"));
+                } else {
+                    expected1 = newValue(Type.getType("[Z"));
+                }
+                expected3 = BasicValue.INT_VALUE;
+                break;
+            case CASTORE:
+                expected1 = newValue(Type.getType("[C"));
+                expected3 = BasicValue.INT_VALUE;
+                break;
+            case SASTORE:
+                expected1 = newValue(Type.getType("[S"));
+                expected3 = BasicValue.INT_VALUE;
+                break;
+            case LASTORE:
+                expected1 = newValue(Type.getType("[J"));
+                expected3 = BasicValue.LONG_VALUE;
+                break;
+            case FASTORE:
+                expected1 = newValue(Type.getType("[F"));
+                expected3 = BasicValue.FLOAT_VALUE;
+                break;
+            case DASTORE:
+                expected1 = newValue(Type.getType("[D"));
+                expected3 = BasicValue.DOUBLE_VALUE;
+                break;
+            case AASTORE:
+                expected1 = value1;
+                expected3 = BasicValue.REFERENCE_VALUE;
+                break;
+            default:
+                throw new RuntimeException("Internal error.");
+        }
+        if (!isSubTypeOf(value1, expected1)) {
+            throw new AnalyzerException("First argument", "a " + expected1
+                    + " array reference", value1);
+        } else if (value2 != BasicValue.INT_VALUE) {
+            throw new AnalyzerException("Second argument",
+                    BasicValue.INT_VALUE,
+                    value2);
+        } else if (!isSubTypeOf(value3, expected3)) {
+            throw new AnalyzerException("Third argument", expected3, value3);
+        }
+        return null;
+    }
+
+    public Value naryOperation(final AbstractInsnNode insn, final List values)
+            throws AnalyzerException
+    {
+        int opcode = insn.getOpcode();
+        if (opcode == MULTIANEWARRAY) {
+            for (int i = 0; i < values.size(); ++i) {
+                if (values.get(i) != BasicValue.INT_VALUE) {
+                    throw new AnalyzerException(null,
+                            BasicValue.INT_VALUE,
+                            (Value) values.get(i));
+                }
+            }
+        } else {
+            int i = 0;
+            int j = 0;
+            if (opcode != INVOKESTATIC) {
+                String own = ((MethodInsnNode) insn).owner;
+                if (own.charAt(0) != '[') { // can happen with JDK1.5 clone()
+                    own = "L" + own + ";";
+                }
+                Type owner = Type.getType(own);
+                if (!isSubTypeOf((Value) values.get(i++), newValue(owner))) {
+                    throw new AnalyzerException("Method owner",
+                            newValue(owner),
+                            (Value) values.get(0));
+                }
+            }
+            Type[] args = Type.getArgumentTypes(((MethodInsnNode) insn).desc);
+            while (i < values.size()) {
+                Value expected = newValue(args[j++]);
+                Value encountered = (Value) values.get(i++);
+                if (!isSubTypeOf(encountered, expected)) {
+                    throw new AnalyzerException("Argument " + j,
+                            expected,
+                            encountered);
+                }
+            }
+        }
+        return super.naryOperation(insn, values);
+    }
+
+    protected boolean isArrayValue(final Value value) {
+        return ((BasicValue) value).isReference();
+    }
+
+    protected Value getElementValue(final Value objectArrayValue)
+            throws AnalyzerException
+    {
+        return BasicValue.REFERENCE_VALUE;
+    }
+
+    protected boolean isSubTypeOf(final Value value, final Value expected) {
+        return value == expected;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/DataflowInterpreter.java b/asmx/src/org/objectweb/asm/tree/analysis/DataflowInterpreter.java
new file mode 100644
index 0000000..07edd74
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/DataflowInterpreter.java
@@ -0,0 +1,174 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.FieldInsnNode;
+import org.objectweb.asm.tree.LdcInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+
+/**
+ * An {@link Interpreter} for {@link DataflowValue} values.
+ * 
+ * @author Eric Bruneton
+ */
+public class DataflowInterpreter implements Opcodes, Interpreter {
+
+    public Value newValue(final Type type) {
+        return new DataflowValue(type == null ? 1 : type.getSize());
+    }
+
+    public Value newOperation(final AbstractInsnNode insn) {
+        int size;
+        switch (insn.getOpcode()) {
+            case LCONST_0:
+            case LCONST_1:
+            case DCONST_0:
+            case DCONST_1:
+                size = 2;
+                break;
+            case LDC:
+                Object cst = ((LdcInsnNode) insn).cst;
+                size = cst instanceof Long || cst instanceof Double ? 2 : 1;
+                break;
+            case GETSTATIC:
+                size = Type.getType(((FieldInsnNode) insn).desc).getSize();
+                break;
+            default:
+                size = 1;
+        }
+        return new DataflowValue(size, insn);
+    }
+
+    public Value copyOperation(final AbstractInsnNode insn, final Value value) {
+        return new DataflowValue(value.getSize(), insn);
+    }
+
+    public Value unaryOperation(final AbstractInsnNode insn, final Value value)
+    {
+        int size;
+        switch (insn.getOpcode()) {
+            case LNEG:
+            case DNEG:
+            case I2L:
+            case I2D:
+            case L2D:
+            case F2L:
+            case F2D:
+            case D2L:
+                size = 2;
+                break;
+            case GETFIELD:
+                size = Type.getType(((FieldInsnNode) insn).desc).getSize();
+                break;
+            default:
+                size = 1;
+        }
+        return new DataflowValue(size, insn);
+    }
+
+    public Value binaryOperation(
+        final AbstractInsnNode insn,
+        final Value value1,
+        final Value value2)
+    {
+        int size;
+        switch (insn.getOpcode()) {
+            case LALOAD:
+            case DALOAD:
+            case LADD:
+            case DADD:
+            case LSUB:
+            case DSUB:
+            case LMUL:
+            case DMUL:
+            case LDIV:
+            case DDIV:
+            case LREM:
+            case DREM:
+            case LSHL:
+            case LSHR:
+            case LUSHR:
+            case LAND:
+            case LOR:
+            case LXOR:
+                size = 2;
+                break;
+            default:
+                size = 1;
+        }
+        return new DataflowValue(size, insn);
+    }
+
+    public Value ternaryOperation(
+        final AbstractInsnNode insn,
+        final Value value1,
+        final Value value2,
+        final Value value3)
+    {
+        return new DataflowValue(1, insn);
+    }
+
+    public Value naryOperation(final AbstractInsnNode insn, final List values) {
+        int size;
+        if (insn.getOpcode() == MULTIANEWARRAY) {
+            size = 1;
+        } else {
+            size = Type.getReturnType(((MethodInsnNode) insn).desc).getSize();
+        }
+        return new DataflowValue(size, insn);
+    }
+
+    public Value merge(final Value v, final Value w) {
+        DataflowValue dv = (DataflowValue) v;
+        DataflowValue dw = (DataflowValue) w;
+        if (dv.insns instanceof SmallSet && dw.insns instanceof SmallSet) {
+            Set s = ((SmallSet) dv.insns).union((SmallSet) dw.insns);
+            if (s == dv.insns && dv.size == dw.size) {
+                return v;
+            } else {
+                return new DataflowValue(Math.min(dv.size, dw.size), s);
+            }
+        }
+        if (dv.size != dw.size || !dv.insns.containsAll(dw.insns)) {
+            Set s = new HashSet();
+            s.addAll(dv.insns);
+            s.addAll(dw.insns);
+            return new DataflowValue(Math.min(dv.size, dw.size), s);
+        }
+        return v;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/DataflowValue.java b/asmx/src/org/objectweb/asm/tree/analysis/DataflowValue.java
new file mode 100644
index 0000000..0019d7f
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/DataflowValue.java
@@ -0,0 +1,92 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.Set;
+
+import org.objectweb.asm.tree.AbstractInsnNode;
+
+/**
+ * A {@link Value} that is represented by its type in a two types type system.
+ * This type system distinguishes the ONEWORD and TWOWORDS types.
+ * 
+ * @author Eric Bruneton
+ */
+public class DataflowValue implements Value {
+
+    /**
+     * The size of this value.
+     */
+    public final int size;
+
+    /**
+     * The instructions that can produce this value. For example, for the Java
+     * code below, the instructions that can produce the value of <tt>i</tt>
+     * at line 5 are the txo ISTORE instructions at line 1 and 3:
+     * 
+     * <pre>
+     * 1: i = 0;
+     * 2: if (...) {
+     * 3:   i = 1;
+     * 4: }
+     * 5: return i;
+     * </pre>
+     * 
+     * This field is a set of {@link AbstractInsnNode} objects.
+     */
+    public final Set insns;
+
+    public DataflowValue(final int size) {
+        this(size, SmallSet.EMPTY_SET);
+    }
+
+    public DataflowValue(final int size, final AbstractInsnNode insn) {
+        this.size = size;
+        this.insns = new SmallSet(insn, null);
+    }
+
+    public DataflowValue(final int size, final Set insns) {
+        this.size = size;
+        this.insns = insns;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public boolean equals(final Object value) {
+        DataflowValue v = (DataflowValue) value;
+        return size == v.size && insns.equals(v.insns);
+    }
+
+    public int hashCode() {
+        return insns.hashCode();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Frame.java b/asmx/src/org/objectweb/asm/tree/analysis/Frame.java
new file mode 100644
index 0000000..bc2d873
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/Frame.java
@@ -0,0 +1,670 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.IincInsnNode;
+import org.objectweb.asm.tree.MethodInsnNode;
+import org.objectweb.asm.tree.MultiANewArrayInsnNode;
+import org.objectweb.asm.tree.VarInsnNode;
+
+/**
+ * A symbolic execution stack frame. A stack frame contains a set of local
+ * variable slots, and an operand stack. Warning: long and double values are
+ * represented by <i>two</i> slots in local variables, and by <i>one</i> slot
+ * in the operand stack.
+ * 
+ * @author Eric Bruneton
+ */
+public class Frame {
+
+    /**
+     * The local variables and operand stack of this frame.
+     */
+    private Value[] values;
+
+    /**
+     * The number of local variables of this frame.
+     */
+    private int locals;
+
+    /**
+     * The number of elements in the operand stack.
+     */
+    private int top;
+
+    /**
+     * Constructs a new frame with the given size.
+     * 
+     * @param nLocals the maximum number of local variables of the frame.
+     * @param nStack the maximum stack size of the frame.
+     */
+    public Frame(final int nLocals, final int nStack) {
+        this.values = new Value[nLocals + nStack];
+        this.locals = nLocals;
+    }
+
+    /**
+     * Constructs a new frame that is identical to the given frame.
+     * 
+     * @param src a frame.
+     */
+    public Frame(final Frame src) {
+        this(src.locals, src.values.length - src.locals);
+        init(src);
+    }
+
+    /**
+     * Copies the state of the given frame into this frame.
+     * 
+     * @param src a frame.
+     * @return this frame.
+     */
+    public Frame init(final Frame src) {
+        System.arraycopy(src.values, 0, values, 0, values.length);
+        top = src.top;
+        return this;
+    }
+
+    /**
+     * Returns the maximum number of local variables of this frame.
+     * 
+     * @return the maximum number of local variables of this frame.
+     */
+    public int getLocals() {
+        return locals;
+    }
+
+    /**
+     * Returns the value of the given local variable.
+     * 
+     * @param i a local variable index.
+     * @return the value of the given local variable.
+     * @throws IndexOutOfBoundsException if the variable does not exist.
+     */
+    public Value getLocal(final int i) throws IndexOutOfBoundsException {
+        if (i >= locals) {
+            throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
+        }
+        return values[i];
+    }
+
+    /**
+     * Sets the value of the given local variable.
+     * 
+     * @param i a local variable index.
+     * @param value the new value of this local variable.
+     * @throws IndexOutOfBoundsException if the variable does not exist.
+     */
+    public void setLocal(final int i, final Value value)
+            throws IndexOutOfBoundsException
+    {
+        if (i >= locals) {
+            throw new IndexOutOfBoundsException("Trying to access an inexistant local variable");
+        }
+        values[i] = value;
+    }
+
+    /**
+     * Returns the number of values in the operand stack of this frame. Long and
+     * double values are treated as single values.
+     * 
+     * @return the number of values in the operand stack of this frame.
+     */
+    public int getStackSize() {
+        return top;
+    }
+
+    /**
+     * Returns the value of the given operand stack slot.
+     * 
+     * @param i the index of an operand stack slot.
+     * @return the value of the given operand stack slot.
+     * @throws IndexOutOfBoundsException if the operand stack slot does not
+     *         exist.
+     */
+    public Value getStack(final int i) throws IndexOutOfBoundsException {
+        if (i >= top) {
+            throw new IndexOutOfBoundsException("Trying to access an inexistant stack element");
+        }
+        return values[i + locals];
+    }
+
+    /**
+     * Clears the operand stack of this frame.
+     */
+    public void clearStack() {
+        top = 0;
+    }
+
+    /**
+     * Pops a value from the operand stack of this frame.
+     * 
+     * @return the value that has been popped from the stack.
+     * @throws IndexOutOfBoundsException if the operand stack is empty.
+     */
+    public Value pop() throws IndexOutOfBoundsException {
+        if (top == 0) {
+            throw new IndexOutOfBoundsException("Cannot pop operand off an empty stack.");
+        }
+        return values[--top + locals];
+    }
+
+    /**
+     * Pushes a value into the operand stack of this frame.
+     * 
+     * @param value the value that must be pushed into the stack.
+     * @throws IndexOutOfBoundsException if the operand stack is full.
+     */
+    public void push(final Value value) throws IndexOutOfBoundsException {
+        if (top + locals >= values.length) {
+            throw new IndexOutOfBoundsException("Insufficient maximum stack size.");
+        }
+        values[top++ + locals] = value;
+    }
+
+    public void execute(
+        final AbstractInsnNode insn,
+        final Interpreter interpreter) throws AnalyzerException
+    {
+        Value value1, value2, value3, value4;
+        List values;
+        int var;
+
+        switch (insn.getOpcode()) {
+            case Opcodes.NOP:
+                break;
+            case Opcodes.ACONST_NULL:
+            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.LCONST_0:
+            case Opcodes.LCONST_1:
+            case Opcodes.FCONST_0:
+            case Opcodes.FCONST_1:
+            case Opcodes.FCONST_2:
+            case Opcodes.DCONST_0:
+            case Opcodes.DCONST_1:
+            case Opcodes.BIPUSH:
+            case Opcodes.SIPUSH:
+            case Opcodes.LDC:
+                push(interpreter.newOperation(insn));
+                break;
+            case Opcodes.ILOAD:
+            case Opcodes.LLOAD:
+            case Opcodes.FLOAD:
+            case Opcodes.DLOAD:
+            case Opcodes.ALOAD:
+                push(interpreter.copyOperation(insn,
+                        getLocal(((VarInsnNode) insn).var)));
+                break;
+            case Opcodes.IALOAD:
+            case Opcodes.LALOAD:
+            case Opcodes.FALOAD:
+            case Opcodes.DALOAD:
+            case Opcodes.AALOAD:
+            case Opcodes.BALOAD:
+            case Opcodes.CALOAD:
+            case Opcodes.SALOAD:
+                value2 = pop();
+                value1 = pop();
+                push(interpreter.binaryOperation(insn, value1, value2));
+                break;
+            case Opcodes.ISTORE:
+            case Opcodes.LSTORE:
+            case Opcodes.FSTORE:
+            case Opcodes.DSTORE:
+            case Opcodes.ASTORE:
+                value1 = interpreter.copyOperation(insn, pop());
+                var = ((VarInsnNode) insn).var;
+                setLocal(var, value1);
+                if (value1.getSize() == 2) {
+                    setLocal(var + 1, interpreter.newValue(null));
+                }
+                if (var > 0) {
+                    Value local = getLocal(var - 1);
+                    if (local != null && local.getSize() == 2) {
+                        setLocal(var + 1, interpreter.newValue(null));
+                    }
+                }
+                break;
+            case Opcodes.IASTORE:
+            case Opcodes.LASTORE:
+            case Opcodes.FASTORE:
+            case Opcodes.DASTORE:
+            case Opcodes.AASTORE:
+            case Opcodes.BASTORE:
+            case Opcodes.CASTORE:
+            case Opcodes.SASTORE:
+                value3 = pop();
+                value2 = pop();
+                value1 = pop();
+                interpreter.ternaryOperation(insn, value1, value2, value3);
+                break;
+            case Opcodes.POP:
+                if (pop().getSize() == 2) {
+                    throw new AnalyzerException("Illegal use of POP");
+                }
+                break;
+            case Opcodes.POP2:
+                if (pop().getSize() == 1) {
+                    if (pop().getSize() != 1) {
+                        throw new AnalyzerException("Illegal use of POP2");
+                    }
+                }
+                break;
+            case Opcodes.DUP:
+                value1 = pop();
+                if (value1.getSize() != 1) {
+                    throw new AnalyzerException("Illegal use of DUP");
+                }
+                push(interpreter.copyOperation(insn, value1));
+                push(interpreter.copyOperation(insn, value1));
+                break;
+            case Opcodes.DUP_X1:
+                value1 = pop();
+                value2 = pop();
+                if (value1.getSize() != 1 || value2.getSize() != 1) {
+                    throw new AnalyzerException("Illegal use of DUP_X1");
+                }
+                push(interpreter.copyOperation(insn, value1));
+                push(interpreter.copyOperation(insn, value2));
+                push(interpreter.copyOperation(insn, value1));
+                break;
+            case Opcodes.DUP_X2:
+                value1 = pop();
+                if (value1.getSize() == 1) {
+                    value2 = pop();
+                    if (value2.getSize() == 1) {
+                        value3 = pop();
+                        if (value3.getSize() == 1) {
+                            push(interpreter.copyOperation(insn, value1));
+                            push(interpreter.copyOperation(insn, value3));
+                            push(interpreter.copyOperation(insn, value2));
+                            push(interpreter.copyOperation(insn, value1));
+                            break;
+                        }
+                    } else {
+                        push(interpreter.copyOperation(insn, value1));
+                        push(interpreter.copyOperation(insn, value2));
+                        push(interpreter.copyOperation(insn, value1));
+                        break;
+                    }
+                }
+                throw new AnalyzerException("Illegal use of DUP_X2");
+            case Opcodes.DUP2:
+                value1 = pop();
+                if (value1.getSize() == 1) {
+                    value2 = pop();
+                    if (value2.getSize() == 1) {
+                        push(interpreter.copyOperation(insn, value2));
+                        push(interpreter.copyOperation(insn, value1));
+                        push(interpreter.copyOperation(insn, value2));
+                        push(interpreter.copyOperation(insn, value1));
+                        break;
+                    }
+                } else {
+                    push(interpreter.copyOperation(insn, value1));
+                    push(interpreter.copyOperation(insn, value1));
+                    break;
+                }
+                throw new AnalyzerException("Illegal use of DUP2");
+            case Opcodes.DUP2_X1:
+                value1 = pop();
+                if (value1.getSize() == 1) {
+                    value2 = pop();
+                    if (value2.getSize() == 1) {
+                        value3 = pop();
+                        if (value3.getSize() == 1) {
+                            push(interpreter.copyOperation(insn, value2));
+                            push(interpreter.copyOperation(insn, value1));
+                            push(interpreter.copyOperation(insn, value3));
+                            push(interpreter.copyOperation(insn, value2));
+                            push(interpreter.copyOperation(insn, value1));
+                            break;
+                        }
+                    }
+                } else {
+                    value2 = pop();
+                    if (value2.getSize() == 1) {
+                        push(interpreter.copyOperation(insn, value1));
+                        push(interpreter.copyOperation(insn, value2));
+                        push(interpreter.copyOperation(insn, value1));
+                        break;
+                    }
+                }
+                throw new AnalyzerException("Illegal use of DUP2_X1");
+            case Opcodes.DUP2_X2:
+                value1 = pop();
+                if (value1.getSize() == 1) {
+                    value2 = pop();
+                    if (value2.getSize() == 1) {
+                        value3 = pop();
+                        if (value3.getSize() == 1) {
+                            value4 = pop();
+                            if (value4.getSize() == 1) {
+                                push(interpreter.copyOperation(insn, value2));
+                                push(interpreter.copyOperation(insn, value1));
+                                push(interpreter.copyOperation(insn, value4));
+                                push(interpreter.copyOperation(insn, value3));
+                                push(interpreter.copyOperation(insn, value2));
+                                push(interpreter.copyOperation(insn, value1));
+                                break;
+                            }
+                        } else {
+                            push(interpreter.copyOperation(insn, value2));
+                            push(interpreter.copyOperation(insn, value1));
+                            push(interpreter.copyOperation(insn, value3));
+                            push(interpreter.copyOperation(insn, value2));
+                            push(interpreter.copyOperation(insn, value1));
+                            break;
+                        }
+                    }
+                } else {
+                    value2 = pop();
+                    if (value2.getSize() == 1) {
+                        value3 = pop();
+                        if (value3.getSize() == 1) {
+                            push(interpreter.copyOperation(insn, value1));
+                            push(interpreter.copyOperation(insn, value3));
+                            push(interpreter.copyOperation(insn, value2));
+                            push(interpreter.copyOperation(insn, value1));
+                            break;
+                        }
+                    } else {
+                        push(interpreter.copyOperation(insn, value1));
+                        push(interpreter.copyOperation(insn, value2));
+                        push(interpreter.copyOperation(insn, value1));
+                        break;
+                    }
+                }
+                throw new AnalyzerException("Illegal use of DUP2_X2");
+            case Opcodes.SWAP:
+                value2 = pop();
+                value1 = pop();
+                if (value1.getSize() != 1 || value2.getSize() != 1) {
+                    throw new AnalyzerException("Illegal use of SWAP");
+                }
+                push(interpreter.copyOperation(insn, value2));
+                push(interpreter.copyOperation(insn, value1));
+                break;
+            case Opcodes.IADD:
+            case Opcodes.LADD:
+            case Opcodes.FADD:
+            case Opcodes.DADD:
+            case Opcodes.ISUB:
+            case Opcodes.LSUB:
+            case Opcodes.FSUB:
+            case Opcodes.DSUB:
+            case Opcodes.IMUL:
+            case Opcodes.LMUL:
+            case Opcodes.FMUL:
+            case Opcodes.DMUL:
+            case Opcodes.IDIV:
+            case Opcodes.LDIV:
+            case Opcodes.FDIV:
+            case Opcodes.DDIV:
+            case Opcodes.IREM:
+            case Opcodes.LREM:
+            case Opcodes.FREM:
+            case Opcodes.DREM:
+                value2 = pop();
+                value1 = pop();
+                push(interpreter.binaryOperation(insn, value1, value2));
+                break;
+            case Opcodes.INEG:
+            case Opcodes.LNEG:
+            case Opcodes.FNEG:
+            case Opcodes.DNEG:
+                push(interpreter.unaryOperation(insn, pop()));
+                break;
+            case Opcodes.ISHL:
+            case Opcodes.LSHL:
+            case Opcodes.ISHR:
+            case Opcodes.LSHR:
+            case Opcodes.IUSHR:
+            case Opcodes.LUSHR:
+            case Opcodes.IAND:
+            case Opcodes.LAND:
+            case Opcodes.IOR:
+            case Opcodes.LOR:
+            case Opcodes.IXOR:
+            case Opcodes.LXOR:
+                value2 = pop();
+                value1 = pop();
+                push(interpreter.binaryOperation(insn, value1, value2));
+                break;
+            case Opcodes.IINC:
+                var = ((IincInsnNode) insn).var;
+                setLocal(var, interpreter.unaryOperation(insn, getLocal(var)));
+                break;
+            case Opcodes.I2L:
+            case Opcodes.I2F:
+            case Opcodes.I2D:
+            case Opcodes.L2I:
+            case Opcodes.L2F:
+            case Opcodes.L2D:
+            case Opcodes.F2I:
+            case Opcodes.F2L:
+            case Opcodes.F2D:
+            case Opcodes.D2I:
+            case Opcodes.D2L:
+            case Opcodes.D2F:
+            case Opcodes.I2B:
+            case Opcodes.I2C:
+            case Opcodes.I2S:
+                push(interpreter.unaryOperation(insn, pop()));
+                break;
+            case Opcodes.LCMP:
+            case Opcodes.FCMPL:
+            case Opcodes.FCMPG:
+            case Opcodes.DCMPL:
+            case Opcodes.DCMPG:
+                value2 = pop();
+                value1 = pop();
+                push(interpreter.binaryOperation(insn, value1, value2));
+                break;
+            case Opcodes.IFEQ:
+            case Opcodes.IFNE:
+            case Opcodes.IFLT:
+            case Opcodes.IFGE:
+            case Opcodes.IFGT:
+            case Opcodes.IFLE:
+                interpreter.unaryOperation(insn, pop());
+                break;
+            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:
+                value2 = pop();
+                value1 = pop();
+                interpreter.binaryOperation(insn, value1, value2);
+                break;
+            case Opcodes.GOTO:
+                break;
+            case Opcodes.JSR:
+                push(interpreter.newOperation(insn));
+                break;
+            case Opcodes.RET:
+                break;
+            case Opcodes.TABLESWITCH:
+            case Opcodes.LOOKUPSWITCH:
+            case Opcodes.IRETURN:
+            case Opcodes.LRETURN:
+            case Opcodes.FRETURN:
+            case Opcodes.DRETURN:
+            case Opcodes.ARETURN:
+                interpreter.unaryOperation(insn, pop());
+                break;
+            case Opcodes.RETURN:
+                break;
+            case Opcodes.GETSTATIC:
+                push(interpreter.newOperation(insn));
+                break;
+            case Opcodes.PUTSTATIC:
+                interpreter.unaryOperation(insn, pop());
+                break;
+            case Opcodes.GETFIELD:
+                push(interpreter.unaryOperation(insn, pop()));
+                break;
+            case Opcodes.PUTFIELD:
+                value2 = pop();
+                value1 = pop();
+                interpreter.binaryOperation(insn, value1, value2);
+                break;
+            case Opcodes.INVOKEVIRTUAL:
+            case Opcodes.INVOKESPECIAL:
+            case Opcodes.INVOKESTATIC:
+            case Opcodes.INVOKEINTERFACE:
+                values = new ArrayList();
+                String desc = ((MethodInsnNode) insn).desc;
+                for (int i = Type.getArgumentTypes(desc).length; i > 0; --i) {
+                    values.add(0, pop());
+                }
+                if (insn.getOpcode() != Opcodes.INVOKESTATIC) {
+                    values.add(0, pop());
+                }
+                if (Type.getReturnType(desc) == Type.VOID_TYPE) {
+                    interpreter.naryOperation(insn, values);
+                } else {
+                    push(interpreter.naryOperation(insn, values));
+                }
+                break;
+            case Opcodes.NEW:
+                push(interpreter.newOperation(insn));
+                break;
+            case Opcodes.NEWARRAY:
+            case Opcodes.ANEWARRAY:
+            case Opcodes.ARRAYLENGTH:
+                push(interpreter.unaryOperation(insn, pop()));
+                break;
+            case Opcodes.ATHROW:
+                interpreter.unaryOperation(insn, pop());
+                break;
+            case Opcodes.CHECKCAST:
+            case Opcodes.INSTANCEOF:
+                push(interpreter.unaryOperation(insn, pop()));
+                break;
+            case Opcodes.MONITORENTER:
+            case Opcodes.MONITOREXIT:
+                interpreter.unaryOperation(insn, pop());
+                break;
+            case Opcodes.MULTIANEWARRAY:
+                values = new ArrayList();
+                for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) {
+                    values.add(0, pop());
+                }
+                push(interpreter.naryOperation(insn, values));
+                break;
+            case Opcodes.IFNULL:
+            case Opcodes.IFNONNULL:
+                interpreter.unaryOperation(insn, pop());
+                break;
+            default:
+                throw new RuntimeException("Illegal opcode");
+        }
+    }
+
+    /**
+     * Merges this frame with the given frame.
+     * 
+     * @param frame a frame.
+     * @param interpreter the interpreter used to merge values.
+     * @return <tt>true</tt> if this frame has been changed as a result of the
+     *         merge operation, or <tt>false</tt> otherwise.
+     * @throws AnalyzerException if the frames have incompatible sizes.
+     */
+    public boolean merge(final Frame frame, final Interpreter interpreter)
+            throws AnalyzerException
+    {
+        if (top != frame.top) {
+            throw new AnalyzerException("Incompatible stack heights");
+        }
+        boolean changes = false;
+        for (int i = 0; i < locals + top; ++i) {
+            Value v = interpreter.merge(values[i], frame.values[i]);
+            if (v != values[i]) {
+                values[i] = v;
+                changes |= true;
+            }
+        }
+        return changes;
+    }
+
+    /**
+     * Merges this frame with the given frame (case of a RET instruction).
+     * 
+     * @param frame a frame
+     * @param access the local variables that have been accessed by the
+     *        subroutine to which the RET instruction corresponds.
+     * @return <tt>true</tt> if this frame has been changed as a result of the
+     *         merge operation, or <tt>false</tt> otherwise.
+     */
+    public boolean merge(final Frame frame, final boolean[] access) {
+        boolean changes = false;
+        for (int i = 0; i < locals; ++i) {
+            if (!access[i] && !values[i].equals(frame.values[i])) {
+                values[i] = frame.values[i];
+                changes = true;
+            }
+        }
+        return changes;
+    }
+
+    /**
+     * Returns a string representation of this frame.
+     * 
+     * @return a string representation of this frame.
+     */
+    public String toString() {
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < locals; ++i) {
+            b.append(values[i]).append(' ');
+        }
+        b.append(' ');
+        for (int i = 0; i < top; ++i) {
+            b.append(values[i + locals].toString()).append(' ');
+        }
+        return b.toString();
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/IntMap.java b/asmx/src/org/objectweb/asm/tree/analysis/IntMap.java
new file mode 100644
index 0000000..9528482
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/IntMap.java
@@ -0,0 +1,73 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+/**
+ * A fixed size map of integer values.
+ * 
+ * @author Eric Bruneton
+ */
+class IntMap {
+
+    private int size;
+
+    private Object[] keys;
+
+    private int[] values;
+
+    public IntMap(final int size) {
+        this.size = size;
+        this.keys = new Object[size];
+        this.values = new int[size];
+    }
+
+    public int get(final Object key) {
+        int n = size;
+        int h = (key.hashCode() & 0x7FFFFFFF) % n; 
+        int i = h;
+        while (keys[i] != key) {
+            i = (i + 1) % n;
+            if (i == h) {
+                throw new RuntimeException("Cannot find index of " + key);
+            }
+        }
+        return values[i];
+    }
+
+    public void put(final Object key, final int value) {
+        int n = size;
+        int i = (key.hashCode() & 0x7FFFFFFF) % n;
+        while (keys[i] != null) {
+            i = (i + 1) % n;
+        }
+        keys[i] = key;
+        values[i] = value;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Interpreter.java b/asmx/src/org/objectweb/asm/tree/analysis/Interpreter.java
new file mode 100644
index 0000000..f7a2f53
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/Interpreter.java
@@ -0,0 +1,178 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.List;
+
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+
+/**
+ * A semantic bytecode interpreter. More precisely, this interpreter only
+ * manages the computation of values from other values: it does not manage the
+ * transfer of values to or from the stack, and to or from the local variables.
+ * This separation allows a generic bytecode {@link Analyzer} to work with
+ * various semantic interpreters, without needing to duplicate the code to
+ * simulate the transfer of values.
+ * 
+ * @author Eric Bruneton
+ */
+public interface Interpreter {
+
+    /**
+     * Creates a new value that represents the given type.
+     * 
+     * Called for method parameters (including <code>this</code>),
+     * exception handler variable and with <code>null</code> type 
+     * for variables reserved by long and double types.
+     * 
+     * @param type a primitive or reference type, or <tt>null</tt> to
+     *        represent an uninitialized value.
+     * @return a value that represents the given type. The size of the returned
+     *         value must be equal to the size of the given type.
+     */
+    Value newValue(Type type);
+
+    /**
+     * Interprets a bytecode instruction without arguments. This method is
+     * called for the following opcodes:
+     * 
+     * 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, BIPUSH, SIPUSH, LDC, JSR, GETSTATIC, NEW
+     * 
+     * @param insn the bytecode instruction to be interpreted.
+     * @return the result of the interpretation of the given instruction.
+     * @throws AnalyzerException if an error occured during the interpretation.
+     */
+    Value newOperation(AbstractInsnNode insn) throws AnalyzerException;
+
+    /**
+     * Interprets a bytecode instruction that moves a value on the stack or to
+     * or from local variables. This method is called for the following opcodes:
+     * 
+     * ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE,
+     * ASTORE, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP
+     * 
+     * @param insn the bytecode instruction to be interpreted.
+     * @param value the value that must be moved by the instruction.
+     * @return the result of the interpretation of the given instruction. The
+     *         returned value must be <tt>equal</tt> to the given value.
+     * @throws AnalyzerException if an error occured during the interpretation.
+     */
+    Value copyOperation(AbstractInsnNode insn, Value value)
+            throws AnalyzerException;
+
+    /**
+     * Interprets a bytecode instruction with a single argument. This method is
+     * called for the following opcodes:
+     * 
+     * INEG, LNEG, FNEG, DNEG, IINC, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L,
+     * F2D, D2I, D2L, D2F, I2B, I2C, I2S, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE,
+     * TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN,
+     * PUTSTATIC, GETFIELD, NEWARRAY, ANEWARRAY, ARRAYLENGTH, ATHROW, CHECKCAST,
+     * INSTANCEOF, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL
+     * 
+     * @param insn the bytecode instruction to be interpreted.
+     * @param value the argument of the instruction to be interpreted.
+     * @return the result of the interpretation of the given instruction.
+     * @throws AnalyzerException if an error occured during the interpretation.
+     */
+    Value unaryOperation(AbstractInsnNode insn, Value value)
+            throws AnalyzerException;
+
+    /**
+     * Interprets a bytecode instruction with two arguments. This method is
+     * called for the following opcodes:
+     * 
+     * IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IADD,
+     * LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV,
+     * LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, ISHL, LSHL, ISHR, LSHR, IUSHR,
+     * LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, LCMP, FCMPL, FCMPG, DCMPL,
+     * DCMPG, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE,
+     * IF_ACMPEQ, IF_ACMPNE, PUTFIELD
+     * 
+     * @param insn the bytecode instruction to be interpreted.
+     * @param value1 the first argument of the instruction to be interpreted.
+     * @param value2 the second argument of the instruction to be interpreted.
+     * @return the result of the interpretation of the given instruction.
+     * @throws AnalyzerException if an error occured during the interpretation.
+     */
+    Value binaryOperation(AbstractInsnNode insn, Value value1, Value value2)
+            throws AnalyzerException;
+
+    /**
+     * Interprets a bytecode instruction with three arguments. This method is
+     * called for the following opcodes:
+     * 
+     * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE
+     * 
+     * @param insn the bytecode instruction to be interpreted.
+     * @param value1 the first argument of the instruction to be interpreted.
+     * @param value2 the second argument of the instruction to be interpreted.
+     * @param value3 the third argument of the instruction to be interpreted.
+     * @return the result of the interpretation of the given instruction.
+     * @throws AnalyzerException if an error occured during the interpretation.
+     */
+    Value ternaryOperation(
+        AbstractInsnNode insn,
+        Value value1,
+        Value value2,
+        Value value3) throws AnalyzerException;
+
+    /**
+     * Interprets a bytecode instruction with a variable number of arguments.
+     * This method is called for the following opcodes:
+     * 
+     * INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE,
+     * MULTIANEWARRAY
+     * 
+     * @param insn the bytecode instruction to be interpreted.
+     * @param values the arguments of the instruction to be interpreted.
+     * @return the result of the interpretation of the given instruction.
+     * @throws AnalyzerException if an error occured during the interpretation.
+     */
+    Value naryOperation(AbstractInsnNode insn, List values)
+            throws AnalyzerException;
+
+    /**
+     * Merges two values. The merge operation must return a value that
+     * represents both values (for instance, if the two values are two types,
+     * the merged value must be a common super type of the two types. If the two
+     * values are integer intervals, the merged value must be an interval that
+     * contains the previous ones. Likewise for other types of values).
+     * 
+     * @param v a value.
+     * @param w another value.
+     * @return the merged value. If the merged value is equal to <tt>v</tt>,
+     *         this method <i>must</i> return <tt>v</tt>.
+     */
+    Value merge(Value v, Value w);
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/SimpleVerifier.java b/asmx/src/org/objectweb/asm/tree/analysis/SimpleVerifier.java
new file mode 100644
index 0000000..808b3f4
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/SimpleVerifier.java
@@ -0,0 +1,266 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.List;
+
+import org.objectweb.asm.Type;
+
+/**
+ * An extended {@link BasicVerifier} that performs more precise verifications.
+ * This verifier computes exact class types, instead of using a single "object
+ * reference" type (as done in the {@link BasicVerifier}).
+ * 
+ * @author Eric Bruneton
+ * @author Bing Ran
+ */
+public class SimpleVerifier extends BasicVerifier {
+
+    /**
+     * The class that is verified.
+     */
+    private final Type currentClass;
+
+    /**
+     * The super class of the class that is verified.
+     */
+    private final Type currentSuperClass;
+
+    /**
+     * The interfaces implemented by the class that is verified.
+     */
+    private final List currentClassInterfaces;
+
+    /**
+     * If the class that is verified is an interface.
+     */
+    private final boolean isInterface;
+
+    /**
+     * Constructs a new {@link SimpleVerifier}.
+     */
+    public SimpleVerifier() {
+        this(null, null, false);
+    }
+
+    /**
+     * Constructs a new {@link SimpleVerifier} to verify a specific class. This
+     * class will not be loaded into the JVM since it may be incorrect.
+     * 
+     * @param currentClass the class that is verified.
+     * @param currentSuperClass the super class of the class that is verified.
+     * @param isInterface if the class that is verified is an interface.
+     */
+    public SimpleVerifier(
+        final Type currentClass,
+        final Type currentSuperClass,
+        final boolean isInterface)
+    {
+        this(currentClass, currentSuperClass, null, isInterface);
+    }
+
+    /**
+     * Constructs a new {@link SimpleVerifier} to verify a specific class. This
+     * class will not be loaded into the JVM since it may be incorrect.
+     * 
+     * @param currentClass the class that is verified.
+     * @param currentSuperClass the super class of the class that is verified.
+     * @param currentClassInterfaces the interfaces implemented by the class
+     *        that is verified.
+     * @param isInterface if the class that is verified is an interface.
+     */
+    public SimpleVerifier(
+        final Type currentClass,
+        final Type currentSuperClass,
+        final List currentClassInterfaces,
+        final boolean isInterface)
+    {
+        this.currentClass = currentClass;
+        this.currentSuperClass = currentSuperClass;
+        this.currentClassInterfaces = currentClassInterfaces;
+        this.isInterface = isInterface;
+    }
+
+    public Value newValue(final Type type) {
+        Value v = super.newValue(type);
+        if (v == BasicValue.REFERENCE_VALUE) {
+            v = new BasicValue(type);
+        }
+        return v;
+    }
+
+    protected boolean isArrayValue(final Value value) {
+        Type t = ((BasicValue) value).getType();
+        if (t != null) {
+            return t.getDescriptor().equals("Lnull;")
+                    || t.getSort() == Type.ARRAY;
+        }
+        return false;
+    }
+
+    protected Value getElementValue(final Value objectArrayValue)
+            throws AnalyzerException
+    {
+        Type arrayType = ((BasicValue) objectArrayValue).getType();
+        if (arrayType != null) {
+            if (arrayType.getSort() == Type.ARRAY) {
+                return newValue(Type.getType(arrayType.getDescriptor()
+                        .substring(1)));
+            } else if (arrayType.getDescriptor().equals("Lnull;")) {
+                return objectArrayValue;
+            }
+        }
+        throw new AnalyzerException("Not an array type");
+    }
+
+    protected boolean isSubTypeOf(final Value value, final Value expected) {
+        Type expectedType = ((BasicValue) expected).getType();
+        Type type = ((BasicValue) value).getType();
+        if (expectedType == null) {
+            return type == null;
+        }
+        switch (expectedType.getSort()) {
+            case Type.INT:
+            case Type.FLOAT:
+            case Type.LONG:
+            case Type.DOUBLE:
+                return type == expectedType;
+            case Type.ARRAY:
+            case Type.OBJECT:
+                if (expectedType.getDescriptor().equals("Lnull;")) {
+                    return type.getSort() == Type.OBJECT
+                            || type.getSort() == Type.ARRAY;
+                }
+                if (type.getDescriptor().equals("Lnull;")) {
+                    return true;
+                } else if (type.getSort() == Type.OBJECT
+                        || type.getSort() == Type.ARRAY)
+                {
+                    return isAssignableFrom(expectedType, type);
+                } else {
+                    return false;
+                }
+            default:
+                throw new RuntimeException("Internal error");
+        }
+    }
+
+    public Value merge(final Value v, final Value w) {
+        if (!v.equals(w)) {
+            Type t = ((BasicValue) v).getType();
+            Type u = ((BasicValue) w).getType();
+            if (t != null
+                    && (t.getSort() == Type.OBJECT || t.getSort() == Type.ARRAY))
+            {
+                if (u != null
+                        && (u.getSort() == Type.OBJECT || u.getSort() == Type.ARRAY))
+                {
+                    if (t.getDescriptor().equals("Lnull;")) {
+                        return w;
+                    }
+                    if (u.getDescriptor().equals("Lnull;")) {
+                        return v;
+                    }
+                    if (isAssignableFrom(t, u)) {
+                        return v;
+                    }
+                    if (isAssignableFrom(u, t)) {
+                        return w;
+                    }
+                    // TODO case of array classes of the same dimension
+                    // TODO should we look also for a common super interface?
+                    // problem: there may be several possible common super
+                    // interfaces
+                    do {
+                        if (t == null || isInterface(t)) {
+                            return BasicValue.REFERENCE_VALUE;
+                        }
+                        t = getSuperClass(t);
+                        if (isAssignableFrom(t, u)) {
+                            return newValue(t);
+                        }
+                    } while (true);
+                }
+            }
+            return BasicValue.UNINITIALIZED_VALUE;
+        }
+        return v;
+    }
+
+    private boolean isInterface(final Type t) {
+        if (currentClass != null && t.equals(currentClass)) {
+            return isInterface;
+        }
+        return getClass(t).isInterface();
+    }
+
+    private Type getSuperClass(final Type t) {
+        if (currentClass != null && t.equals(currentClass)) {
+            return currentSuperClass;
+        }
+        Class c = getClass(t).getSuperclass();
+        return c == null ? null : Type.getType(c);
+    }
+
+    private boolean isAssignableFrom(final Type t, final Type u) {
+        if (t.equals(u)) {
+            return true;
+        }
+        if (currentClass != null && t.equals(currentClass)) {
+            return isAssignableFrom(t, getSuperClass(u));
+        }
+        if (currentClass != null && u.equals(currentClass)) {
+            if (isAssignableFrom(t, currentSuperClass)) {
+                return true;
+            }
+            if (currentClassInterfaces != null) {
+                for (int i = 0; i < currentClassInterfaces.size(); ++i) {
+                    Type v = (Type) currentClassInterfaces.get(i);
+                    if (isAssignableFrom(t, v)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+        return getClass(t).isAssignableFrom(getClass(u));
+    }
+
+    protected Class getClass(final Type t) {
+        try {
+            if (t.getSort() == Type.ARRAY) {
+                return Class.forName(t.getDescriptor().replace('/', '.'));
+            }
+            return Class.forName(t.getClassName());
+        } catch (ClassNotFoundException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/SmallSet.java b/asmx/src/org/objectweb/asm/tree/analysis/SmallSet.java
new file mode 100644
index 0000000..74bd590
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/SmallSet.java
@@ -0,0 +1,126 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.AbstractSet;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A set of at most two elements.
+ * 
+ * @author Eric Bruneton
+ */
+class SmallSet extends AbstractSet implements Iterator {
+
+    // if e1 is null, e2 must be null; otherwise e2 must be different from e1
+
+    Object e1, e2;
+
+    final static SmallSet EMPTY_SET = new SmallSet(null, null);
+
+    SmallSet(Object e1, Object e2) {
+        this.e1 = e1;
+        this.e2 = e2;
+    }
+
+    // -------------------------------------------------------------------------
+    // Implementation of inherited abstract methods
+    // -------------------------------------------------------------------------
+
+    public Iterator iterator() {
+        return new SmallSet(e1, e2);
+    }
+
+    public int size() {
+        return e1 == null ? 0 : (e2 == null ? 1 : 2);
+    }
+
+    // -------------------------------------------------------------------------
+    // Implementation of the Iterator interface
+    // -------------------------------------------------------------------------
+
+    public boolean hasNext() {
+        return e1 != null;
+    }
+
+    public Object next() {
+        Object e = e1;
+        e1 = e2;
+        e2 = null;
+        return e;
+    }
+
+    public void remove() {
+    }
+
+    // -------------------------------------------------------------------------
+    // Utility methods
+    // -------------------------------------------------------------------------
+
+    Set union(SmallSet s) {
+        if ((s.e1 == e1 && s.e2 == e2) || (s.e1 == e2 && s.e2 == e1)) {
+            return this; // if the two sets are equal, return this
+        }
+        if (s.e1 == null) {
+            return this; // if s is empty, return this
+        }
+        if (e1 == null) {
+            return s; // if this is empty, return s
+        }
+        if (s.e2 == null) { // s contains exactly one element
+            if (e2 == null) {
+                return new SmallSet(e1, s.e1); // necessarily e1 != s.e1
+            } else if (s.e1 == e1 || s.e1 == e2) { // s is included in this
+                return this;
+            }
+        }
+        if (e2 == null) { // this contains exactly one element
+            // if (s.e2 == null) { // cannot happen
+            // return new SmallSet(e1, s.e1); // necessarily e1 != s.e1
+            // } else
+            if (e1 == s.e1 || e1 == s.e2) { // this in included in s
+                return s;
+            }
+        }
+        // here we know that there are at least 3 distinct elements
+        HashSet r = new HashSet(4);
+        r.add(e1);
+        if (e2 != null) {
+            r.add(e2);
+        }
+        r.add(s.e1);
+        if (s.e2 != null) {
+            r.add(s.e2);
+        }
+        return r;
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Subroutine.java b/asmx/src/org/objectweb/asm/tree/analysis/Subroutine.java
new file mode 100644
index 0000000..44120a7
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/Subroutine.java
@@ -0,0 +1,96 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.tree.JumpInsnNode;
+
+/**
+ * A method subroutine (corresponds to a JSR instruction).
+ * 
+ * @author Eric Bruneton
+ */
+class Subroutine {
+
+    Label start;
+
+    boolean[] access;
+
+    List callers;
+
+    private Subroutine() {
+    }
+
+    public Subroutine(
+        final Label start,
+        final int maxLocals,
+        final JumpInsnNode caller)
+    {
+        this.start = start;
+        this.access = new boolean[maxLocals];
+        this.callers = new ArrayList();
+        callers.add(caller);
+    }
+
+    public Subroutine copy() {
+        Subroutine result = new Subroutine();
+        result.start = start;
+        result.access = new boolean[access.length];
+        System.arraycopy(access, 0, result.access, 0, access.length);
+        result.callers = new ArrayList(callers);
+        return result;
+    }
+
+    public boolean merge(final Subroutine subroutine, boolean checkOverlap)
+            throws AnalyzerException
+    {
+        if (checkOverlap && subroutine.start != start) {
+            throw new AnalyzerException("Overlapping sub routines");
+        }
+        boolean changes = false;
+        for (int i = 0; i < access.length; ++i) {
+            if (subroutine.access[i] && !access[i]) {
+                access[i] = true;
+                changes = true;
+            }
+        }
+        for (int i = 0; i < subroutine.callers.size(); ++i) {
+            Object caller = subroutine.callers.get(i);
+            if (!callers.contains(caller)) {
+                callers.add(caller);
+                changes = true;
+            }
+        }
+        return changes;
+    }
+}
\ No newline at end of file
diff --git a/asmx/src/org/objectweb/asm/tree/analysis/Value.java b/asmx/src/org/objectweb/asm/tree/analysis/Value.java
new file mode 100644
index 0000000..6578b79
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/tree/analysis/Value.java
@@ -0,0 +1,45 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.tree.analysis;
+
+/**
+ * An immutable symbolic value for semantic interpretation of bytecode.
+ * 
+ * @author Eric Bruneton
+ */
+public interface Value {
+
+    /**
+     * Returns the size of this value in words.
+     * 
+     * @return either 1 or 2.
+     */
+    int getSize();
+}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierAbstractVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierAbstractVisitor.java
new file mode 100644
index 0000000..a621529
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/ASMifierAbstractVisitor.java
@@ -0,0 +1,256 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import java.util.HashMap;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.util.attrs.ASMifiable;
+
+/**
+ * An abstract ASMifier visitor.
+ * 
+ * @author Eric Bruneton
+ */
+public class ASMifierAbstractVisitor extends AbstractVisitor {
+
+    /**
+     * The name of the variable for this visitor in the produced code.
+     */
+    protected String name;
+
+    /**
+     * The label names. This map associates String values to Label keys. It is
+     * used only in ASMifierMethodVisitor.
+     */
+    HashMap labelNames;
+
+    /**
+     * Constructs a new {@link ASMifierAbstractVisitor}.
+     * 
+     * @param name the name of the variable for this visitor in the produced
+     *        code.
+     */
+    protected ASMifierAbstractVisitor(final String name) {
+        this.name = name;
+    }
+
+    /**
+     * Prints the ASM code that generates the given annotation.
+     * 
+     * @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.
+     */
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        buf.setLength(0);
+        buf.append("{\n")
+                .append("av0 = ")
+                .append(name)
+                .append(".visitAnnotation(");
+        appendConstant(desc);
+        buf.append(", ").append(visible).append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    /**
+     * Prints the ASM code that generates the given extended annotation.
+     * 
+     * @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.
+     */
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible,
+        final boolean inCode)
+    {
+        buf.setLength(0);
+        buf.append("{\n")
+                .append("xav0 = ")
+                .append(name)
+                .append(".visitTypeAnnotation(");
+        appendConstant(desc);
+        buf.append(", ").append(visible);
+        buf.append(", ").append(inCode).append(");\n");
+        text.add(buf.toString());
+        ASMifierTypeAnnotationVisitor xav = 
+          new ASMifierTypeAnnotationVisitor(0);
+        text.add(xav.getText());
+        text.add("}\n");
+        return xav;
+    }
+    
+    
+    /**
+     * Prints the ASM code that generates the given attribute.
+     * 
+     * @param attr an attribute.
+     */
+    public void visitAttribute(final Attribute attr) {
+        buf.setLength(0);
+        if (attr instanceof ASMifiable) {
+            buf.append("{\n");
+            buf.append("// ATTRIBUTE\n");
+            ((ASMifiable) attr).asmify(buf, "attr", labelNames);
+            buf.append(name).append(".visitAttribute(attr);\n");
+            buf.append("}\n");
+        } else {
+            buf.append("// WARNING! skipped a non standard attribute of type \"");
+            buf.append(attr.type).append("\"\n");
+        }
+        text.add(buf.toString());
+    }
+
+    /**
+     * Prints the ASM code to end the visit.
+     */
+    public void visitEnd() {
+        buf.setLength(0);
+        buf.append(name).append(".visitEnd();\n");
+        text.add(buf.toString());
+    }
+
+    /**
+     * Appends a string representation of the given constant to the given
+     * buffer.
+     * 
+     * @param cst an {@link Integer}, {@link Float}, {@link Long},
+     *        {@link Double} or {@link String} object. May be <tt>null</tt>.
+     */
+    void appendConstant(final Object cst) {
+        appendConstant(buf, cst);
+    }
+
+    /**
+     * Appends a string representation of the given constant to the given
+     * buffer.
+     * 
+     * @param buf a string buffer.
+     * @param cst an {@link Integer}, {@link Float}, {@link Long},
+     *        {@link Double} or {@link String} object. May be <tt>null</tt>.
+     */
+    static void appendConstant(final StringBuffer buf, final Object cst) {
+        if (cst == null) {
+            buf.append("null");
+        } else if (cst instanceof String) {
+            appendString(buf, (String) cst);
+        } else if (cst instanceof Type) {
+            buf.append("Type.getType(\"");
+            buf.append(((Type) cst).getDescriptor());
+            buf.append("\")");
+        } else if (cst instanceof Byte) {
+            buf.append("new Byte((byte)").append(cst).append(")");
+        } else if (cst instanceof Boolean) {
+            buf.append("new Boolean(").append(cst).append(")");
+        } else if (cst instanceof Short) {
+            buf.append("new Short((short)").append(cst).append(")");
+        } else if (cst instanceof Character) {
+            int c = ((Character) cst).charValue();
+            buf.append("new Character((char)").append(c).append(")");
+        } else if (cst instanceof Integer) {
+            buf.append("new Integer(").append(cst).append(")");
+        } else if (cst instanceof Float) {
+            buf.append("new Float(\"").append(cst).append("\")");
+        } else if (cst instanceof Long) {
+            buf.append("new Long(").append(cst).append("L)");
+        } else if (cst instanceof Double) {
+            buf.append("new Double(\"").append(cst).append("\")");
+        } else if (cst instanceof byte[]) {
+            byte[] v = (byte[]) cst;
+            buf.append("new byte[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",").append(v[i]);
+            }
+            buf.append("}");
+        } else if (cst instanceof boolean[]) {
+            boolean[] v = (boolean[]) cst;
+            buf.append("new boolean[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",").append(v[i]);
+            }
+            buf.append("}");
+        } else if (cst instanceof short[]) {
+            short[] v = (short[]) cst;
+            buf.append("new short[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",").append("(short)").append(v[i]);
+            }
+            buf.append("}");
+        } else if (cst instanceof char[]) {
+            char[] v = (char[]) cst;
+            buf.append("new char[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",")
+                        .append("(char)")
+                        .append((int) v[i]);
+            }
+            buf.append("}");
+        } else if (cst instanceof int[]) {
+            int[] v = (int[]) cst;
+            buf.append("new int[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",").append(v[i]);
+            }
+            buf.append("}");
+        } else if (cst instanceof long[]) {
+            long[] v = (long[]) cst;
+            buf.append("new long[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",").append(v[i]).append("L");
+            }
+            buf.append("}");
+        } else if (cst instanceof float[]) {
+            float[] v = (float[]) cst;
+            buf.append("new float[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",").append(v[i]).append("f");
+            }
+            buf.append("}");
+        } else if (cst instanceof double[]) {
+            double[] v = (double[]) cst;
+            buf.append("new double[] {");
+            for (int i = 0; i < v.length; i++) {
+                buf.append(i == 0 ? "" : ",").append(v[i]).append("d");
+            }
+            buf.append("}");
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierAnnotationVisitor.java
new file mode 100644
index 0000000..bb48e2c
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/ASMifierAnnotationVisitor.java
@@ -0,0 +1,127 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+
+/**
+ * An {@link AnnotationVisitor} that prints the ASM code that generates the
+ * annotations it visits.
+ * 
+ * @author Eric Bruneton
+ */
+public class ASMifierAnnotationVisitor extends AbstractVisitor implements
+        AnnotationVisitor
+{
+
+    /**
+     * Identifier of the annotation visitor variable in the produced code.
+     */
+    protected final int id;
+
+    /**
+     * Constructs a new {@link ASMifierAnnotationVisitor}.
+     * 
+     * @param id identifier of the annotation visitor variable in the produced
+     *        code.
+     */
+    public ASMifierAnnotationVisitor(final int id) {
+        this.id = id;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the AnnotationVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visit(final String name, final Object value) {
+        buf.setLength(0);
+        buf.append("av").append(id).append(".visit(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, value);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitEnum(
+        final String name,
+        final String desc,
+        final String value)
+    {
+        buf.setLength(0);
+        buf.append("av").append(id).append(".visitEnum(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, desc);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, value);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
+        buf.append(id).append(".visitAnnotation(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, desc);
+        buf.append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    public AnnotationVisitor visitArray(final String name) {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
+        buf.append(id).append(".visitArray(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    public void visitEnd() {
+        buf.setLength(0);
+        buf.append("av").append(id).append(".visitEnd();\n");
+        text.add(buf.toString());
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierClassVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierClassVisitor.java
new file mode 100644
index 0000000..b89cb65
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/ASMifierClassVisitor.java
@@ -0,0 +1,630 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A {@link ClassVisitor} that prints the ASM code that generates the classes it
+ * visits. This class visitor can be used to quickly write ASM code to generate
+ * some given bytecode: <ul> <li>write the Java source code equivalent to the
+ * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li>
+ * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the
+ * {@link #main main} method);</li> <li>edit the generated source code, if
+ * necessary.</li> </ul> The source code printed when visiting the
+ * <tt>Hello</tt> class is the following: <p> <blockquote>
+ * 
+ * <pre>
+ * import org.objectweb.asm.*;
+ * 
+ * public class HelloDump implements Opcodes {
+ * 
+ *     public static byte[] dump() throws Exception {
+ * 
+ *         ClassWriter cw = new ClassWriter(false);
+ *         FieldVisitor fv;
+ *         MethodVisitor mv;
+ *         AnnotationVisitor av0;
+ * 
+ *         cw.visit(49,
+ *                 ACC_PUBLIC + ACC_SUPER,
+ *                 &quot;Hello&quot;,
+ *                 null,
+ *                 &quot;java/lang/Object&quot;,
+ *                 null);
+ * 
+ *         cw.visitSource(&quot;Hello.java&quot;, null);
+ * 
+ *         {
+ *             mv = cw.visitMethod(ACC_PUBLIC, &quot;&lt;init&gt;&quot;, &quot;()V&quot;, null, null);
+ *             mv.visitVarInsn(ALOAD, 0);
+ *             mv.visitMethodInsn(INVOKESPECIAL,
+ *                     &quot;java/lang/Object&quot;,
+ *                     &quot;&lt;init&gt;&quot;,
+ *                     &quot;()V&quot;);
+ *             mv.visitInsn(RETURN);
+ *             mv.visitMaxs(1, 1);
+ *             mv.visitEnd();
+ *         }
+ *         {
+ *             mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC,
+ *                     &quot;main&quot;,
+ *                     &quot;([Ljava/lang/String;)V&quot;,
+ *                     null,
+ *                     null);
+ *             mv.visitFieldInsn(GETSTATIC,
+ *                     &quot;java/lang/System&quot;,
+ *                     &quot;out&quot;,
+ *                     &quot;Ljava/io/PrintStream;&quot;);
+ *             mv.visitLdcInsn(&quot;hello&quot;);
+ *             mv.visitMethodInsn(INVOKEVIRTUAL,
+ *                     &quot;java/io/PrintStream&quot;,
+ *                     &quot;println&quot;,
+ *                     &quot;(Ljava/lang/String;)V&quot;);
+ *             mv.visitInsn(RETURN);
+ *             mv.visitMaxs(2, 1);
+ *             mv.visitEnd();
+ *         }
+ *         cw.visitEnd();
+ * 
+ *         return cw.toByteArray();
+ *     }
+ * }
+ * 
+ * </pre>
+ * 
+ * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
+ * 
+ * <pre>
+ * public class Hello {
+ * 
+ *     public static void main(String[] args) {
+ *         System.out.println(&quot;hello&quot;);
+ *     }
+ * }
+ * </pre>
+ * 
+ * </blockquote>
+ * 
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements
+        ClassVisitor
+{
+    /**
+     * Pseudo access flag used to distinguish class access flags.
+     */
+    private final static int ACCESS_CLASS = 262144;
+
+    /**
+     * Pseudo access flag used to distinguish field access flags.
+     */
+    private final static int ACCESS_FIELD = 524288;
+
+    /**
+     * Pseudo access flag used to distinguish inner class flags.
+     */
+    private static final int ACCESS_INNER = 1048576;
+
+    /**
+     * The print writer to be used to print the class.
+     */
+    protected final PrintWriter pw;
+
+    /**
+     * Prints the ASM source code to generate the given class to the standard
+     * output. <p> Usage: ASMifierClassVisitor [-debug] &lt;fully qualified
+     * class name or class file name&gt;
+     * 
+     * @param args the command line arguments.
+     * 
+     * @throws Exception if the class cannot be found, or if an IO exception
+     *         occurs.
+     */
+    public static void main(final String[] args) throws Exception {
+        int i = 0;
+        boolean skipDebug = true;
+
+        boolean ok = true;
+        if (args.length < 1 || args.length > 2) {
+            ok = false;
+        }
+        if (ok && args[0].equals("-debug")) {
+            i = 1;
+            skipDebug = false;
+            if (args.length != 2) {
+                ok = false;
+            }
+        }
+        if (!ok) {
+            System.err.println("Prints the ASM code to generate the given class.");
+            System.err.println("Usage: ASMifierClassVisitor [-debug] "
+                    + "<fully qualified class name or class file name>");
+            return;
+        }
+        ClassReader cr;
+        if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
+                || args[i].indexOf('/') > -1) {
+            cr = new ClassReader(new FileInputStream(args[i]));
+        } else {
+            cr = new ClassReader(args[i]);
+        }
+        cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)),
+                getDefaultAttributes(),
+                skipDebug);
+    }
+
+    /**
+     * Constructs a new {@link ASMifierClassVisitor} object.
+     * 
+     * @param pw the print writer to be used to print the class.
+     */
+    public ASMifierClassVisitor(final PrintWriter pw) {
+        super("cw");
+        this.pw = pw;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the ClassVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+        String simpleName;
+        int n = name.lastIndexOf('/');
+        if (n != -1) {
+            text.add("package asm." + name.substring(0, n).replace('/', '.')
+                    + ";\n");
+            simpleName = name.substring(n + 1);
+        } else {
+            simpleName = name;
+        }
+        text.add("import java.util.*;\n");
+        text.add("import org.objectweb.asm.*;\n");
+        text.add("import org.objectweb.asm.attrs.*;\n");
+        text.add("public class " + simpleName + "Dump implements Opcodes {\n\n");
+        text.add("public static byte[] dump () throws Exception {\n\n");
+        text.add("ClassWriter cw = new ClassWriter(false);\n");
+        text.add("FieldVisitor fv;\n");
+        text.add("MethodVisitor mv;\n");
+        text.add("AnnotationVisitor av0;\n");
+        text.add("TypeAnnotationVisitor xav0;\n\n");
+
+        buf.setLength(0);
+        buf.append("cw.visit(");
+        switch (version) {
+            case Opcodes.V1_1:
+                buf.append("V1_1");
+                break;
+            case Opcodes.V1_2:
+                buf.append("V1_2");
+                break;
+            case Opcodes.V1_3:
+                buf.append("V1_3");
+                break;
+            case Opcodes.V1_4:
+                buf.append("V1_4");
+                break;
+            case Opcodes.V1_5:
+                buf.append("V1_5");
+                break;
+            case Opcodes.V1_6:
+                buf.append("V1_6");
+                break;
+            default:
+                buf.append(version);
+                break;
+        }
+        buf.append(", ");
+        appendAccess(access | ACCESS_CLASS);
+        buf.append(", ");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(signature);
+        buf.append(", ");
+        appendConstant(superName);
+        buf.append(", ");
+        if (interfaces != null && interfaces.length > 0) {
+            buf.append("new String[] {");
+            for (int i = 0; i < interfaces.length; ++i) {
+                buf.append(i == 0 ? " " : ", ");
+                appendConstant(interfaces[i]);
+            }
+            buf.append(" }");
+        } else {
+            buf.append("null");
+        }
+        buf.append(");\n\n");
+        text.add(buf.toString());
+    }
+
+    public void visitSource(final String file, final String debug) {
+        buf.setLength(0);
+        buf.append("cw.visitSource(");
+        appendConstant(file);
+        buf.append(", ");
+        appendConstant(debug);
+        buf.append(");\n\n");
+        text.add(buf.toString());
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append("cw.visitOuterClass(");
+        appendConstant(owner);
+        buf.append(", ");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(desc);
+        buf.append(");\n\n");
+        text.add(buf.toString());
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        buf.setLength(0);
+        buf.append("cw.visitInnerClass(");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(outerName);
+        buf.append(", ");
+        appendConstant(innerName);
+        buf.append(", ");
+        appendAccess(access | ACCESS_INNER);
+        buf.append(");\n\n");
+        text.add(buf.toString());
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("fv = cw.visitField(");
+        appendAccess(access | ACCESS_FIELD);
+        buf.append(", ");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(desc);
+        buf.append(", ");
+        appendConstant(signature);
+        buf.append(", ");
+        appendConstant(value);
+        buf.append(");\n");
+        text.add(buf.toString());
+        ASMifierFieldVisitor aav = new ASMifierFieldVisitor();
+        text.add(aav.getText());
+        text.add("}\n");
+        return aav;
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("mv = cw.visitMethod(");
+        appendAccess(access);
+        buf.append(", ");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(desc);
+        buf.append(", ");
+        appendConstant(signature);
+        buf.append(", ");
+        if (exceptions != null && exceptions.length > 0) {
+            buf.append("new String[] {");
+            for (int i = 0; i < exceptions.length; ++i) {
+                buf.append(i == 0 ? " " : ", ");
+                appendConstant(exceptions[i]);
+            }
+            buf.append(" }");
+        } else {
+            buf.append("null");
+        }
+        buf.append(");\n");
+        text.add(buf.toString());
+        ASMifierMethodVisitor acv = new ASMifierMethodVisitor();
+        text.add(acv.getText());
+        text.add("}\n");
+        return acv;
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("av0 = cw.visitAnnotation(");
+        appendConstant(desc);
+        buf.append(", ");
+        buf.append(visible);
+        buf.append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+    
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible,
+        final boolean inCode)
+    {
+      buf.setLength(0);
+      buf.append("{\n");
+      buf.append("xav0 = cw.visitTypeAnnotation(");
+      appendConstant(desc);
+      buf.append(", ");
+      buf.append(visible);
+      buf.append(", ");
+      buf.append(inCode);
+      buf.append(");\n");
+      text.add(buf.toString());
+      ASMifierTypeAnnotationVisitor xav = 
+        new ASMifierTypeAnnotationVisitor(0);
+      text.add(xav.getText());
+      text.add("}\n");
+      return xav;
+    }
+
+    public void visitEnd() {
+        text.add("cw.visitEnd();\n\n");
+        text.add("return cw.toByteArray();\n");
+        text.add("}\n");
+        text.add("}\n");
+        printList(pw, text);
+        pw.flush();
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Appends a string representation of the given access modifiers to {@link
+     * #buf buf}.
+     * 
+     * @param access some access modifiers.
+     */
+    void appendAccess(final int access) {
+        boolean first = true;
+        if ((access & Opcodes.ACC_PUBLIC) != 0) {
+            buf.append("ACC_PUBLIC");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_PRIVATE) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_PRIVATE");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_PROTECTED) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_PROTECTED");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_FINAL) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_FINAL");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_STATIC) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_STATIC");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            if ((access & ACCESS_CLASS) != 0) {
+                buf.append("ACC_SUPER");
+            } else {
+                buf.append("ACC_SYNCHRONIZED");
+            }
+            first = false;
+        }
+        if ((access & Opcodes.ACC_VOLATILE) != 0
+                && (access & ACCESS_FIELD) != 0)
+        {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_VOLATILE");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0
+                && (access & ACCESS_FIELD) == 0)
+        {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_BRIDGE");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0
+                && (access & ACCESS_FIELD) == 0)
+        {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_VARARGS");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_TRANSIENT) != 0
+                && (access & ACCESS_FIELD) != 0)
+        {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_TRANSIENT");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0
+                && (access & ACCESS_FIELD) == 0)
+        {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_NATIVE");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_ENUM) != 0
+                && ((access & ACCESS_CLASS) != 0
+                        || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0))
+        {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_ENUM");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_ANNOTATION) != 0
+                && ((access & ACCESS_CLASS) != 0))
+        {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_ANNOTATION");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_ABSTRACT) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_ABSTRACT");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_INTERFACE) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_INTERFACE");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_STRICT) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_STRICT");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_SYNTHETIC");
+            first = false;
+        }
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            if (!first) {
+                buf.append(" + ");
+            }
+            buf.append("ACC_DEPRECATED");
+            first = false;
+        }
+        if (first) {
+            buf.append("0");
+        }
+    }
+
+    /**
+     * Appends a string representation of the given constant to the given
+     * buffer.
+     * 
+     * @param buf a string buffer.
+     * @param cst an {@link java.lang.Integer Integer}, {@link java.lang.Float
+     *        Float}, {@link java.lang.Long Long},
+     *        {@link java.lang.Double Double} or {@link String String} object.
+     *        May be <tt>null</tt>.
+     */
+    static void appendConstant(final StringBuffer buf, final Object cst) {
+        if (cst == null) {
+            buf.append("null");
+        } else if (cst instanceof String) {
+            AbstractVisitor.appendString(buf, (String) cst);
+        } else if (cst instanceof Type) {
+            buf.append("Type.getType(\"")
+                    .append(((Type) cst).getDescriptor())
+                    .append("\")");
+        } else if (cst instanceof Integer) {
+            buf.append("new Integer(").append(cst).append(")");
+        } else if (cst instanceof Float) {
+            buf.append("new Float(\"").append(cst).append("\")");
+        } else if (cst instanceof Long) {
+            buf.append("new Long(").append(cst).append("L)");
+        } else if (cst instanceof Double) {
+            buf.append("new Double(\"").append(cst).append("\")");
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierFieldVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierFieldVisitor.java
new file mode 100644
index 0000000..d9f7804
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/ASMifierFieldVisitor.java
@@ -0,0 +1,49 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.FieldVisitor;
+
+/**
+ * A {@link FieldVisitor} that prints the ASM code that generates the fields it
+ * visits.
+ *
+ * @author Eric Bruneton
+ */
+public class ASMifierFieldVisitor extends ASMifierAbstractVisitor implements
+        FieldVisitor
+{
+    /**
+     * Constructs a new {@link ASMifierFieldVisitor}.
+     */
+    public ASMifierFieldVisitor() {
+        super("fv");
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierMethodVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierMethodVisitor.java
new file mode 100644
index 0000000..4459860
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/ASMifierMethodVisitor.java
@@ -0,0 +1,382 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import java.util.HashMap;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.TypePath;
+
+/**
+ * A {@link MethodVisitor} that prints the ASM code that generates the methods
+ * it visits.
+ *
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class ASMifierMethodVisitor extends ASMifierAbstractVisitor implements
+        MethodVisitor
+{
+    /**
+     * Constructs a new {@link ASMifierMethodVisitor} object.
+     */
+    public ASMifierMethodVisitor() {
+        super("mv");
+        this.labelNames = new HashMap();
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotationDefault() {
+        buf.setLength(0);
+        buf.append("{\n").append("av0 = mv.visitAnnotationDefault();\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        buf.setLength(0);
+        buf.append("{\n")
+                .append("av0 = mv.visitParameterAnnotation(")
+                .append(parameter)
+                .append(", ");
+        appendConstant(desc);
+        buf.append(", ").append(visible).append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    @Override
+    public void visitCode() {
+        text.add("mv.visitCode();\n");
+    }
+
+    @Override
+    public void visitInsn(final int opcode) {
+        buf.setLength(0);
+        buf.append("mv.visitInsn(").append(OPCODES[opcode]).append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitIntInsn(final int opcode, final int operand) {
+        buf.setLength(0);
+        buf.append("mv.visitIntInsn(")
+                .append(OPCODES[opcode])
+                .append(", ")
+                .append(opcode == Opcodes.NEWARRAY
+                        ? TYPES[operand]
+                        : Integer.toString(operand))
+                .append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitVarInsn(final int opcode, final int var) {
+        buf.setLength(0);
+        buf.append("mv.visitVarInsn(")
+                .append(OPCODES[opcode])
+                .append(", ")
+                .append(var)
+                .append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitTypeInsn(final int opcode, final String desc) {
+        buf.setLength(0);
+        buf.append("mv.visitTypeInsn(").append(OPCODES[opcode]).append(", ");
+        appendConstant(desc);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append("mv.visitFieldInsn(").append(OPCODES[opcode]).append(", ");
+        appendConstant(owner);
+        buf.append(", ");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(desc);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append("mv.visitMethodInsn(").append(OPCODES[opcode]).append(", ");
+        appendConstant(owner);
+        buf.append(", ");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(desc);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+        Object... bsmArgs) {
+      // TODO Auto-generated method stub
+      
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(int typeRef,
+        TypePath typePath, String desc, boolean visible) {
+      // TODO Auto-generated method stub
+      return null;
+    }
+
+    @Override
+    public void visitJumpInsn(final int opcode, final Label label) {
+        buf.setLength(0);
+        declareLabel(label);
+        buf.append("mv.visitJumpInsn(").append(OPCODES[opcode]).append(", ");
+        appendLabel(label);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitLabel(final Label label) {
+        buf.setLength(0);
+        declareLabel(label);
+        buf.append("mv.visitLabel(");
+        appendLabel(label);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitLdcInsn(final Object cst) {
+        buf.setLength(0);
+        buf.append("mv.visitLdcInsn(");
+        appendConstant(cst);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitIincInsn(final int var, final int increment) {
+        buf.setLength(0);
+        buf.append("mv.visitIincInsn(")
+                .append(var)
+                .append(", ")
+                .append(increment)
+                .append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label labels[])
+    {
+        buf.setLength(0);
+        for (int i = 0; i < labels.length; ++i) {
+            declareLabel(labels[i]);
+        }
+        declareLabel(dflt);
+
+        buf.append("mv.visitTableSwitchInsn(")
+                .append(min)
+                .append(", ")
+                .append(max)
+                .append(", ");
+        appendLabel(dflt);
+        buf.append(", new Label[] {");
+        for (int i = 0; i < labels.length; ++i) {
+            buf.append(i == 0 ? " " : ", ");
+            appendLabel(labels[i]);
+        }
+        buf.append(" });\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int keys[],
+        final Label labels[])
+    {
+        buf.setLength(0);
+        for (int i = 0; i < labels.length; ++i) {
+            declareLabel(labels[i]);
+        }
+        declareLabel(dflt);
+
+        buf.append("mv.visitLookupSwitchInsn(");
+        appendLabel(dflt);
+        buf.append(", new int[] {");
+        for (int i = 0; i < keys.length; ++i) {
+            buf.append(i == 0 ? " " : ", ").append(keys[i]);
+        }
+        buf.append(" }, new Label[] {");
+        for (int i = 0; i < labels.length; ++i) {
+            buf.append(i == 0 ? " " : ", ");
+            appendLabel(labels[i]);
+        }
+        buf.append(" });\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        buf.setLength(0);
+        buf.append("mv.visitMultiANewArrayInsn(");
+        appendConstant(desc);
+        buf.append(", ").append(dims).append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        buf.setLength(0);
+        declareLabel(start);
+        declareLabel(end);
+        declareLabel(handler);
+        buf.append("mv.visitTryCatchBlock(");
+        appendLabel(start);
+        buf.append(", ");
+        appendLabel(end);
+        buf.append(", ");
+        appendLabel(handler);
+        buf.append(", ");
+        appendConstant(type);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        buf.setLength(0);
+        buf.append("mv.visitLocalVariable(");
+        appendConstant(name);
+        buf.append(", ");
+        appendConstant(desc);
+        buf.append(", ");
+        appendConstant(signature);
+        buf.append(", ");
+        appendLabel(start);
+        buf.append(", ");
+        appendLabel(end);
+        buf.append(", ").append(index).append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitLineNumber(final int line, final Label start) {
+        buf.setLength(0);
+        buf.append("mv.visitLineNumber(").append(line).append(", ");
+        appendLabel(start);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    @Override
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        buf.setLength(0);
+        buf.append("mv.visitMaxs(")
+                .append(maxStack)
+                .append(", ")
+                .append(maxLocals)
+                .append(");\n");
+        text.add(buf.toString());
+    }
+
+    /**
+     * Appends a declaration of the given label to {@link #buf buf}. This
+     * declaration is of the form "Label lXXX = new Label();". Does nothing if
+     * the given label has already been declared.
+     *
+     * @param l a label.
+     */
+    private void declareLabel(final Label l) {
+        String name = (String) labelNames.get(l);
+        if (name == null) {
+            name = "l" + labelNames.size();
+            labelNames.put(l, name);
+            buf.append("Label ").append(name).append(" = new Label();\n");
+        }
+    }
+
+    /**
+     * Appends the name of the given label to {@link #buf buf}. The given label
+     * <i>must</i> already have a name. One way to ensure this is to always
+     * call {@link #declareLabel declared} before calling this method.
+     *
+     * @param l a label.
+     */
+    private void appendLabel(final Label l) {
+        buf.append((String) labelNames.get(l));
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/ASMifierTypeAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/ASMifierTypeAnnotationVisitor.java
new file mode 100644
index 0000000..499f82f
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/ASMifierTypeAnnotationVisitor.java
@@ -0,0 +1,205 @@
+package org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * An {@link TypeAnnotationVisitor} that prints the ASM code that generates 
+ *  the extended annotations it visits.
+ * 
+ * @author jaimeq
+ */
+public class ASMifierTypeAnnotationVisitor extends AbstractVisitor 
+  implements TypeAnnotationVisitor
+{
+
+    /**
+     * Identifier of the extended annotation visitor variable in the 
+     *  produced code.
+     */
+    protected final int id;
+
+    /**
+     * Constructs a new {@link ASMifierTypeAnnotationVisitor}.
+     * 
+     * @param id identifier of the extended annotation visitor variable in the
+     *  produced code.
+     */
+    public ASMifierTypeAnnotationVisitor(final int id) {
+        this.id = id;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the TypeAnnotationVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visit(final String name, final Object value) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visit(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, value);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitEnum(
+        final String name,
+        final String desc,
+        final String value)
+    {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitEnum(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, desc);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, value);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
+        buf.append(id).append(".visitAnnotation(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(", ");
+        ASMifierAbstractVisitor.appendConstant(buf, desc);
+        buf.append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    public AnnotationVisitor visitArray(final String name) {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("AnnotationVisitor av").append(id + 1).append(" = av");
+        buf.append(id).append(".visitArray(");
+        ASMifierAbstractVisitor.appendConstant(buf, name);
+        buf.append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(id + 1);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    public void visitEnd() {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitEnd();\n");
+        text.add(buf.toString());
+    }
+
+    public void appendConstant(final StringBuffer buf, int i) {
+        ASMifierAbstractVisitor.appendConstant(buf, Integer.valueOf(i));
+    }
+
+    public void visitXTargetType(int target_type) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXTargetType(");
+        appendConstant(buf, target_type);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXOffset(int offset) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXOffset(");
+        appendConstant(buf, offset);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXLocationLength(int location_length) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXLocationLength(");
+        appendConstant(buf, location_length);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXLocation(TypePathEntry location) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXLocation(");
+        buf.append(location.toString());
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXNumEntries(int num_entries) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXNumEntries(");
+        appendConstant(buf, num_entries);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXStartPc(int start_pc) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXStartPc(");
+        appendConstant(buf, start_pc);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXLength(int length) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXLength(");
+        appendConstant(buf, length);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXIndex(int index) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXIndex(");
+        appendConstant(buf, index);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXParamIndex(int param_index) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXParamIndex(");
+        appendConstant(buf, param_index);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXBoundIndex(int bound_index) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXBoundIndex(");
+        appendConstant(buf, bound_index);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXTypeIndex(int type_index) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXTypeIndex(");
+        appendConstant(buf, type_index);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXExceptionIndex(int exception_index) {
+        buf.setLength(0);
+        buf.append("xav").append(id).append(".visitXExceptionIndex(");
+        appendConstant(buf, exception_index);
+        buf.append(");\n");
+        text.add(buf.toString());
+    }
+
+    public void visitXNameAndArgsSize() {
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/AbstractVisitor.java b/asmx/src/org/objectweb/asm/util/AbstractVisitor.java
new file mode 100644
index 0000000..3e329f7
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/AbstractVisitor.java
@@ -0,0 +1,201 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.util.attrs.ASMStackMapAttribute;
+import org.objectweb.asm.util.attrs.ASMStackMapTableAttribute;
+
+/**
+ * An abstract visitor.
+ * 
+ * @author Eric Bruneton
+ */
+public abstract class AbstractVisitor {
+
+    /**
+     * The names of the Java Virtual Machine opcodes.
+     */
+    public final static String[] OPCODES;
+    /**
+     * Types for <code>operand</code> parameter of the
+     * {@link org.objectweb.asm.MethodVisitor#visitIntInsn} method when
+     * <code>opcode</code> is <code>NEWARRAY</code>.
+     */
+    public final static String[] TYPES;
+
+    static {
+        String s = "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,BIPUSH,SIPUSH,LDC,,,"
+                + "ILOAD,LLOAD,FLOAD,DLOAD,ALOAD,,,,,,,,,,,,,,,,,,,,,IALOAD,"
+                + "LALOAD,FALOAD,DALOAD,AALOAD,BALOAD,CALOAD,SALOAD,ISTORE,"
+                + "LSTORE,FSTORE,DSTORE,ASTORE,,,,,,,,,,,,,,,,,,,,,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,IINC,I2L,"
+                + "I2F,I2D,L2I,L2F,L2D,F2I,F2L,F2D,D2I,D2L,D2F,I2B,I2C,I2S,LCMP,"
+                + "FCMPL,FCMPG,DCMPL,DCMPG,IFEQ,IFNE,IFLT,IFGE,IFGT,IFLE,"
+                + "IF_ICMPEQ,IF_ICMPNE,IF_ICMPLT,IF_ICMPGE,IF_ICMPGT,IF_ICMPLE,"
+                + "IF_ACMPEQ,IF_ACMPNE,GOTO,JSR,RET,TABLESWITCH,LOOKUPSWITCH,"
+                + "IRETURN,LRETURN,FRETURN,DRETURN,ARETURN,RETURN,GETSTATIC,"
+                + "PUTSTATIC,GETFIELD,PUTFIELD,INVOKEVIRTUAL,INVOKESPECIAL,"
+                + "INVOKESTATIC,INVOKEINTERFACE,,NEW,NEWARRAY,ANEWARRAY,"
+                + "ARRAYLENGTH,ATHROW,CHECKCAST,INSTANCEOF,MONITORENTER,"
+                + "MONITOREXIT,,MULTIANEWARRAY,IFNULL,IFNONNULL,";
+        OPCODES = new String[200];
+        int i = 0;
+        int j = 0;
+        int l;
+        while ((l = s.indexOf(',', j)) > 0) {
+            OPCODES[i++] = j + 1 == l ? null : s.substring(j, l);
+            j = l + 1;
+        }
+
+        s = "T_BOOLEAN,T_CHAR,T_FLOAT,T_DOUBLE,T_BYTE,T_SHORT,T_INT,T_LONG,";
+        TYPES = new String[12];
+        j = 0;
+        i = 4;
+        while ((l = s.indexOf(',', j)) > 0) {
+            TYPES[i++] = s.substring(j, l);
+            j = l + 1;
+        }
+    }
+
+    /**
+     * The text to be printed. Since the code of methods is not necessarily
+     * visited in sequential order, one method after the other, but can be
+     * interlaced (some instructions from method one, then some instructions
+     * from method two, then some instructions from method one again...), it is
+     * not possible to print the visited instructions directly to a sequential
+     * stream. A class is therefore printed in a two steps process: a string
+     * tree is constructed during the visit, and printed to a sequential stream
+     * at the end of the visit. This string tree is stored in this field, as a
+     * string list that can contain other string lists, which can themselves
+     * contain other string lists, and so on.
+     */
+    public final List text;
+
+    /**
+     * A buffer that can be used to create strings.
+     */
+    protected final StringBuffer buf;
+
+    /**
+     * Constructs a new {@link AbstractVisitor}.
+     */
+    protected AbstractVisitor() {
+        this.text = new ArrayList();
+        this.buf = new StringBuffer();
+    }
+
+    /**
+     * Returns the text printed by this visitor.
+     * 
+     * @return the text printed by this visitor.
+     */
+    public List getText() {
+        return text;
+    }
+
+    /**
+     * Appends a quoted string to a given buffer.
+     * 
+     * @param buf the buffer where the string must be added.
+     * @param s the string to be added.
+     */
+    public static void appendString(final StringBuffer buf, final String s) {
+        buf.append("\"");
+        for (int i = 0; i < s.length(); ++i) {
+            char c = s.charAt(i);
+            if (c == '\n') {
+                buf.append("\\n");
+            } else if (c == '\r') {
+                buf.append("\\r");
+            } else if (c == '\\') {
+                buf.append("\\\\");
+            } else if (c == '"') {
+                buf.append("\\\"");
+            } else if (c < 0x20 || c > 0x7f) {
+                buf.append("\\u");
+                if (c < 0x10) {
+                    buf.append("000");
+                } else if (c < 0x100) {
+                    buf.append("00");
+                } else if (c < 0x1000) {
+                    buf.append("0");
+                }
+                buf.append(Integer.toString(c, 16));
+            } else {
+                buf.append(c);
+            }
+        }
+        buf.append("\"");
+    }
+
+    /**
+     * Prints the given string tree.
+     * 
+     * @param pw the writer to be used to print the tree.
+     * @param l a string tree, i.e., a string list that can contain other string
+     *        lists, and so on recursively.
+     */
+    void printList(final PrintWriter pw, final List l) {
+        for (int i = 0; i < l.size(); ++i) {
+            Object o = l.get(i);
+            if (o instanceof List) {
+                printList(pw, (List) o);
+            } else {
+                pw.print(o.toString());
+            }
+        }
+    }
+
+    /**
+     * Returns the default {@link ASMifiable} prototypes.
+     * 
+     * @return the default {@link ASMifiable} prototypes.
+     */
+    public static Attribute[] getDefaultAttributes() {
+        try {
+            return new Attribute[] {
+                new ASMStackMapAttribute(),
+                new ASMStackMapTableAttribute() };
+        } catch (Exception e) {
+            return new Attribute[0];
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/CheckAnnotationAdapter.java b/asmx/src/org/objectweb/asm/util/CheckAnnotationAdapter.java
new file mode 100644
index 0000000..d00933c
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/CheckAnnotationAdapter.java
@@ -0,0 +1,125 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Type;
+
+/**
+ * An {@link AnnotationVisitor} that checks that its methods are properly used.
+ * 
+ * @author Eric Bruneton
+ */
+public class CheckAnnotationAdapter implements AnnotationVisitor {
+
+    private AnnotationVisitor av;
+
+    private boolean named;
+
+    private boolean end;
+
+    public CheckAnnotationAdapter(final AnnotationVisitor av) {
+        this(av, true);
+    }
+
+    CheckAnnotationAdapter(
+        final AnnotationVisitor av,
+        final boolean named)
+    {
+        this.av = av;
+        this.named = named;
+    }
+
+    public void visit(final String name, final Object value) {
+        checkEnd();
+        checkName(name);
+        if (!(value instanceof Byte || value instanceof Boolean
+                || value instanceof Character || value instanceof Short
+                || value instanceof Integer || value instanceof Long
+                || value instanceof Float || value instanceof Double
+                || value instanceof String || value instanceof Type
+                || value instanceof byte[] || value instanceof boolean[]
+                || value instanceof char[] || value instanceof short[]
+                || value instanceof int[] || value instanceof long[]
+                || value instanceof float[] || value instanceof double[]))
+        {
+            throw new IllegalArgumentException("Invalid annotation value");
+        }
+        av.visit(name, value);
+    }
+
+    public void visitEnum(
+        final String name,
+        final String desc,
+        final String value)
+    {
+        checkEnd();
+        checkName(name);
+        CheckMethodAdapter.checkDesc(desc, false);
+        if (value == null) {
+            throw new IllegalArgumentException("Invalid enum value");
+        }
+        av.visitEnum(name, desc, value);
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String name,
+        final String desc)
+    {
+        checkEnd();
+        checkName(name);
+        CheckMethodAdapter.checkDesc(desc, false);
+        return new CheckAnnotationAdapter(av.visitAnnotation(name, desc));
+    }
+
+    public AnnotationVisitor visitArray(final String name) {
+        checkEnd();
+        checkName(name);
+        return new CheckAnnotationAdapter(av.visitArray(name), false);
+    }
+
+    public void visitEnd() {
+        checkEnd();
+        end = true;
+        av.visitEnd();
+    }
+
+    private void checkEnd() {
+        if (end) {
+            throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
+        }
+    }
+
+    private void checkName(final String name) {
+        if (named && name == null) {
+            throw new IllegalArgumentException("Annotation value name must not be null");
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/CheckClassAdapter.java b/asmx/src/org/objectweb/asm/util/CheckClassAdapter.java
new file mode 100644
index 0000000..640e8b0
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/CheckClassAdapter.java
@@ -0,0 +1,416 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
+import org.objectweb.asm.tree.analysis.Analyzer;
+import org.objectweb.asm.tree.analysis.SimpleVerifier;
+import org.objectweb.asm.tree.analysis.Frame;
+
+/**
+ * A {@link ClassAdapter} that checks that its methods are properly used. More
+ * precisely this class adapter checks each method call individually, based
+ * <i>only</i> on its arguments, but does <i>not</i> check the <i>sequence</i>
+ * of method calls. For example, the invalid sequence
+ * <tt>visitField(ACC_PUBLIC, "i", "I", null)</tt> <tt>visitField(ACC_PUBLIC,
+ * "i", "D", null)</tt>
+ * will <i>not</i> be detected by this class adapter.
+ * 
+ * @author Eric Bruneton
+ */
+public class CheckClassAdapter extends ClassAdapter {
+
+    /**
+     * <tt>true</tt> if the visit method has been called.
+     */
+    private boolean start;
+
+    /**
+     * <tt>true</tt> if the visitSource method has been called.
+     */
+    private boolean source;
+
+    /**
+     * <tt>true</tt> if the visitOuterClass method has been called.
+     */
+    private boolean outer;
+
+    /**
+     * <tt>true</tt> if the visitEnd method has been called.
+     */
+    private boolean end;
+
+    /**
+     * Checks a given class. <p> Usage: CheckClassAdapter &lt;fully qualified
+     * class name or class file name&gt;
+     * 
+     * @param args the command line arguments.
+     * 
+     * @throws Exception if the class cannot be found, or if an IO exception
+     *         occurs.
+     */
+    public static void main(final String[] args) throws Exception {
+        if (args.length != 1) {
+            System.err.println("Verifies the given class.");
+            System.err.println("Usage: CheckClassAdapter "
+                    + "<fully qualified class name or class file name>");
+            return;
+        }
+        ClassReader cr;
+        if (args[0].endsWith(".class")) {
+            cr = new ClassReader(new FileInputStream(args[0]));
+        } else {
+            cr = new ClassReader(args[0]);
+        }
+
+        verify(cr, false, new PrintWriter(System.err));
+    }
+
+    /**
+     * Checks a given class
+     * 
+     * @param cr a <code>ClassReader</code> that contains bytecode for the analysis. 
+     * @param dump true if bytecode should be printed out not only when errors are found.
+     * @param pw write where results going to be printed
+     */
+    public static void verify(ClassReader cr, boolean dump, PrintWriter pw) {
+        ClassNode cn = new ClassNode();
+        cr.accept(new CheckClassAdapter(cn), true);
+
+        List methods = cn.methods;
+        for (int i = 0; i < methods.size(); ++i) {
+            MethodNode method = (MethodNode) methods.get(i);
+            if (method.instructions.size() > 0) {
+                Analyzer a = new Analyzer(new SimpleVerifier(Type.getType("L"
+                        + cn.name + ";"),
+                        Type.getType("L" + cn.superName + ";"),
+                        (cn.access & Opcodes.ACC_INTERFACE) != 0));
+                try {
+                    a.analyze(cn.name, method);
+                    if (!dump) {
+                        continue;
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                Frame[] frames = a.getFrames();
+
+                TraceMethodVisitor mv = new TraceMethodVisitor();
+
+                pw.println(method.name + method.desc);
+                for (int j = 0; j < method.instructions.size(); ++j) {
+                    ((AbstractInsnNode) method.instructions.get(j)).accept(mv);
+                    
+                    StringBuffer s = new StringBuffer();
+                    Frame f = frames[j];
+                    if (f == null) {
+                        s.append('?');
+                    } else {
+                        for (int k = 0; k < f.getLocals(); ++k) {
+                            s.append(getShortName(f.getLocal(k).toString()))
+                                    .append(' ');
+                        }
+                        s.append(" : ");
+                        for (int k = 0; k < f.getStackSize(); ++k) {
+                            s.append(getShortName(f.getStack(k).toString()))
+                                    .append(' ');
+                        }
+                    }
+                    while (s.length() < method.maxStack + method.maxLocals + 1)
+                    {
+                        s.append(' ');
+                    }
+                    pw.print(Integer.toString(j + 100000).substring(1));
+                    pw.print(" " + s + " : " + mv.buf); // mv.text.get(j));
+                }
+                for (int j = 0; j < method.tryCatchBlocks.size(); ++j) {
+                    ((TryCatchBlockNode) method.tryCatchBlocks.get(j)).accept(mv);
+                    pw.print(" " + mv.buf);
+                }
+                pw.println();
+            }
+        }
+    }
+
+    private static String getShortName(String name) {
+        int n = name.lastIndexOf('/');
+        int k = name.length();
+        if(name.charAt(k-1)==';') k--;
+        return n==-1 ? name : name.substring(n+1, k);
+    }
+
+    /**
+     * Constructs a new {@link CheckClassAdapter}.
+     * 
+     * @param cv the class visitor to which this adapter must delegate calls.
+     */
+    public CheckClassAdapter(final ClassVisitor cv) {
+        super(cv);
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the ClassVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+        if (start) {
+            throw new IllegalStateException("visit must be called only once");
+        } else {
+            start = true;
+        }
+        checkState();
+        checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL
+                + Opcodes.ACC_SUPER + Opcodes.ACC_INTERFACE
+                + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
+                + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM
+                + Opcodes.ACC_DEPRECATED);
+        CheckMethodAdapter.checkInternalName(name, "class name");
+        if ("java/lang/Object".equals(name)) {
+            if (superName != null) {
+                throw new IllegalArgumentException("The super class name of the Object class must be 'null'");
+            }
+        } else {
+            CheckMethodAdapter.checkInternalName(superName, "super class name");
+        }
+        if (signature != null) {
+            // TODO
+        }
+        if ((access & Opcodes.ACC_INTERFACE) != 0) {
+            if (!"java/lang/Object".equals(superName)) {
+                throw new IllegalArgumentException("The super class name of interfaces must be 'java/lang/Object'");
+            }
+        }
+        if (interfaces != null) {
+            for (int i = 0; i < interfaces.length; ++i) {
+                CheckMethodAdapter.checkInternalName(interfaces[i],
+                        "interface name at index " + i);
+            }
+        }
+        cv.visit(version, access, name, signature, superName, interfaces);
+    }
+
+    public void visitSource(final String file, final String debug) {
+        checkState();
+        if (source) {
+            throw new IllegalStateException("visitSource can be called only once.");
+        }
+        source = true;
+        cv.visitSource(file, debug);
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        checkState();
+        if (outer) {
+            throw new IllegalStateException("visitSource can be called only once.");
+        }
+        outer = true;
+        if (owner == null) {
+            throw new IllegalArgumentException("Illegal outer class owner");
+        }
+        if (desc != null) {
+            CheckMethodAdapter.checkMethodDesc(desc);
+        }
+        cv.visitOuterClass(owner, name, desc);
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        checkState();
+        CheckMethodAdapter.checkInternalName(name, "class name");
+        if (outerName != null) {
+            CheckMethodAdapter.checkInternalName(outerName, "outer class name");
+        }
+        if (innerName != null) {
+            CheckMethodAdapter.checkIdentifier(innerName, "inner class name");
+        }
+        checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+                + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+                + Opcodes.ACC_FINAL + Opcodes.ACC_INTERFACE
+                + Opcodes.ACC_ABSTRACT + Opcodes.ACC_SYNTHETIC
+                + Opcodes.ACC_ANNOTATION + Opcodes.ACC_ENUM);
+        cv.visitInnerClass(name, outerName, innerName, access);
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        checkState();
+        checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+                + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+                + Opcodes.ACC_FINAL + Opcodes.ACC_VOLATILE
+                + Opcodes.ACC_TRANSIENT + Opcodes.ACC_SYNTHETIC
+                + Opcodes.ACC_ENUM + Opcodes.ACC_DEPRECATED);
+        CheckMethodAdapter.checkIdentifier(name, "field name");
+        CheckMethodAdapter.checkDesc(desc, false);
+        if (signature != null) {
+            // TODO
+        }
+        if (value != null) {
+            CheckMethodAdapter.checkConstant(value);
+        }
+        FieldVisitor av = cv.visitField(access, name, desc, signature, value);
+        return new CheckFieldAdapter(av);
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        checkState();
+        checkAccess(access, Opcodes.ACC_PUBLIC + Opcodes.ACC_PRIVATE
+                + Opcodes.ACC_PROTECTED + Opcodes.ACC_STATIC
+                + Opcodes.ACC_FINAL + Opcodes.ACC_SYNCHRONIZED
+                + Opcodes.ACC_BRIDGE + Opcodes.ACC_VARARGS + Opcodes.ACC_NATIVE
+                + Opcodes.ACC_ABSTRACT + Opcodes.ACC_STRICT
+                + Opcodes.ACC_SYNTHETIC + Opcodes.ACC_DEPRECATED);
+        CheckMethodAdapter.checkMethodIdentifier(name, "method name");
+        CheckMethodAdapter.checkMethodDesc(desc);
+        if (signature != null) {
+            // TODO
+        }
+        if (exceptions != null) {
+            for (int i = 0; i < exceptions.length; ++i) {
+                CheckMethodAdapter.checkInternalName(exceptions[i],
+                        "exception name at index " + i);
+            }
+        }
+        return new CheckMethodAdapter(cv.visitMethod(access,
+                name,
+                desc,
+                signature,
+                exceptions));
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        checkState();
+        CheckMethodAdapter.checkDesc(desc, false);
+        return new CheckAnnotationAdapter(cv.visitAnnotation(desc, visible));
+    }
+
+    public void visitAttribute(final Attribute attr) {
+        checkState();
+        if (attr == null) {
+            throw new IllegalArgumentException("Invalid attribute (must not be null)");
+        }
+        cv.visitAttribute(attr);
+    }
+
+    public void visitEnd() {
+        checkState();
+        end = true;
+        cv.visitEnd();
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Checks that the visit method has been called and that visitEnd has not
+     * been called.
+     */
+    private void checkState() {
+        if (!start) {
+            throw new IllegalStateException("Cannot visit member before visit has been called.");
+        }
+        if (end) {
+            throw new IllegalStateException("Cannot visit member after visitEnd has been called.");
+        }
+    }
+
+    /**
+     * Checks that the given access flags do not contain invalid flags. This
+     * method also checks that mutually incompatible flags are not set
+     * simultaneously.
+     * 
+     * @param access the access flags to be checked
+     * @param possibleAccess the valid access flags.
+     */
+    static void checkAccess(final int access, final int possibleAccess) {
+        if ((access & ~possibleAccess) != 0) {
+            throw new IllegalArgumentException("Invalid access flags: "
+                    + access);
+        }
+        int pub = ((access & Opcodes.ACC_PUBLIC) != 0 ? 1 : 0);
+        int pri = ((access & Opcodes.ACC_PRIVATE) != 0 ? 1 : 0);
+        int pro = ((access & Opcodes.ACC_PROTECTED) != 0 ? 1 : 0);
+        if (pub + pri + pro > 1) {
+            throw new IllegalArgumentException("public private and protected are mutually exclusive: "
+                    + access);
+        }
+        int fin = ((access & Opcodes.ACC_FINAL) != 0 ? 1 : 0);
+        int abs = ((access & Opcodes.ACC_ABSTRACT) != 0 ? 1 : 0);
+        if (fin + abs > 1) {
+            throw new IllegalArgumentException("final and abstract are mutually exclusive: "
+                    + access);
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/CheckFieldAdapter.java b/asmx/src/org/objectweb/asm/util/CheckFieldAdapter.java
new file mode 100644
index 0000000..e267093
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/CheckFieldAdapter.java
@@ -0,0 +1,85 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+
+/**
+ * A {@link FieldVisitor} that checks that its methods are properly used.
+ */
+public class CheckFieldAdapter implements FieldVisitor {
+
+    private FieldVisitor fv;
+
+    private boolean end;
+
+    public CheckFieldAdapter(final FieldVisitor fv) {
+        this.fv = fv;
+    }
+
+    public AnnotationVisitor visitAnnotation(final String desc, boolean visible)
+    {
+        checkEnd();
+        CheckMethodAdapter.checkDesc(desc, false);
+        return new CheckAnnotationAdapter(fv.visitAnnotation(desc, visible));
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+        boolean visible,
+        boolean inCode)
+    {
+        throw new RuntimeException("Jaime did not implement yet");
+    }
+    //end jaime
+
+    public void visitAttribute(final Attribute attr) {
+        checkEnd();
+        if (attr == null) {
+            throw new IllegalArgumentException("Invalid attribute (must not be null)");
+        }
+        fv.visitAttribute(attr);
+    }
+
+    public void visitEnd() {
+        checkEnd();
+        end = true;
+        fv.visitEnd();
+    }
+
+    private void checkEnd() {
+        if (end) {
+            throw new IllegalStateException("Cannot call a visit method after visitEnd has been called");
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/CheckMethodAdapter.java b/asmx/src/org/objectweb/asm/util/CheckMethodAdapter.java
new file mode 100644
index 0000000..7844077
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/CheckMethodAdapter.java
@@ -0,0 +1,942 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Type;
+
+import java.util.HashMap;
+
+/**
+ * A {@link MethodAdapter} that checks that its methods are properly used. More
+ * precisely this code adapter checks each instruction individually (i.e., each
+ * visit method checks some preconditions based <i>only</i> on its arguments -
+ * such as the fact that the given opcode is correct for a given visit method),
+ * but does <i>not</i> check the <i>sequence</i> of instructions. For example,
+ * in a method whose signature is <tt>void m ()</tt>, the invalid instruction
+ * IRETURN, or the invalid sequence IADD L2I will <i>not</i> be detected by
+ * this code adapter.
+ * 
+ * @author Eric Bruneton
+ */
+public class CheckMethodAdapter extends MethodAdapter {
+
+    /**
+     * <tt>true</tt> if the visitCode method has been called.
+     */
+    private boolean startCode;
+
+    /**
+     * <tt>true</tt> if the visitMaxs method has been called.
+     */
+    private boolean endCode;
+
+    /**
+     * <tt>true</tt> if the visitEnd method has been called.
+     */
+    private boolean endMethod;
+
+    /**
+     * The already visited labels. This map associate Integer values to Label
+     * keys.
+     */
+    private HashMap labels;
+
+    /**
+     * Code of the visit method to be used for each opcode.
+     */
+    private final static int[] TYPE;
+
+    static {
+        String s = "BBBBBBBBBBBBBBBBCCIAADDDDDAAAAAAAAAAAAAAAAAAAABBBBBBBBDD"
+                + "DDDAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+                + "BBBBBBBBBBBBBBBBBBBJBBBBBBBBBBBBBBBBBBBBHHHHHHHHHHHHHHHHD"
+                + "KLBBBBBBFFFFGGGGAECEBBEEBBAMHHAA";
+        TYPE = new int[s.length()];
+        for (int i = 0; i < TYPE.length; ++i) {
+            TYPE[i] = (s.charAt(i) - 'A' - 1);
+        }
+    }
+
+    // code to generate the above string
+    // public static void main (String[] args) {
+    // int[] TYPE = new int[] {
+    // 0, //NOP
+    // 0, //ACONST_NULL
+    // 0, //ICONST_M1
+    // 0, //ICONST_0
+    // 0, //ICONST_1
+    // 0, //ICONST_2
+    // 0, //ICONST_3
+    // 0, //ICONST_4
+    // 0, //ICONST_5
+    // 0, //LCONST_0
+    // 0, //LCONST_1
+    // 0, //FCONST_0
+    // 0, //FCONST_1
+    // 0, //FCONST_2
+    // 0, //DCONST_0
+    // 0, //DCONST_1
+    // 1, //BIPUSH
+    // 1, //SIPUSH
+    // 7, //LDC
+    // -1, //LDC_W
+    // -1, //LDC2_W
+    // 2, //ILOAD
+    // 2, //LLOAD
+    // 2, //FLOAD
+    // 2, //DLOAD
+    // 2, //ALOAD
+    // -1, //ILOAD_0
+    // -1, //ILOAD_1
+    // -1, //ILOAD_2
+    // -1, //ILOAD_3
+    // -1, //LLOAD_0
+    // -1, //LLOAD_1
+    // -1, //LLOAD_2
+    // -1, //LLOAD_3
+    // -1, //FLOAD_0
+    // -1, //FLOAD_1
+    // -1, //FLOAD_2
+    // -1, //FLOAD_3
+    // -1, //DLOAD_0
+    // -1, //DLOAD_1
+    // -1, //DLOAD_2
+    // -1, //DLOAD_3
+    // -1, //ALOAD_0
+    // -1, //ALOAD_1
+    // -1, //ALOAD_2
+    // -1, //ALOAD_3
+    // 0, //IALOAD
+    // 0, //LALOAD
+    // 0, //FALOAD
+    // 0, //DALOAD
+    // 0, //AALOAD
+    // 0, //BALOAD
+    // 0, //CALOAD
+    // 0, //SALOAD
+    // 2, //ISTORE
+    // 2, //LSTORE
+    // 2, //FSTORE
+    // 2, //DSTORE
+    // 2, //ASTORE
+    // -1, //ISTORE_0
+    // -1, //ISTORE_1
+    // -1, //ISTORE_2
+    // -1, //ISTORE_3
+    // -1, //LSTORE_0
+    // -1, //LSTORE_1
+    // -1, //LSTORE_2
+    // -1, //LSTORE_3
+    // -1, //FSTORE_0
+    // -1, //FSTORE_1
+    // -1, //FSTORE_2
+    // -1, //FSTORE_3
+    // -1, //DSTORE_0
+    // -1, //DSTORE_1
+    // -1, //DSTORE_2
+    // -1, //DSTORE_3
+    // -1, //ASTORE_0
+    // -1, //ASTORE_1
+    // -1, //ASTORE_2
+    // -1, //ASTORE_3
+    // 0, //IASTORE
+    // 0, //LASTORE
+    // 0, //FASTORE
+    // 0, //DASTORE
+    // 0, //AASTORE
+    // 0, //BASTORE
+    // 0, //CASTORE
+    // 0, //SASTORE
+    // 0, //POP
+    // 0, //POP2
+    // 0, //DUP
+    // 0, //DUP_X1
+    // 0, //DUP_X2
+    // 0, //DUP2
+    // 0, //DUP2_X1
+    // 0, //DUP2_X2
+    // 0, //SWAP
+    // 0, //IADD
+    // 0, //LADD
+    // 0, //FADD
+    // 0, //DADD
+    // 0, //ISUB
+    // 0, //LSUB
+    // 0, //FSUB
+    // 0, //DSUB
+    // 0, //IMUL
+    // 0, //LMUL
+    // 0, //FMUL
+    // 0, //DMUL
+    // 0, //IDIV
+    // 0, //LDIV
+    // 0, //FDIV
+    // 0, //DDIV
+    // 0, //IREM
+    // 0, //LREM
+    // 0, //FREM
+    // 0, //DREM
+    // 0, //INEG
+    // 0, //LNEG
+    // 0, //FNEG
+    // 0, //DNEG
+    // 0, //ISHL
+    // 0, //LSHL
+    // 0, //ISHR
+    // 0, //LSHR
+    // 0, //IUSHR
+    // 0, //LUSHR
+    // 0, //IAND
+    // 0, //LAND
+    // 0, //IOR
+    // 0, //LOR
+    // 0, //IXOR
+    // 0, //LXOR
+    // 8, //IINC
+    // 0, //I2L
+    // 0, //I2F
+    // 0, //I2D
+    // 0, //L2I
+    // 0, //L2F
+    // 0, //L2D
+    // 0, //F2I
+    // 0, //F2L
+    // 0, //F2D
+    // 0, //D2I
+    // 0, //D2L
+    // 0, //D2F
+    // 0, //I2B
+    // 0, //I2C
+    // 0, //I2S
+    // 0, //LCMP
+    // 0, //FCMPL
+    // 0, //FCMPG
+    // 0, //DCMPL
+    // 0, //DCMPG
+    // 6, //IFEQ
+    // 6, //IFNE
+    // 6, //IFLT
+    // 6, //IFGE
+    // 6, //IFGT
+    // 6, //IFLE
+    // 6, //IF_ICMPEQ
+    // 6, //IF_ICMPNE
+    // 6, //IF_ICMPLT
+    // 6, //IF_ICMPGE
+    // 6, //IF_ICMPGT
+    // 6, //IF_ICMPLE
+    // 6, //IF_ACMPEQ
+    // 6, //IF_ACMPNE
+    // 6, //GOTO
+    // 6, //JSR
+    // 2, //RET
+    // 9, //TABLESWITCH
+    // 10, //LOOKUPSWITCH
+    // 0, //IRETURN
+    // 0, //LRETURN
+    // 0, //FRETURN
+    // 0, //DRETURN
+    // 0, //ARETURN
+    // 0, //RETURN
+    // 4, //GETSTATIC
+    // 4, //PUTSTATIC
+    // 4, //GETFIELD
+    // 4, //PUTFIELD
+    // 5, //INVOKEVIRTUAL
+    // 5, //INVOKESPECIAL
+    // 5, //INVOKESTATIC
+    // 5, //INVOKEINTERFACE
+    // -1, //UNUSED
+    // 3, //NEW
+    // 1, //NEWARRAY
+    // 3, //ANEWARRAY
+    // 0, //ARRAYLENGTH
+    // 0, //ATHROW
+    // 3, //CHECKCAST
+    // 3, //INSTANCEOF
+    // 0, //MONITORENTER
+    // 0, //MONITOREXIT
+    // -1, //WIDE
+    // 11, //MULTIANEWARRAY
+    // 6, //IFNULL
+    // 6, //IFNONNULL
+    // -1, //GOTO_W
+    // -1 //JSR_W
+    // };
+    // for (int i = 0; i < TYPE.length; ++i) {
+    // System.out.print((char)(TYPE[i] + 1 + 'A'));
+    // }
+    // System.out.println();
+    // }
+
+    /**
+     * Constructs a new {@link CheckMethodAdapter} object.
+     * 
+     * @param cv the code visitor to which this adapter must delegate calls.
+     */
+    public CheckMethodAdapter(final MethodVisitor cv) {
+        super(cv);
+        this.labels = new HashMap();
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        checkEndMethod();
+        checkDesc(desc, false);
+        return new CheckAnnotationAdapter(mv.visitAnnotation(desc, visible));
+    }
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        checkEndMethod();
+        return new CheckAnnotationAdapter(mv.visitAnnotationDefault(), false);
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        checkEndMethod();
+        checkDesc(desc, false);
+        return new CheckAnnotationAdapter(mv.visitParameterAnnotation(parameter,
+                desc,
+                visible));
+    }
+
+    public void visitAttribute(final Attribute attr) {
+        checkEndMethod();
+        if (attr == null) {
+            throw new IllegalArgumentException("Invalid attribute (must not be null)");
+        }
+        mv.visitAttribute(attr);
+    }
+
+    public void visitCode() {
+        startCode = true;
+        mv.visitCode();
+    }
+
+    public void visitInsn(final int opcode) {
+        checkStartCode();
+        checkEndCode();
+        checkOpcode(opcode, 0);
+        mv.visitInsn(opcode);
+    }
+
+    public void visitIntInsn(final int opcode, final int operand) {
+        checkStartCode();
+        checkEndCode();
+        checkOpcode(opcode, 1);
+        switch (opcode) {
+            case Opcodes.BIPUSH:
+                checkSignedByte(operand, "Invalid operand");
+                break;
+            case Opcodes.SIPUSH:
+                checkSignedShort(operand, "Invalid operand");
+                break;
+            // case Constants.NEWARRAY:
+            default:
+                if (operand < Opcodes.T_BOOLEAN || operand > Opcodes.T_LONG) {
+                    throw new IllegalArgumentException("Invalid operand (must be an array type code T_...): "
+                            + operand);
+                }
+        }
+        mv.visitIntInsn(opcode, operand);
+    }
+
+    public void visitVarInsn(final int opcode, final int var) {
+        checkStartCode();
+        checkEndCode();
+        checkOpcode(opcode, 2);
+        checkUnsignedShort(var, "Invalid variable index");
+        mv.visitVarInsn(opcode, var);
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        checkStartCode();
+        checkEndCode();
+        checkOpcode(opcode, 3);
+        if (desc != null && desc.length() > 0 && desc.charAt(0) == '[') {
+            checkDesc(desc, false);
+        } else {
+            checkInternalName(desc, "type");
+        }
+        if (opcode == Opcodes.NEW && desc.charAt(0) == '[') {
+            throw new IllegalArgumentException("NEW cannot be used to create arrays: "
+                    + desc);
+        }
+        mv.visitTypeInsn(opcode, desc);
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        checkStartCode();
+        checkEndCode();
+        checkOpcode(opcode, 4);
+        checkInternalName(owner, "owner");
+        checkIdentifier(name, "name");
+        checkDesc(desc, false);
+        mv.visitFieldInsn(opcode, owner, name, desc);
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        checkStartCode();
+        checkEndCode();
+        checkOpcode(opcode, 5);
+        checkMethodIdentifier(name, "name");
+        if (!name.equals("clone")) {
+            // In JDK1.5, clone method can be called on array class descriptors
+            checkInternalName(owner, "owner");
+        }
+        checkMethodDesc(desc);
+        mv.visitMethodInsn(opcode, owner, name, desc);
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+        checkStartCode();
+        checkEndCode();
+        checkOpcode(opcode, 6);
+        checkLabel(label, false, "label");
+        mv.visitJumpInsn(opcode, label);
+    }
+
+    public void visitLabel(final Label label) {
+        checkStartCode();
+        checkEndCode();
+        checkLabel(label, false, "label");
+        if (labels.get(label) != null) {
+            throw new IllegalArgumentException("Already visited label");
+        } else {
+            labels.put(label, new Integer(labels.size()));
+        }
+        mv.visitLabel(label);
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        checkStartCode();
+        checkEndCode();
+        if (!(cst instanceof Type)) {
+            checkConstant(cst);
+        }
+        mv.visitLdcInsn(cst);
+    }
+
+    public void visitIincInsn(final int var, final int increment) {
+        checkStartCode();
+        checkEndCode();
+        checkUnsignedShort(var, "Invalid variable index");
+        checkSignedShort(increment, "Invalid increment");
+        mv.visitIincInsn(var, increment);
+    }
+
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label labels[])
+    {
+        checkStartCode();
+        checkEndCode();
+        if (max < min) {
+            throw new IllegalArgumentException("Max = " + max
+                    + " must be greater than or equal to min = " + min);
+        }
+        checkLabel(dflt, false, "default label");
+        if (labels == null || labels.length != max - min + 1) {
+            throw new IllegalArgumentException("There must be max - min + 1 labels");
+        }
+        for (int i = 0; i < labels.length; ++i) {
+            checkLabel(labels[i], false, "label at index " + i);
+        }
+        mv.visitTableSwitchInsn(min, max, dflt, labels);
+    }
+
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int keys[],
+        final Label labels[])
+    {
+        checkEndCode();
+        checkStartCode();
+        checkLabel(dflt, false, "default label");
+        if (keys == null || labels == null || keys.length != labels.length) {
+            throw new IllegalArgumentException("There must be the same number of keys and labels");
+       }
+        for (int i = 0; i < labels.length; ++i) {
+            checkLabel(labels[i], false, "label at index " + i);
+        }
+        mv.visitLookupSwitchInsn(dflt, keys, labels);
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        checkStartCode();
+        checkEndCode();
+        checkDesc(desc, false);
+        if (desc.charAt(0) != '[') {
+            throw new IllegalArgumentException("Invalid descriptor (must be an array type descriptor): "
+                    + desc);
+        }
+        if (dims < 1) {
+            throw new IllegalArgumentException("Invalid dimensions (must be greater than 0): "
+                    + dims);
+        }
+        if (dims > desc.lastIndexOf('[') + 1) {
+            throw new IllegalArgumentException("Invalid dimensions (must not be greater than dims(desc)): "
+                    + dims);
+        }
+        mv.visitMultiANewArrayInsn(desc, dims);
+    }
+
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        checkStartCode();
+        checkEndCode();
+        if (type != null) {
+            checkInternalName(type, "type");
+        }
+        mv.visitTryCatchBlock(start, end, handler, type);
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        checkStartCode();
+        checkEndCode();
+        checkIdentifier(name, "name");
+        checkDesc(desc, false);
+        checkLabel(start, true, "start label");
+        checkLabel(end, true, "end label");
+        checkUnsignedShort(index, "Invalid variable index");
+        int s = ((Integer) labels.get(start)).intValue();
+        int e = ((Integer) labels.get(end)).intValue();
+        if (e < s) {
+            throw new IllegalArgumentException("Invalid start and end labels (end must be greater than start)");
+        }
+        mv.visitLocalVariable(name, desc, signature, start, end, index);
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        checkStartCode();
+        checkEndCode();
+        checkUnsignedShort(line, "Invalid line number");
+        checkLabel(start, true, "start label");
+        mv.visitLineNumber(line, start);
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        checkStartCode();
+        checkEndCode();
+        endCode = true;
+        checkUnsignedShort(maxStack, "Invalid max stack");
+        checkUnsignedShort(maxLocals, "Invalid max locals");
+        mv.visitMaxs(maxStack, maxLocals);
+    }
+
+    public void visitEnd() {
+        checkEndMethod();
+        endMethod = true;
+        mv.visitEnd();
+    }
+
+    // -------------------------------------------------------------------------
+
+    /**
+     * Checks that the visitCode method has been called.
+     */
+    void checkStartCode() {
+        if (!startCode) {
+            throw new IllegalStateException("Cannot visit instructions before visitCode has been called.");
+        }
+    }
+
+    /**
+     * Checks that the visitMaxs method has not been called.
+     */
+    void checkEndCode() {
+        if (endCode) {
+            throw new IllegalStateException("Cannot visit instructions after visitMaxs has been called.");
+        }
+    }
+
+    /**
+     * Checks that the visitEnd method has not been called.
+     */
+    void checkEndMethod() {
+        if (endMethod) {
+            throw new IllegalStateException("Cannot visit elements after visitEnd has been called.");
+        }
+    }
+
+    /**
+     * Checks that the type of the given opcode is equal to the given type.
+     * 
+     * @param opcode the opcode to be checked.
+     * @param type the expected opcode type.
+     */
+    static void checkOpcode(final int opcode, final int type) {
+        if (opcode < 0 || opcode > 199 || TYPE[opcode] != type) {
+            throw new IllegalArgumentException("Invalid opcode: " + opcode);
+        }
+    }
+
+    /**
+     * Checks that the given value is a signed byte.
+     * 
+     * @param value the value to be checked.
+     * @param msg an message to be used in case of error.
+     */
+    static void checkSignedByte(final int value, final String msg) {
+        if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
+            throw new IllegalArgumentException(msg
+                    + " (must be a signed byte): " + value);
+        }
+    }
+
+    /**
+     * Checks that the given value is a signed short.
+     * 
+     * @param value the value to be checked.
+     * @param msg an message to be used in case of error.
+     */
+    static void checkSignedShort(final int value, final String msg) {
+        if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+            throw new IllegalArgumentException(msg
+                    + " (must be a signed short): " + value);
+        }
+    }
+
+    /**
+     * Checks that the given value is an unsigned short.
+     * 
+     * @param value the value to be checked.
+     * @param msg an message to be used in case of error.
+     */
+    static void checkUnsignedShort(final int value, final String msg) {
+        if (value < 0 || value > 65535) {
+            throw new IllegalArgumentException(msg
+                    + " (must be an unsigned short): " + value);
+        }
+    }
+
+    /**
+     * Checks that the given value is an {@link Integer}, a{@link Float}, a
+     * {@link Long}, a {@link Double} or a {@link String}.
+     * 
+     * @param cst the value to be checked.
+     */
+    static void checkConstant(final Object cst) {
+        if (!(cst instanceof Integer) && !(cst instanceof Float)
+                && !(cst instanceof Long) && !(cst instanceof Double)
+                && !(cst instanceof String))
+        {
+            throw new IllegalArgumentException("Invalid constant: " + cst);
+        }
+    }
+
+    /**
+     * Checks that the given string is a valid Java identifier.
+     * 
+     * @param name the string to be checked.
+     * @param msg a message to be used in case of error.
+     */
+    static void checkIdentifier(final String name, final String msg) {
+        checkIdentifier(name, 0, -1, msg);
+    }
+
+    /**
+     * Checks that the given substring is a valid Java identifier.
+     * 
+     * @param name the string to be checked.
+     * @param start index of the first character of the identifier (inclusive).
+     * @param end index of the last character of the identifier (exclusive). -1
+     *        is equivalent to <tt>name.length()</tt> if name is not
+     *        <tt>null</tt>.
+     * @param msg a message to be used in case of error.
+     */
+    static void checkIdentifier(
+        final String name,
+        final int start,
+        final int end,
+        final String msg)
+    {
+        if (name == null || (end == -1 ? name.length() <= start : end <= start))
+        {
+            throw new IllegalArgumentException("Invalid " + msg
+                    + " (must not be null or empty)");
+        }
+        if (!Character.isJavaIdentifierStart(name.charAt(start))) {
+            throw new IllegalArgumentException("Invalid " + msg
+                    + " (must be a valid Java identifier): " + name);
+        }
+        int max = (end == -1 ? name.length() : end);
+        for (int i = start + 1; i < max; ++i) {
+            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
+                throw new IllegalArgumentException("Invalid " + msg
+                        + " (must be a valid Java identifier): " + name);
+            }
+        }
+    }
+
+    /**
+     * Checks that the given string is a valid Java identifier or is equal to
+     * '&lt;init&gt;' or '&lt;clinit&gt;'.
+     * 
+     * @param name the string to be checked.
+     * @param msg a message to be used in case of error.
+     */
+    static void checkMethodIdentifier(final String name, final String msg) {
+        if (name == null || name.length() == 0) {
+            throw new IllegalArgumentException("Invalid " + msg
+                    + " (must not be null or empty)");
+        }
+        if (name.equals("<init>") || name.equals("<clinit>")) {
+            return;
+        }
+        if (!Character.isJavaIdentifierStart(name.charAt(0))) {
+            throw new IllegalArgumentException("Invalid "
+                    + msg
+                    + " (must be a '<init>', '<clinit>' or a valid Java identifier): "
+                    + name);
+        }
+        for (int i = 1; i < name.length(); ++i) {
+            if (!Character.isJavaIdentifierPart(name.charAt(i))) {
+                throw new IllegalArgumentException("Invalid "
+                        + msg
+                        + " (must be '<init>' or '<clinit>' or a valid Java identifier): "
+                        + name);
+            }
+        }
+    }
+
+    /**
+     * Checks that the given string is a valid internal class name.
+     * 
+     * @param name the string to be checked.
+     * @param msg a message to be used in case of error.
+     */
+    static void checkInternalName(final String name, final String msg) {
+        checkInternalName(name, 0, -1, msg);
+    }
+
+    /**
+     * Checks that the given substring is a valid internal class name.
+     * 
+     * @param name the string to be checked.
+     * @param start index of the first character of the identifier (inclusive).
+     * @param end index of the last character of the identifier (exclusive). -1
+     *        is equivalent to <tt>name.length()</tt> if name is not
+     *        <tt>null</tt>.
+     * @param msg a message to be used in case of error.
+     */
+    static void checkInternalName(
+        final String name,
+        final int start,
+        final int end,
+        final String msg)
+    {
+        if (name == null || name.length() == 0) {
+            throw new IllegalArgumentException("Invalid " + msg
+                    + " (must not be null or empty)");
+        }
+        int max = (end == -1 ? name.length() : end);
+        try {
+            int begin = start;
+            int slash;
+            do {
+                slash = name.indexOf('/', begin + 1);
+                if (slash == -1 || slash > max) {
+                    slash = max;
+                }
+                checkIdentifier(name, begin, slash, null);
+                begin = slash + 1;
+            } while (slash != max);
+        } catch (IllegalArgumentException ex) {
+            throw new IllegalArgumentException("Invalid "
+                    + msg
+                    + " (must be a fully qualified class name in internal form): "
+                    + name);
+        }
+    }
+
+    /**
+     * Checks that the given string is a valid type descriptor.
+     * 
+     * @param desc the string to be checked.
+     * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
+     */
+    static void checkDesc(final String desc, final boolean canBeVoid) {
+        int end = checkDesc(desc, 0, canBeVoid);
+        if (end != desc.length()) {
+            throw new IllegalArgumentException("Invalid descriptor: " + desc);
+        }
+    }
+
+    /**
+     * Checks that a the given substring is a valid type descriptor.
+     * 
+     * @param desc the string to be checked.
+     * @param start index of the first character of the identifier (inclusive).
+     * @param canBeVoid <tt>true</tt> if <tt>V</tt> can be considered valid.
+     * @return the index of the last character of the type decriptor, plus one.
+     */
+    static int checkDesc(
+        final String desc,
+        final int start,
+        final boolean canBeVoid)
+    {
+        if (desc == null || start >= desc.length()) {
+            throw new IllegalArgumentException("Invalid type descriptor (must not be null or empty)");
+        }
+        int index;
+        switch (desc.charAt(start)) {
+            case 'V':
+                if (canBeVoid) {
+                    return start + 1;
+                } else {
+                    throw new IllegalArgumentException("Invalid descriptor: "
+                            + desc);
+                }
+            case 'Z':
+            case 'C':
+            case 'B':
+            case 'S':
+            case 'I':
+            case 'F':
+            case 'J':
+            case 'D':
+                return start + 1;
+            case '[':
+                index = start + 1;
+                while (index < desc.length() && desc.charAt(index) == '[') {
+                    ++index;
+                }
+                if (index < desc.length()) {
+                    return checkDesc(desc, index, false);
+                } else {
+                    throw new IllegalArgumentException("Invalid descriptor: "
+                            + desc);
+                }
+            case 'L':
+                index = desc.indexOf(';', start);
+                if (index == -1 || index - start < 2) {
+                    throw new IllegalArgumentException("Invalid descriptor: "
+                            + desc);
+                }
+                try {
+                    checkInternalName(desc, start + 1, index, null);
+                } catch (IllegalArgumentException ex) {
+                    throw new IllegalArgumentException("Invalid descriptor: "
+                            + desc);
+                }
+                return index + 1;
+            default:
+                throw new IllegalArgumentException("Invalid descriptor: "
+                        + desc);
+        }
+    }
+
+    /**
+     * Checks that the given string is a valid method descriptor.
+     * 
+     * @param desc the string to be checked.
+     */
+    static void checkMethodDesc(final String desc) {
+        if (desc == null || desc.length() == 0) {
+            throw new IllegalArgumentException("Invalid method descriptor (must not be null or empty)");
+        }
+        if (desc.charAt(0) != '(' || desc.length() < 3) {
+            throw new IllegalArgumentException("Invalid descriptor: " + desc);
+        }
+        int start = 1;
+        if (desc.charAt(start) != ')') {
+            do {
+                if (desc.charAt(start) == 'V') {
+                    throw new IllegalArgumentException("Invalid descriptor: "
+                            + desc);
+                }
+                start = checkDesc(desc, start, false);
+            } while (start < desc.length() && desc.charAt(start) != ')');
+        }
+        start = checkDesc(desc, start + 1, true);
+        if (start != desc.length()) {
+            throw new IllegalArgumentException("Invalid descriptor: " + desc);
+        }
+    }
+
+    /**
+     * Checks that the given label is not null. This method can also check that
+     * the label has been visited.
+     * 
+     * @param label the label to be checked.
+     * @param checkVisited <tt>true</tt> to check that the label has been
+     *        visited.
+     * @param msg a message to be used in case of error.
+     */
+    void checkLabel(
+        final Label label,
+        final boolean checkVisited,
+        final String msg)
+    {
+        if (label == null) {
+            throw new IllegalArgumentException("Invalid " + msg
+                    + " (must not be null)");
+        }
+        if (checkVisited && labels.get(label) == null) {
+            throw new IllegalArgumentException("Invalid " + msg
+                    + " (must be visited first)");
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/TraceAbstractVisitor.java b/asmx/src/org/objectweb/asm/util/TraceAbstractVisitor.java
new file mode 100644
index 0000000..75bca94
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/TraceAbstractVisitor.java
@@ -0,0 +1,205 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.util.attrs.Traceable;
+
+/**
+ * An abstract trace visitor.
+ * 
+ * @author Eric Bruneton
+ */
+public abstract class TraceAbstractVisitor extends AbstractVisitor {
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for internal
+     * type names in bytecode notation.
+     */
+    public final static int INTERNAL_NAME = 0;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for field
+     * descriptors, formatted in bytecode notation
+     */
+    public final static int FIELD_DESCRIPTOR = 1;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for field
+     * signatures, formatted in bytecode notation
+     */
+    public final static int FIELD_SIGNATURE = 2;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for method
+     * descriptors, formatted in bytecode notation
+     */
+    public final static int METHOD_DESCRIPTOR = 3;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for method
+     * signatures, formatted in bytecode notation
+     */
+    public final static int METHOD_SIGNATURE = 4;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for class
+     * signatures, formatted in bytecode notation
+     */
+    public final static int CLASS_SIGNATURE = 5;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for field or
+     * method return value signatures, formatted in default Java notation
+     * (non-bytecode)
+     */
+    public final static int TYPE_DECLARATION = 6;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for class
+     * signatures, formatted in default Java notation (non-bytecode)
+     */
+    public final static int CLASS_DECLARATION = 7;
+
+    /**
+     * Constant used in {@link #appendDescriptor appendDescriptor} for method
+     * parameter signatures, formatted in default Java notation (non-bytecode)
+     */
+    public final static int PARAMETERS_DECLARATION = 8;
+
+    /**
+     * Tab for class members.
+     */
+    protected String tab = "  ";
+
+    /**
+     * Prints a disassembled view of the given annotation.
+     * 
+     * @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.
+     */
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        buf.setLength(0);
+        buf.append(tab).append('@');
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append('(');
+        text.add(buf.toString());
+        TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+        text.add(tav.getText());
+        text.add(visible ? ")\n" : ") // invisible\n");
+        return tav;
+    }
+
+    //jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        buf.setLength(0);
+        buf.append(tab).append('@');
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append('(');
+        text.add(buf.toString());
+        TraceTypeAnnotationVisitor txav = 
+          createTraceTypeAnnotationVisitor();
+        text.add(txav.getText());
+        text.add(visible ? ")\n" : ") // invisible\n");
+        return txav;
+    }
+    //end jaime
+
+    /**
+     * Prints a disassembled view of the given attribute.
+     * 
+     * @param attr an attribute.
+     */
+    public void visitAttribute(final Attribute attr) {
+        buf.setLength(0);
+        buf.append(tab).append("ATTRIBUTE ");
+        appendDescriptor(-1, attr.type);
+
+        if (attr instanceof Traceable) {
+            ((Traceable) attr).trace(buf, null);
+        } else {
+            buf.append(" : ").append(attr.toString()).append("\n");
+        }
+
+        text.add(buf.toString());
+    }
+
+    /**
+     * Does nothing.
+     */
+    public void visitEnd() {
+        // does nothing
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    protected TraceAnnotationVisitor createTraceAnnotationVisitor() {
+        return new TraceAnnotationVisitor();
+    }
+
+    //jaime
+    protected TraceTypeAnnotationVisitor createTraceTypeAnnotationVisitor() {
+        return new TraceTypeAnnotationVisitor();
+    }
+    //end jaime
+
+    /**
+     * Appends an internal name, a type descriptor or a type signature to
+     * {@link #buf buf}.
+     * 
+     * @param type indicates if desc is an internal name, a field descriptor, a
+     *        method descriptor, a class signature, ...
+     * @param desc an internal name, type descriptor, or type signature. May be
+     *        <tt>null</tt>.
+     */
+    protected void appendDescriptor(final int type, final String desc) {
+        if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE
+                || type == METHOD_SIGNATURE)
+        {
+            if (desc != null) {
+                buf.append("// signature ").append(desc).append('\n');
+            }
+        } else {
+            buf.append(desc);
+        }
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/util/TraceAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/TraceAnnotationVisitor.java
new file mode 100644
index 0000000..827225b
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/TraceAnnotationVisitor.java
@@ -0,0 +1,272 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Type;
+
+/**
+ * An {@link AnnotationVisitor} that prints a disassembled view of the
+ * annotations it visits.
+ * 
+ * @author Eric Bruneton
+ */
+public class TraceAnnotationVisitor extends TraceAbstractVisitor implements
+        AnnotationVisitor
+{
+
+    /**
+     * The {@link AnnotationVisitor} to which this visitor delegates calls. May
+     * be <tt>null</tt>.
+     */
+    protected AnnotationVisitor av;
+
+    private int valueNumber = 0;
+
+    /**
+     * Constructs a new {@link TraceAnnotationVisitor}.
+     */
+    public TraceAnnotationVisitor() {
+        // ignore
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the AnnotationVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visit(final String name, final Object value) {
+        buf.setLength(0);
+        appendComa(valueNumber++);
+
+        if (name != null) {
+            buf.append(name).append('=');
+        }
+
+        if (value instanceof String) {
+            visitString((String) value);
+        } else if (value instanceof Type) {
+            visitType((Type) value);
+        } else if (value instanceof Byte) {
+            visitByte(((Byte) value).byteValue());
+        } else if (value instanceof Boolean) {
+            visitBoolean(((Boolean) value).booleanValue());
+        } else if (value instanceof Short) {
+            visitShort(((Short) value).shortValue());
+        } else if (value instanceof Character) {
+            visitChar(((Character) value).charValue());
+        } else if (value instanceof Integer) {
+            visitInt(((Integer) value).intValue());
+        } else if (value instanceof Float) {
+            visitFloat(((Float) value).floatValue());
+        } else if (value instanceof Long) {
+            visitLong(((Long) value).longValue());
+        } else if (value instanceof Double) {
+            visitDouble(((Double) value).doubleValue());
+        } else if (value.getClass().isArray()) {
+            buf.append('{');
+            if (value instanceof byte[]) {
+                byte[] v = (byte[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitByte(v[i]);
+                }
+            } else if (value instanceof boolean[]) {
+                boolean[] v = (boolean[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitBoolean(v[i]);
+                }
+            } else if (value instanceof short[]) {
+                short[] v = (short[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitShort(v[i]);
+                }
+            } else if (value instanceof char[]) {
+                char[] v = (char[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitChar(v[i]);
+                }
+            } else if (value instanceof int[]) {
+                int[] v = (int[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitInt(v[i]);
+                }
+            } else if (value instanceof long[]) {
+                long[] v = (long[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitLong(v[i]);
+                }
+            } else if (value instanceof float[]) {
+                float[] v = (float[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitFloat(v[i]);
+                }
+            } else if (value instanceof double[]) {
+                double[] v = (double[]) value;
+                for (int i = 0; i < v.length; i++) {
+                    appendComa(i);
+                    visitDouble(v[i]);
+                }
+            }
+            buf.append('}');
+        } else {
+            buf.append(value);
+        }
+
+        text.add(buf.toString());
+
+        if (av != null) {
+            av.visit(name, value);
+        }
+    }
+
+    private void visitInt(int value) {
+        buf.append(value);
+    }
+
+    private void visitLong(long value) {
+        buf.append(value).append('L');
+    }
+
+    private void visitFloat(float value) {
+        buf.append(value).append('F');
+    }
+
+    private void visitDouble(double value) {
+        buf.append(value).append('D');
+    }
+
+    private void visitChar(char value) {
+        buf.append("(char)").append((int) value);
+    }
+
+    private void visitShort(short value) {
+        buf.append("(short)").append(value);
+    }
+
+    private void visitByte(byte value) {
+        buf.append("(byte)").append(value);
+    }
+
+    private void visitBoolean(boolean value) {
+        buf.append(value);
+    }
+
+    private void visitString(String value) {
+        appendString(buf, value);
+    }
+
+    private void visitType(Type value) {
+        buf.append(value.getClassName()).append(".class");
+    }
+
+    public void visitEnum(
+        final String name,
+        final String desc,
+        final String value)
+    {
+        buf.setLength(0);
+        appendComa(valueNumber++);
+        if (name != null) {
+            buf.append(name).append('=');
+        }
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append('.').append(value);
+        text.add(buf.toString());
+
+        if (av != null) {
+            av.visitEnum(name, desc, value);
+        }
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        appendComa(valueNumber++);
+        if (name != null) {
+            buf.append(name).append('=');
+        }
+        buf.append('@');
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append('(');
+        text.add(buf.toString());
+        TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+        text.add(tav.getText());
+        text.add(")");
+        if (av != null) {
+            tav.av = av.visitAnnotation(name, desc);
+        }
+        return tav;
+    }
+
+    public AnnotationVisitor visitArray(final String name) {
+        buf.setLength(0);
+        appendComa(valueNumber++);
+        if (name != null) {
+            buf.append(name).append('=');
+        }
+        buf.append('{');
+        text.add(buf.toString());
+        TraceAnnotationVisitor tav = createTraceAnnotationVisitor();
+        text.add(tav.getText());
+        text.add("}");
+        if (av != null) {
+            tav.av = av.visitArray(name);
+        }
+        return tav;
+    }
+
+    public void visitEnd() {
+        if (av != null) {
+            av.visitEnd();
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    protected TraceAnnotationVisitor createTraceAnnotationVisitor() {
+        return new TraceAnnotationVisitor();
+    }
+
+    private void appendComa(int i) {
+        if (i != 0) {
+            buf.append(", ");
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/TraceClassVisitor.java b/asmx/src/org/objectweb/asm/util/TraceClassVisitor.java
new file mode 100644
index 0000000..3055ffd
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/TraceClassVisitor.java
@@ -0,0 +1,550 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.signature.SignatureReader;
+
+/**
+ * A {@link ClassVisitor} that prints a disassembled view of the classes it
+ * visits. This class visitor can be used alone (see the {@link #main main}
+ * method) to disassemble a class. It can also be used in the middle of class
+ * visitor chain to trace the class that is visited at a given point in this
+ * chain. This may be uselful for debugging purposes. <p> The trace printed when
+ * visiting the <tt>Hello</tt> class is the following: <p> <blockquote>
+ * 
+ * <pre>
+ * // class version 49.0 (49)
+ * // access flags 33
+ * public class Hello {
+ *
+ *  // compiled from: Hello.java
+ *
+ *   // access flags 1
+ *   public &lt;init&gt; ()V
+ *     ALOAD 0
+ *     INVOKESPECIAL java/lang/Object &lt;init&gt; ()V
+ *     RETURN
+ *     MAXSTACK = 1
+ *     MAXLOCALS = 1
+ *
+ *   // access flags 9
+ *   public static main ([Ljava/lang/String;)V
+ *     GETSTATIC java/lang/System out Ljava/io/PrintStream;
+ *     LDC &quot;hello&quot;
+ *     INVOKEVIRTUAL java/io/PrintStream println (Ljava/lang/String;)V
+ *     RETURN
+ *     MAXSTACK = 2
+ *     MAXLOCALS = 1
+ * }
+ * </pre>
+ * 
+ * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote>
+ * 
+ * <pre>
+ * public class Hello {
+ *
+ *     public static void main(String[] args) {
+ *         System.out.println(&quot;hello&quot;);
+ *     }
+ * }
+ * </pre>
+ * 
+ * </blockquote>
+ * 
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class TraceClassVisitor extends TraceAbstractVisitor implements
+        ClassVisitor
+{
+
+    /**
+     * The {@link ClassVisitor} to which this visitor delegates calls. May be
+     * <tt>null</tt>.
+     */
+    protected final ClassVisitor cv;
+
+    /**
+     * The print writer to be used to print the class.
+     */
+    protected final PrintWriter pw;
+
+    /**
+     * Prints a disassembled view of the given class to the standard output. <p>
+     * Usage: TraceClassVisitor [-debug] &lt;fully qualified class name or class
+     * file name &gt;
+     * 
+     * @param args the command line arguments.
+     * 
+     * @throws Exception if the class cannot be found, or if an IO exception
+     *         occurs.
+     */
+    public static void main(final String[] args) throws Exception {
+        int i = 0;
+        boolean skipDebug = true;
+
+        boolean ok = true;
+        if (args.length < 1 || args.length > 2) {
+            ok = false;
+        }
+        if (ok && args[0].equals("-debug")) {
+            i = 1;
+            skipDebug = false;
+            if (args.length != 2) {
+                ok = false;
+            }
+        }
+        if (!ok) {
+            System.err.println("Prints a disassembled view of the given class.");
+            System.err.println("Usage: TraceClassVisitor [-debug] "
+                    + "<fully qualified class name or class file name>");
+            return;
+        }
+        ClassReader cr;
+        if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
+                || args[i].indexOf('/') > -1)
+        {
+            cr = new ClassReader(new FileInputStream(args[i]));
+        } else {
+            cr = new ClassReader(args[i]);
+        }
+        cr.accept(new TraceClassVisitor(new PrintWriter(System.out)),
+                getDefaultAttributes(),
+                skipDebug);
+    }
+
+    /**
+     * Constructs a new {@link TraceClassVisitor}.
+     * 
+     * @param pw the print writer to be used to print the class.
+     */
+    public TraceClassVisitor(final PrintWriter pw) {
+        this(null, pw);
+    }
+
+    /**
+     * Constructs a new {@link TraceClassVisitor}.
+     * 
+     * @param cv the {@link ClassVisitor} to which this visitor delegates calls.
+     *        May be <tt>null</tt>.
+     * @param pw the print writer to be used to print the class.
+     */
+    public TraceClassVisitor(final ClassVisitor cv, final PrintWriter pw) {
+        this.cv = cv;
+        this.pw = pw;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the ClassVisitor interface
+    // ------------------------------------------------------------------------
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+        int major = version & 0xFFFF;
+        int minor = version >>> 16;
+        buf.setLength(0);
+        buf.append("// class version ")
+                .append(major)
+                .append('.')
+                .append(minor)
+                .append(" (")
+                .append(version)
+                .append(")\n");
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            buf.append("// DEPRECATED\n");
+        }
+        buf.append("// access flags ").append(access).append('\n');
+
+        appendDescriptor(CLASS_SIGNATURE, signature);
+        if (signature != null) {
+            TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
+            SignatureReader r = new SignatureReader(signature);
+            r.accept(sv);
+            buf.append("// declaration: ")
+                    .append(name)
+                    .append(sv.getDeclaration())
+                    .append('\n');
+        }
+
+        appendAccess(access & ~Opcodes.ACC_SUPER);
+        if ((access & Opcodes.ACC_ANNOTATION) != 0) {
+            buf.append("@interface ");
+        } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
+            buf.append("interface ");
+        } else if ((access & Opcodes.ACC_ENUM) != 0) {
+            buf.append("enum ");
+        } else {
+            buf.append("class ");
+        }
+        appendDescriptor(INTERNAL_NAME, name);
+
+        if (superName != null && !superName.equals("java/lang/Object")) {
+            buf.append(" extends ");
+            appendDescriptor(INTERNAL_NAME, superName);
+            buf.append(' ');
+        }
+        if (interfaces != null && interfaces.length > 0) {
+            buf.append(" implements ");
+            for (int i = 0; i < interfaces.length; ++i) {
+                appendDescriptor(INTERNAL_NAME, interfaces[i]);
+                buf.append(' ');
+            }
+        }
+        buf.append(" {\n\n");
+
+        text.add(buf.toString());
+
+        if (cv != null) {
+            cv.visit(version, access, name, signature, superName, interfaces);
+        }
+    }
+
+    public void visitSource(final String file, final String debug) {
+        buf.setLength(0);
+        if (file != null) {
+            buf.append(tab)
+                    .append("// compiled from: ")
+                    .append(file)
+                    .append('\n');
+        }
+        if (debug != null) {
+            buf.append(tab)
+                    .append("// debug info: ")
+                    .append(debug)
+                    .append('\n');
+        }
+        if (buf.length() > 0) {
+            text.add(buf.toString());
+        }
+
+        if (cv != null) {
+            cv.visitSource(file, debug);
+        }
+    }
+
+    public void visitOuterClass(
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append(tab).append("OUTERCLASS ");
+        appendDescriptor(INTERNAL_NAME, owner);
+        // if enclosing name is null, so why should we show this info?
+        if (name != null) {
+            buf.append(' ').append(name).append(' ');
+        } else {
+            buf.append(' ');
+        }
+        appendDescriptor(METHOD_DESCRIPTOR, desc);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (cv != null) {
+            cv.visitOuterClass(owner, name, desc);
+        }
+    }
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        text.add("\n");
+        AnnotationVisitor tav = super.visitAnnotation(desc, visible);
+        if (cv != null) {
+            ((TraceAnnotationVisitor) tav).av = cv.visitAnnotation(desc,
+                    visible);
+        }
+        return tav;
+    }
+
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible,
+        final boolean inCode)
+    {
+        text.add("\n");
+        TypeAnnotationVisitor txav = 
+          super.visitTypeAnnotation(desc, visible);
+        if (cv != null) {
+            ((TraceTypeAnnotationVisitor) txav).xav =
+              cv.visitTypeAnnotation(desc, visible, inCode);
+        }
+        return txav;
+    }
+
+    public void visitAttribute(final Attribute attr) {
+        text.add("\n");
+        super.visitAttribute(attr);
+
+        if (cv != null) {
+            cv.visitAttribute(attr);
+        }
+    }
+
+    public void visitInnerClass(
+        final String name,
+        final String outerName,
+        final String innerName,
+        final int access)
+    {
+        buf.setLength(0);
+        buf.append(tab).append("// access flags ").append(access
+                & ~Opcodes.ACC_SUPER).append('\n');
+        buf.append(tab);
+        appendAccess(access);
+        buf.append("INNERCLASS ");
+        if ((access & Opcodes.ACC_ENUM) != 0) {
+            buf.append("enum ");
+        }
+        appendDescriptor(INTERNAL_NAME, name);
+        buf.append(' ');
+        appendDescriptor(INTERNAL_NAME, outerName);
+        buf.append(' ');
+        appendDescriptor(INTERNAL_NAME, innerName);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (cv != null) {
+            cv.visitInnerClass(name, outerName, innerName, access);
+        }
+    }
+
+    public FieldVisitor visitField(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final Object value)
+    {
+        buf.setLength(0);
+        buf.append('\n');
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            buf.append(tab).append("// DEPRECATED\n");
+        }
+        buf.append(tab).append("// access flags ").append(access).append('\n');
+        if (signature != null) {
+            buf.append(tab);
+            appendDescriptor(FIELD_SIGNATURE, signature);
+
+            TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+            SignatureReader r = new SignatureReader(signature);
+            r.acceptType(sv);
+            buf.append(tab)
+                    .append("// declaration: ")
+                    .append(sv.getDeclaration())
+                    .append('\n');
+        }
+
+        buf.append(tab);
+        appendAccess(access);
+        if ((access & Opcodes.ACC_ENUM) != 0) {
+            buf.append("enum ");
+        }
+
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append(' ').append(name);
+        if (value != null) {
+            buf.append(" = ");
+            if (value instanceof String) {
+                buf.append("\"").append(value).append("\"");
+            } else {
+                buf.append(value);
+            }
+        }
+
+        buf.append('\n');
+        text.add(buf.toString());
+
+        TraceFieldVisitor tav = createTraceFieldVisitor();
+        text.add(tav.getText());
+
+        if (cv != null) {
+            tav.fv = cv.visitField(access, name, desc, signature, value);
+        }
+
+        return tav;
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        buf.setLength(0);
+        buf.append('\n');
+        if ((access & Opcodes.ACC_DEPRECATED) != 0) {
+            buf.append(tab).append("// DEPRECATED\n");
+        }
+        buf.append(tab).append("// access flags ").append(access).append('\n');
+        buf.append(tab);
+        appendDescriptor(METHOD_SIGNATURE, signature);
+
+        if (signature != null) {
+            TraceSignatureVisitor v = new TraceSignatureVisitor(0);
+            SignatureReader r = new SignatureReader(signature);
+            r.accept(v);
+            String genericDecl = v.getDeclaration();
+            String genericReturn = v.getReturnType();
+            String genericExceptions = v.getExceptions();
+
+            buf.append(tab)
+                    .append("// declaration: ")
+                    .append(genericReturn)
+                    .append(' ')
+                    .append(name)
+                    .append(genericDecl);
+            if (genericExceptions != null) {
+                buf.append(" throws ").append(genericExceptions);
+            }
+            buf.append('\n');
+        }
+
+        appendAccess(access);
+        if ((access & Opcodes.ACC_NATIVE) != 0) {
+            buf.append("native ");
+        }
+        if ((access & Opcodes.ACC_VARARGS) != 0) {
+            buf.append("varargs ");
+        }
+        if ((access & Opcodes.ACC_BRIDGE) != 0) {
+            buf.append("bridge ");
+        }
+
+        buf.append(name);
+        appendDescriptor(METHOD_DESCRIPTOR, desc);
+        if (exceptions != null && exceptions.length > 0) {
+            buf.append(" throws ");
+            for (int i = 0; i < exceptions.length; ++i) {
+                appendDescriptor(INTERNAL_NAME, exceptions[i]);
+                buf.append(' ');
+            }
+        }
+
+        buf.append('\n');
+        text.add(buf.toString());
+
+        TraceMethodVisitor tcv = createTraceMethodVisitor();
+        text.add(tcv.getText());
+
+        if (cv != null) {
+            tcv.mv = cv.visitMethod(access, name, desc, signature, exceptions);
+        }
+
+        return tcv;
+    }
+
+    public void visitEnd() {
+        text.add("}\n");
+
+        printList(pw, text);
+        pw.flush();
+
+        if (cv != null) {
+            cv.visitEnd();
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    protected TraceFieldVisitor createTraceFieldVisitor() {
+        return new TraceFieldVisitor();
+    }
+
+    protected TraceMethodVisitor createTraceMethodVisitor() {
+        return new TraceMethodVisitor();
+    }
+
+    /**
+     * Appends a string representation of the given access modifiers to {@link
+     * #buf buf}.
+     * 
+     * @param access some access modifiers.
+     */
+    private void appendAccess(final int access) {
+        if ((access & Opcodes.ACC_PUBLIC) != 0) {
+            buf.append("public ");
+        }
+        if ((access & Opcodes.ACC_PRIVATE) != 0) {
+            buf.append("private ");
+        }
+        if ((access & Opcodes.ACC_PROTECTED) != 0) {
+            buf.append("protected ");
+        }
+        if ((access & Opcodes.ACC_FINAL) != 0) {
+            buf.append("final ");
+        }
+        if ((access & Opcodes.ACC_STATIC) != 0) {
+            buf.append("static ");
+        }
+        if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
+            buf.append("synchronized ");
+        }
+        if ((access & Opcodes.ACC_VOLATILE) != 0) {
+            buf.append("volatile ");
+        }
+        if ((access & Opcodes.ACC_TRANSIENT) != 0) {
+            buf.append("transient ");
+        }
+        // if ((access & Constants.ACC_NATIVE) != 0) {
+        // buf.append("native ");
+        // }
+        if ((access & Opcodes.ACC_ABSTRACT) != 0) {
+            buf.append("abstract ");
+        }
+        if ((access & Opcodes.ACC_STRICT) != 0) {
+            buf.append("strictfp ");
+        }
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0) {
+            buf.append("synthetic ");
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/TraceFieldVisitor.java b/asmx/src/org/objectweb/asm/util/TraceFieldVisitor.java
new file mode 100644
index 0000000..d90f1eb
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/TraceFieldVisitor.java
@@ -0,0 +1,93 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+
+/**
+ * A {@link FieldVisitor} that prints a disassembled view of the fields it
+ * visits.
+ * 
+ * @author Eric Bruneton
+ */
+public class TraceFieldVisitor extends TraceAbstractVisitor implements
+        FieldVisitor
+{
+
+    /**
+     * The {@link FieldVisitor} to which this visitor delegates calls. May be
+     * <tt>null</tt>.
+     */
+    protected FieldVisitor fv;
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        AnnotationVisitor av = super.visitAnnotation(desc, visible);
+        if (fv != null) {
+            ((TraceAnnotationVisitor) av).av = fv.visitAnnotation(desc, visible);
+        }
+        return av;
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc,
+        final boolean visible,
+        final boolean inCode)
+    {
+        TypeAnnotationVisitor xav = super.visitTypeAnnotation(desc, visible);
+        if (fv != null) {
+            ((TraceTypeAnnotationVisitor) xav).xav = fv.visitTypeAnnotation(desc, visible, inCode);
+        }
+        return xav;
+    }
+    //end jaime
+
+    public void visitAttribute(final Attribute attr) {
+        super.visitAttribute(attr);
+
+        if (fv != null) {
+            fv.visitAttribute(attr);
+        }
+    }
+
+    public void visitEnd() {
+        super.visitEnd();
+
+        if (fv != null) {
+            fv.visitEnd();
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/TraceMethodVisitor.java b/asmx/src/org/objectweb/asm/util/TraceMethodVisitor.java
new file mode 100644
index 0000000..da81ada
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/TraceMethodVisitor.java
@@ -0,0 +1,517 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import java.util.HashMap;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.util.attrs.Traceable;
+
+/**
+ * A {@link MethodVisitor} that prints a disassembled view of the methods it
+ * visits.
+ * 
+ * @author Eric Bruneton
+ */
+public class TraceMethodVisitor extends TraceAbstractVisitor implements
+        MethodVisitor
+{
+    /**
+     * The {@link MethodVisitor} to which this visitor delegates calls. May be
+     * <tt>null</tt>.
+     */
+    protected MethodVisitor mv;
+
+    /**
+     * Tab for bytecode instructions.
+     */
+    protected String tab2 = "    ";
+
+    /**
+     * Tab for table and lookup switch instructions.
+     */
+    protected String tab3 = "      ";
+
+    /**
+     * Tab for labels.
+     */
+    protected String ltab = "   ";
+
+    /**
+     * The label names. This map associate String values to Label keys.
+     */
+    protected final HashMap labelNames;
+
+    /**
+     * Constructs a new {@link TraceMethodVisitor}.
+     */
+    public TraceMethodVisitor() {
+        this(null);
+    }
+
+    /**
+     * Constructs a new {@link TraceMethodVisitor}.
+     * 
+     * @param mv the {@link MethodVisitor} to which this visitor delegates
+     *        calls. May be <tt>null</tt>.
+     */
+    public TraceMethodVisitor(final MethodVisitor mv) {
+        this.labelNames = new HashMap();
+        this.mv = mv;
+    }
+
+    // ------------------------------------------------------------------------
+    // Implementation of the MethodVisitor interface
+    // ------------------------------------------------------------------------
+
+    public AnnotationVisitor visitAnnotation(
+        final String desc,
+        final boolean visible)
+    {
+        AnnotationVisitor av = super.visitAnnotation(desc, visible);
+        if (mv != null) {
+            ((TraceAnnotationVisitor) av).av = mv.visitAnnotation(desc, visible);
+        }
+        return av;
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        final String desc, 
+        final boolean visible,
+        final boolean inCode)
+    {
+        TypeAnnotationVisitor xav = super.visitTypeAnnotation(desc, visible);
+        if (mv != null) {
+            ((TraceTypeAnnotationVisitor) xav).xav =
+                mv.visitTypeAnnotation(desc, visible, inCode);
+        }
+        return xav;
+    }
+    //end jaime
+
+    public void visitAttribute(final Attribute attr) {
+        buf.setLength(0);
+        buf.append(tab).append("ATTRIBUTE ");
+        appendDescriptor(-1, attr.type);
+
+        if (attr instanceof Traceable) {
+            ((Traceable) attr).trace(buf, labelNames);
+        } else {
+            buf.append(" : ").append(attr.toString()).append("\n");
+        }
+
+        text.add(buf.toString());
+        if (mv != null) {
+            mv.visitAttribute(attr);
+        }
+    }
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        text.add(tab2 + "default=");
+        TraceAnnotationVisitor tav = new TraceAnnotationVisitor();
+        text.add(tav.getText());
+        text.add("\n");
+        if (mv != null) {
+            tav.av = mv.visitAnnotationDefault();
+        }
+        return tav;
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        buf.setLength(0);
+        buf.append(tab2).append('@');
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append('(');
+        text.add(buf.toString());
+        TraceAnnotationVisitor tav = new TraceAnnotationVisitor();
+        text.add(tav.getText());
+        text.add(visible ? ") // parameter " : ") // invisible, parameter ");
+        text.add(new Integer(parameter));
+        text.add("\n");
+        if (mv != null) {
+            tav.av = mv.visitParameterAnnotation(parameter, desc, visible);
+        }
+        return tav;
+    }
+
+    public void visitCode() {
+        if (mv != null) {
+            mv.visitCode();
+        }
+    }
+
+    public void visitInsn(final int opcode) {
+        buf.setLength(0);
+        buf.append(tab2).append(OPCODES[opcode]).append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitInsn(opcode);
+        }
+    }
+
+    public void visitIntInsn(final int opcode, final int operand) {
+        buf.setLength(0);
+        buf.append(tab2)
+                .append(OPCODES[opcode])
+                .append(' ')
+                .append(opcode == Opcodes.NEWARRAY
+                        ? TYPES[operand]
+                        : Integer.toString(operand))
+                .append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitIntInsn(opcode, operand);
+        }
+    }
+
+    public void visitVarInsn(final int opcode, final int var) {
+        buf.setLength(0);
+        buf.append(tab2)
+                .append(OPCODES[opcode])
+                .append(' ')
+                .append(var)
+                .append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitVarInsn(opcode, var);
+        }
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        buf.setLength(0);
+        buf.append(tab2).append(OPCODES[opcode]).append(' ');
+        if (desc.startsWith("[")) {
+            appendDescriptor(FIELD_DESCRIPTOR, desc);
+        } else {
+            appendDescriptor(INTERNAL_NAME, desc);
+        }
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitTypeInsn(opcode, desc);
+        }
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append(tab2).append(OPCODES[opcode]).append(' ');
+        appendDescriptor(INTERNAL_NAME, owner);
+        buf.append('.').append(name).append(" : ");
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitFieldInsn(opcode, owner, name, desc);
+        }
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        buf.append(tab2).append(OPCODES[opcode]).append(' ');
+        appendDescriptor(INTERNAL_NAME, owner);
+        buf.append('.').append(name).append(' ');
+        appendDescriptor(METHOD_DESCRIPTOR, desc);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitMethodInsn(opcode, owner, name, desc);
+        }
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+        Object... bsmArgs) {
+      // TODO Auto-generated method stub
+      
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(int typeRef,
+        TypePath typePath, String desc, boolean visible) {
+      // TODO Auto-generated method stub
+      return null;
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+        buf.setLength(0);
+        buf.append(tab2).append(OPCODES[opcode]).append(' ');
+        appendLabel(label);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitJumpInsn(opcode, label);
+        }
+    }
+
+    public void visitLabel(final Label label) {
+        buf.setLength(0);
+        buf.append(ltab);
+        appendLabel(label);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitLabel(label);
+        }
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        buf.setLength(0);
+        buf.append(tab2).append("LDC ");
+        if (cst instanceof String) {
+            AbstractVisitor.appendString(buf, (String) cst);
+        } else if (cst instanceof Type) {
+            buf.append(((Type) cst).getDescriptor() + ".class");
+        } else {
+            buf.append(cst);
+        }
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitLdcInsn(cst);
+        }
+    }
+
+    public void visitIincInsn(final int var, final int increment) {
+        buf.setLength(0);
+        buf.append(tab2)
+                .append("IINC ")
+                .append(var)
+                .append(' ')
+                .append(increment)
+                .append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitIincInsn(var, increment);
+        }
+    }
+
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label labels[])
+    {
+        buf.setLength(0);
+        buf.append(tab2).append("TABLESWITCH\n");
+        for (int i = 0; i < labels.length; ++i) {
+            buf.append(tab3).append(min + i).append(": ");
+            appendLabel(labels[i]);
+            buf.append('\n');
+        }
+        buf.append(tab3).append("default: ");
+        appendLabel(dflt);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitTableSwitchInsn(min, max, dflt, labels);
+        }
+    }
+
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int keys[],
+        final Label labels[])
+    {
+        buf.setLength(0);
+        buf.append(tab2).append("LOOKUPSWITCH\n");
+        for (int i = 0; i < labels.length; ++i) {
+            buf.append(tab3).append(keys[i]).append(": ");
+            appendLabel(labels[i]);
+            buf.append('\n');
+        }
+        buf.append(tab3).append("default: ");
+        appendLabel(dflt);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitLookupSwitchInsn(dflt, keys, labels);
+        }
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        buf.setLength(0);
+        buf.append(tab2).append("MULTIANEWARRAY ");
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append(' ').append(dims).append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitMultiANewArrayInsn(desc, dims);
+        }
+    }
+
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        buf.setLength(0);
+        buf.append(tab2).append("TRYCATCHBLOCK ");
+        appendLabel(start);
+        buf.append(' ');
+        appendLabel(end);
+        buf.append(' ');
+        appendLabel(handler);
+        buf.append(' ');
+        appendDescriptor(INTERNAL_NAME, type);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitTryCatchBlock(start, end, handler, type);
+        }
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        buf.setLength(0);
+        buf.append(tab2).append("LOCALVARIABLE ").append(name).append(' ');
+        appendDescriptor(FIELD_DESCRIPTOR, desc);
+        buf.append(' ');
+        appendLabel(start);
+        buf.append(' ');
+        appendLabel(end);
+        buf.append(' ').append(index).append('\n');
+
+        if (signature != null) {
+            buf.append(tab2);
+            appendDescriptor(FIELD_SIGNATURE, signature);
+
+            TraceSignatureVisitor sv = new TraceSignatureVisitor(0);
+            SignatureReader r = new SignatureReader(signature);
+            r.acceptType(sv);
+            buf.append(tab2)
+                    .append("// declaration: ")
+                    .append(sv.getDeclaration())
+                    .append('\n');
+        }
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitLocalVariable(name, desc, signature, start, end, index);
+        }
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        buf.setLength(0);
+        buf.append(tab2).append("LINENUMBER ").append(line).append(' ');
+        appendLabel(start);
+        buf.append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitLineNumber(line, start);
+        }
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        buf.setLength(0);
+        buf.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n');
+        text.add(buf.toString());
+
+        buf.setLength(0);
+        buf.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n');
+        text.add(buf.toString());
+
+        if (mv != null) {
+            mv.visitMaxs(maxStack, maxLocals);
+        }
+    }
+
+    public void visitEnd() {
+        super.visitEnd();
+
+        if (mv != null) {
+            mv.visitEnd();
+        }
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    /**
+     * Appends the name of the given label to {@link #buf buf}. Creates a new
+     * label name if the given label does not yet have one.
+     * 
+     * @param l a label.
+     */
+    public void appendLabel(final Label l) {
+        String name = (String) labelNames.get(l);
+        if (name == null) {
+            name = "L" + labelNames.size();
+            labelNames.put(l, name);
+        }
+        buf.append(name);
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/TraceSignatureVisitor.java b/asmx/src/org/objectweb/asm/util/TraceSignatureVisitor.java
new file mode 100644
index 0000000..c7145c0
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/TraceSignatureVisitor.java
@@ -0,0 +1,300 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.signature.SignatureVisitor;
+
+/**
+ * A {@link SignatureVisitor} that prints a disassembled view of the signature
+ * it visits.
+ * 
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public class TraceSignatureVisitor implements SignatureVisitor {
+
+    private StringBuffer declaration;
+
+    private boolean isInterface;
+
+    private boolean seenFormalParameter;
+
+    private boolean seenInterfaceBound;
+
+    private boolean seenParameter;
+
+    private boolean seenInterface;
+
+    private StringBuffer returnType;
+
+    private StringBuffer exceptions;
+
+    /**
+     * Stack used to keep track of class types that have arguments. Each element
+     * of this stack is a boolean encoded in one bit. The top of the stack is
+     * the lowest order bit. Pushing false = *2, pushing true = *2+1, popping =
+     * /2.
+     */
+    private int argumentStack;
+
+    /**
+     * Stack used to keep track of array class types. Each element of this stack
+     * is a boolean encoded in one bit. The top of the stack is the lowest order
+     * bit. Pushing false = *2, pushing true = *2+1, popping = /2.
+     */
+    private int arrayStack;
+
+    private String separator = "";
+
+    public TraceSignatureVisitor(int access) {
+        isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
+        this.declaration = new StringBuffer();
+    }
+
+    private TraceSignatureVisitor(StringBuffer buf) {
+        this.declaration = buf;
+    }
+
+    public void visitFormalTypeParameter(String name) {
+        declaration.append(seenFormalParameter ? ", " : "<").append(name);
+        seenFormalParameter = true;
+        seenInterfaceBound = false;
+    }
+
+    public SignatureVisitor visitClassBound() {
+        separator = " extends ";
+        startType();
+        return this;
+    }
+
+    public SignatureVisitor visitInterfaceBound() {
+        separator = seenInterfaceBound ? ", " : " extends ";
+        seenInterfaceBound = true;
+        startType();
+        return this;
+    }
+
+    public SignatureVisitor visitSuperclass() {
+        endFormals();
+        separator = " extends ";
+        startType();
+        return this;
+    }
+
+    public SignatureVisitor visitInterface() {
+        separator = seenInterface ? ", " : (isInterface
+                ? " extends "
+                : " implements ");
+        seenInterface = true;
+        startType();
+        return this;
+    }
+
+    public SignatureVisitor visitParameterType() {
+        endFormals();
+        if (!seenParameter) {
+            seenParameter = true;
+            declaration.append('(');
+        } else {
+            declaration.append(", ");
+        }
+        startType();
+        return this;
+    }
+
+    public SignatureVisitor visitReturnType() {
+        endFormals();
+        if (!seenParameter) {
+            declaration.append('(');
+        } else {
+            seenParameter = false;
+        }
+        declaration.append(')');
+        returnType = new StringBuffer();
+        return new TraceSignatureVisitor(returnType);
+    }
+
+    public SignatureVisitor visitExceptionType() {
+        if (exceptions == null) {
+            exceptions = new StringBuffer();
+        } else {
+            exceptions.append(", ");
+        }
+        // startType();
+        return new TraceSignatureVisitor(exceptions);
+    }
+
+    public void visitBaseType(char descriptor) {
+        switch (descriptor) {
+            case 'V':
+                declaration.append("void");
+                break;
+            case 'B':
+                declaration.append("byte");
+                break;
+            case 'J':
+                declaration.append("long");
+                break;
+            case 'Z':
+                declaration.append("boolean");
+                break;
+            case 'I':
+                declaration.append("int");
+                break;
+            case 'S':
+                declaration.append("short");
+                break;
+            case 'C':
+                declaration.append("char");
+                break;
+            case 'F':
+                declaration.append("float");
+                break;
+            // case 'D':
+            default:
+                declaration.append("double");
+                break;
+        }
+        endType();
+    }
+
+    public void visitTypeVariable(String name) {
+        declaration.append(name);
+        endType();
+    }
+
+    public SignatureVisitor visitArrayType() {
+        startType();
+        arrayStack |= 1;
+        return this;
+    }
+
+    public void visitClassType(String name) {
+        if (!"java/lang/Object".equals(name)) {
+            declaration.append(separator).append(name.replace('/', '.'));
+        } else {
+            // Map<java.lang.Object,java.util.List>
+            // or
+            // abstract public V get(Object key); (seen in Dictionary.class)
+            // should have Object
+            // but java.lang.String extends java.lang.Object is unnecessary
+            boolean needObjectClass = argumentStack % 2 == 1 || seenParameter;
+            if (needObjectClass) {
+                declaration.append(separator).append(name.replace('/', '.'));
+            }
+        }
+        separator = "";
+        argumentStack *= 2;
+    }
+
+    public void visitInnerClassType(String name) {
+        if (argumentStack % 2 == 1) {
+            declaration.append('>');
+        }
+        argumentStack /= 2;
+        declaration.append('.');
+        declaration.append(separator).append(name.replace('/', '.'));
+        separator = "";
+        argumentStack *= 2;
+    }
+
+    public void visitTypeArgument() {
+        if (argumentStack % 2 == 0) {
+            ++argumentStack;
+            declaration.append('<');
+        } else {
+            declaration.append(", ");
+        }
+        declaration.append('?');
+    }
+
+    public SignatureVisitor visitTypeArgument(char tag) {
+        if (argumentStack % 2 == 0) {
+            ++argumentStack;
+            declaration.append('<');
+        } else {
+            declaration.append(", ");
+        }
+
+        if (tag == SignatureVisitor.EXTENDS) {
+            declaration.append("? extends ");
+        } else if (tag == SignatureVisitor.SUPER) {
+            declaration.append("? super ");
+        }
+
+        startType();
+        return this;
+    }
+
+    public void visitEnd() {
+        if (argumentStack % 2 == 1) {
+            declaration.append('>');
+        }
+        argumentStack /= 2;
+        endType();
+    }
+
+    public String getDeclaration() {
+        return declaration.toString();
+    }
+
+    public String getReturnType() {
+        return returnType == null ? null : returnType.toString();
+    }
+
+    public String getExceptions() {
+        return exceptions == null ? null : exceptions.toString();
+    }
+
+    // -----------------------------------------------
+
+    private void endFormals() {
+        if (seenFormalParameter) {
+            declaration.append('>');
+            seenFormalParameter = false;
+        }
+    }
+
+    private void startType() {
+        arrayStack *= 2;
+    }
+
+    private void endType() {
+        if (arrayStack % 2 == 1) {
+            while (arrayStack % 2 == 1) {
+                arrayStack /= 2;
+                declaration.append("[]");
+            }
+        } else {
+            arrayStack /= 2;
+        }
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/TraceTypeAnnotationVisitor.java b/asmx/src/org/objectweb/asm/util/TraceTypeAnnotationVisitor.java
new file mode 100644
index 0000000..8ae0069
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/TraceTypeAnnotationVisitor.java
@@ -0,0 +1,287 @@
+package org.objectweb.asm.util;
+
+import org.objectweb.asm.TypeAnnotationVisitor;
+
+import com.sun.tools.javac.code.TargetType;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * An {@link TypeAnnotationVisitor} that prints a disassembled view of the
+ * extended annotations it visits.
+ * 
+ * @author jaimeq
+ */
+
+public class TraceTypeAnnotationVisitor extends TraceAnnotationVisitor 
+  implements TypeAnnotationVisitor
+{
+
+    /**
+     * The {@link TypeAnnotationVisitor} to which this visitor 
+     * delegates calls. May be <tt>null</tt>.
+     */
+    protected TypeAnnotationVisitor xav;
+
+    /**
+     * A string representing two consecutive tabs, as defined in
+     * {@link TraceAbstractVisitor}.
+     */
+    protected String doubleTab = tab + tab;
+
+    // variables necessary in order to print reference info for
+    // extended annotation
+    private int xtarget_type;
+    private int xoffset;
+    private int xlocation_length;
+    private TypePathEntry xlocations[];
+    private int xlocations_index;
+    private int xstart_pc;
+    private int xlength;
+    private int xindex;
+    private int xparam_index;
+    private int xbound_index;
+    private int xexception_index;
+    private int xtype_index;
+
+    /**
+     * Constructs a new {@link TraceTypeAnnotationVisitor}.
+     */
+    public TraceTypeAnnotationVisitor() {
+        // ignore
+    }
+
+    public void visitEnd() {
+        super.visitEnd();
+        finishExtendedPart();
+    }
+
+    // ------------------------------------------------------------------------
+    // Utility methods
+    // ------------------------------------------------------------------------
+
+    protected TraceTypeAnnotationVisitor createTraceTypeAnnotationVisitor() {
+        return new TraceTypeAnnotationVisitor();
+    }
+
+    /**
+     * Prints the reference info of this extended annotation to this.text
+     */
+    private void finishExtendedPart() {
+      buf.setLength(0);
+      buf.append("\n  extended annotation: \n");
+      buf.append(doubleTab).append("target_type: ")
+        .append(xtarget_type).append("\n");
+
+      TargetType tt = TargetType.fromTargetTypeValue(xtarget_type);
+      switch(tt) {
+      // type test (instanceof)
+      // object creation
+      // constructor/method reference receiver
+      // {
+      //   u2 offset;
+      // } reference_info;
+      case INSTANCEOF:
+      case NEW:
+      case CONSTRUCTOR_REFERENCE:
+      case METHOD_REFERENCE:
+          buf.append(doubleTab).append("offset: ").append(xoffset).append("\n");
+          break;
+
+      // method receiver
+      // {
+      // } reference_info;
+      case METHOD_RECEIVER:
+          break;
+
+      // local variable
+      // {
+      //   u2 start_pc;
+      //   u2 length;
+      //   u2 index;
+      // } reference_info;
+      case LOCAL_VARIABLE:
+      // resource variable
+      case RESOURCE_VARIABLE:
+          buf.append(doubleTab).append("start_pc: ").append(xstart_pc).append("\n");
+          buf.append(doubleTab).append("length: ").append(xlength).append("\n");
+          buf.append(doubleTab).append("index: ").append(xindex).append("\n");
+          break;
+
+      // method return type
+      // {
+      // } reference_info;
+      case METHOD_RETURN:
+          break;
+
+      // method parameter
+      // {
+      //   TEMP this should contain the index but doesn't, so for the moment
+      //        we don't print an index
+      // } reference_info;
+      case METHOD_FORMAL_PARAMETER:
+          buf.append(doubleTab).append("index: ").append("FIXME").append("\n");
+          break;
+
+      // field
+      // {
+      // } reference_info;
+      case FIELD:
+          break;
+
+      // class type parameter bound
+      // method type parameter bound
+      // {
+      //   u1 bound_index;
+      // } reference_info;
+      case CLASS_TYPE_PARAMETER_BOUND:
+      case METHOD_TYPE_PARAMETER_BOUND:
+          buf.append(doubleTab).append("param_index: ").append(xparam_index).append("\n");
+          buf.append(doubleTab).append("bound_index: ").append(xbound_index).append("\n");
+          break;
+
+      // class extends/implements
+      // exception type in throws/implements
+      // {
+      //   u1 type_index;  
+      // }
+      case CLASS_EXTENDS:
+      case THROWS:
+          buf.append(doubleTab).append("type_index: ").append(xtype_index).append("\n");
+          break;
+
+      // typecast
+      // type argument in constructor call
+      // type argument in method call
+      // type argument in constructor reference
+      // type argument in method reference
+      // {
+      //   u2 offset;
+      //   u1 type_index;
+      // } reference_info;
+      case CAST:
+      case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
+      case METHOD_INVOCATION_TYPE_ARGUMENT:
+      case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
+      case METHOD_REFERENCE_TYPE_ARGUMENT:
+          buf.append(doubleTab).append("offset: ").append(xoffset).append("\n");
+          buf.append(doubleTab).append("type_index: ").append(xtype_index).append("\n");
+          break;
+
+      // method type parameter
+      // {        
+      //    u1 param_index;
+      // } reference_info;
+      case METHOD_TYPE_PARAMETER:
+          buf.append(doubleTab).append("param_index: ").append(xparam_index).append("\n");
+          break;
+
+      default: throw new RuntimeException("Unrecognized target type: + " + xtarget_type);
+      }
+
+      // now print out location string for generic target types
+      if (xlocation_length != 0) {
+          buf.append(doubleTab).append("location_length: " + xlocation_length).append("\n");
+          buf.append(doubleTab).append("locations: ");
+          boolean first = true;
+          for(int i = 0; i < xlocations.length ; i++) {
+              if(!first) {
+                  buf.append(", ");
+              }
+              first = false;
+              buf.append(xlocations[i]);
+          }
+          buf.append("\n");
+      }
+
+      text.add(buf.toString());
+    }
+
+    public void visitXTargetType(int target_type) {
+      this.xtarget_type = target_type;
+      if(xav != null) {
+        xav.visitXTargetType(target_type);
+      }
+    }
+
+    public void visitXOffset(int offset) {
+      this.xoffset = offset;
+      if(xav != null) {
+        xav.visitXOffset(offset);
+      }
+    }
+
+    public void visitXLocationLength(int location_length) {
+      this.xlocation_length = location_length;
+      this.xlocations = new TypePathEntry[this.xlocation_length];
+      this.xlocations_index = 0;
+      if(xav != null) {
+        xav.visitXLocationLength(location_length);
+      }
+    }
+
+    public void visitXLocation(TypePathEntry location) {
+      this.xlocations[xlocations_index] = location;
+      this.xlocations_index++;
+      if(xav != null) {
+        xav.visitXLocation(location);
+      }
+    }
+
+    public void visitXNumEntries(int num_entries) {
+      if(xav != null) {
+        xav.visitXNumEntries(num_entries);
+      }
+    }
+
+    public void visitXStartPc(int start_pc) {
+      this.xstart_pc = start_pc;
+      if(xav != null) {
+        xav.visitXStartPc(start_pc);
+      }
+    }
+
+    public void visitXLength(int length) {
+      this.xlength = length;
+      if(xav != null) {
+        xav.visitXLength(length);
+      }
+    }
+
+    public void visitXIndex(int index) {
+      this.xindex = index;
+      if(xav != null) {
+        xav.visitXIndex(index);
+      }
+    }
+
+    public void visitXParamIndex(int param_index) {
+      this.xparam_index = param_index;
+      if(xav != null) {
+        xav.visitXParamIndex(param_index);
+      }
+    }
+
+    public void visitXBoundIndex(int bound_index) {
+      this.xbound_index = bound_index;
+      if(xav != null) {
+        xav.visitXBoundIndex(bound_index);
+      }
+    }
+
+    public void visitXTypeIndex(int type_index) {
+      this.xtype_index = type_index;
+      if(xav != null) {
+        xav.visitXTypeIndex(type_index);
+      }
+    }
+
+    public void visitXExceptionIndex(int exception_index) {
+      this.xexception_index = exception_index;
+      if(xav != null) {
+        xav.visitXExceptionIndex(exception_index);
+      }
+    }
+
+    public void visitXNameAndArgsSize() {
+    }
+}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapAttribute.java b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapAttribute.java
new file mode 100644
index 0000000..88c7af8
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapAttribute.java
@@ -0,0 +1,223 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util.attrs;
+
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.attrs.StackMapAttribute;
+import org.objectweb.asm.attrs.StackMapFrame;
+import org.objectweb.asm.attrs.StackMapType;
+
+/**
+ * An {@link ASMifiable} {@link StackMapAttribute} sub class.
+ * 
+ * @author Eugene Kuleshov
+ */
+public class ASMStackMapAttribute extends StackMapAttribute implements
+        ASMifiable,
+        Traceable
+{
+    /**
+     * Length of the attribute used for comparison
+     */
+    private int len;
+
+    public ASMStackMapAttribute() {
+        super();
+    }
+
+    public ASMStackMapAttribute(List frames, int len) {
+        super(frames);
+        this.len = len;
+    }
+
+    protected Attribute read(
+        ClassReader cr,
+        int off,
+        int len,
+        char[] buf,
+        int codeOff,
+        Label[] labels)
+    {
+        StackMapAttribute attr = (StackMapAttribute) super.read(cr,
+                off,
+                len,
+                buf,
+                codeOff,
+                labels);
+
+        return new ASMStackMapAttribute(attr.getFrames(), len);
+    }
+
+    public void asmify(StringBuffer buf, String varName, Map labelNames) {
+        List frames = getFrames();
+        buf.append("{\n");
+        buf.append("StackMapAttribute ").append(varName).append("Attr");
+        buf.append(" = new StackMapAttribute();\n");
+        if (frames.size() > 0) {
+            for (int i = 0; i < frames.size(); i++) {
+                asmify((StackMapFrame) frames.get(i), buf, varName + "frame"
+                        + i, labelNames);
+            }
+        }
+        buf.append(varName).append(".visitAttribute(").append(varName);
+        buf.append("Attr);\n}\n");
+    }
+
+    void asmify(
+        StackMapFrame f,
+        StringBuffer buf,
+        String varName,
+        Map labelNames)
+    {
+        declareLabel(buf, labelNames, f.label);
+        buf.append("{\n");
+
+        buf.append("StackMapFrame ")
+                .append(varName)
+                .append(" = new StackMapFrame();\n");
+
+        buf.append(varName)
+                .append(".label = ")
+                .append(labelNames.get(f.label))
+                .append(";\n");
+
+        asmifyTypeInfo(buf, varName, labelNames, f.locals, "locals");
+        asmifyTypeInfo(buf, varName, labelNames, f.stack, "stack");
+
+        buf.append("cvAttr.frames.add(").append(varName).append(");\n");
+        buf.append("}\n");
+    }
+
+    void asmifyTypeInfo(
+        StringBuffer buf,
+        String varName,
+        Map labelNames,
+        List infos,
+        String field)
+    {
+        if (infos.size() > 0) {
+            buf.append("{\n");
+            for (int i = 0; i < infos.size(); i++) {
+                StackMapType typeInfo = (StackMapType) infos.get(i);
+                String localName = varName + "Info" + i;
+                int type = typeInfo.getType();
+                buf.append("StackMapType ")
+                        .append(localName)
+                        .append(" = StackMapType.getTypeInfo( StackMapType.ITEM_")
+                        .append(StackMapType.ITEM_NAMES[type])
+                        .append(");\n");
+
+                switch (type) {
+                    case StackMapType.ITEM_Object: //
+                        buf.append(localName)
+                                .append(".setObject(\"")
+                                .append(typeInfo.getObject())
+                                .append("\");\n");
+                        break;
+
+                    case StackMapType.ITEM_Uninitialized: //
+                        declareLabel(buf, labelNames, typeInfo.getLabel());
+                        buf.append(localName)
+                                .append(".setLabel(")
+                                .append(labelNames.get(typeInfo.getLabel()))
+                                .append(");\n");
+                        break;
+                }
+                buf.append(varName)
+                        .append(".")
+                        .append(field)
+                        .append(".add(")
+                        .append(localName)
+                        .append(");\n");
+            }
+            buf.append("}\n");
+        }
+    }
+
+    static void declareLabel(StringBuffer buf, Map labelNames, Label l) {
+        String name = (String) labelNames.get(l);
+        if (name == null) {
+            name = "l" + labelNames.size();
+            labelNames.put(l, name);
+            buf.append("Label ").append(name).append(" = new Label();\n");
+        }
+    }
+
+    public void trace(StringBuffer buf, Map labelNames) {
+        List frames = getFrames();
+        buf.append("[\n");
+        for (int i = 0; i < frames.size(); i++) {
+            StackMapFrame f = (StackMapFrame) frames.get(i);
+
+            buf.append("    Frame:");
+            appendLabel(buf, labelNames, f.label);
+
+            buf.append(" locals[");
+            traceTypeInfo(buf, labelNames, f.locals);
+            buf.append("]");
+            buf.append(" stack[");
+            traceTypeInfo(buf, labelNames, f.stack);
+            buf.append("]\n");
+        }
+        buf.append("  ] length:").append(len).append("\n");
+    }
+
+    private void traceTypeInfo(StringBuffer buf, Map labelNames, List infos) {
+        String sep = "";
+        for (int i = 0; i < infos.size(); i++) {
+            StackMapType t = (StackMapType) infos.get(i);
+
+            buf.append(sep).append(StackMapType.ITEM_NAMES[t.getType()]);
+            sep = ", ";
+            if (t.getType() == StackMapType.ITEM_Object) {
+                buf.append(":").append(t.getObject());
+            }
+            if (t.getType() == StackMapType.ITEM_Uninitialized) {
+                buf.append(":");
+                appendLabel(buf, labelNames, t.getLabel());
+            }
+        }
+    }
+
+    protected void appendLabel(StringBuffer buf, Map labelNames, Label l) {
+        String name = (String) labelNames.get(l);
+        if (name == null) {
+            name = "L" + labelNames.size();
+            labelNames.put(l, name);
+        }
+        buf.append(name);
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapTableAttribute.java b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapTableAttribute.java
new file mode 100644
index 0000000..b878357
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/attrs/ASMStackMapTableAttribute.java
@@ -0,0 +1,214 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util.attrs;
+
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.attrs.StackMapTableAttribute;
+import org.objectweb.asm.attrs.StackMapFrame;
+import org.objectweb.asm.attrs.StackMapType;
+
+/**
+ * An {@link ASMifiable} {@link StackMapTableAttribute} sub class.
+ * 
+ * @author Eugene Kuleshov
+ */
+public class ASMStackMapTableAttribute extends StackMapTableAttribute implements
+        ASMifiable,
+        Traceable
+{
+    /**
+     * Length of the attribute used for comparison
+     */
+    private int len;
+
+    public ASMStackMapTableAttribute() {
+        super();
+    }
+
+    public ASMStackMapTableAttribute(List frames, int len) {
+        super(frames);
+        this.len = len;
+    }
+
+    protected Attribute read(
+        ClassReader cr,
+        int off,
+        int len,
+        char[] buf,
+        int codeOff,
+        Label[] labels)
+    {
+        StackMapTableAttribute attr = (StackMapTableAttribute) super.read(cr,
+                off,
+                len,
+                buf,
+                codeOff,
+                labels);
+
+        return new ASMStackMapTableAttribute(attr.getFrames(), len);
+    }
+
+    public void asmify(StringBuffer buf, String varName, Map labelNames) {
+        List frames = getFrames();
+        if (frames.size() == 0) {
+            buf.append("List frames = Collections.EMPTY_LIST;\n");
+        } else {
+            buf.append("List frames = new ArrayList();\n");
+            for (int i = 0; i < frames.size(); i++) {
+                buf.append("{\n");
+                StackMapFrame f = (StackMapFrame) frames.get(i);
+                declareLabel(buf, labelNames, f.label);
+
+                String frameVar = varName + "frame" + i;
+                asmifyTypeInfo(buf, frameVar, labelNames, f.locals, "locals");
+                asmifyTypeInfo(buf, frameVar, labelNames, f.stack, "stack");
+
+                buf.append("StackMapFrame ")
+                        .append(frameVar)
+                        .append(" = new StackMapFrame(")
+                        .append(labelNames.get(f.label))
+                        .append(", locals, stack);\n");
+                buf.append("frames.add(").append(frameVar).append(");\n");
+                buf.append("}\n");
+            }
+        }
+        buf.append("StackMapTableAttribute ").append(varName);
+        buf.append(" = new StackMapTableAttribute(frames);\n");
+    }
+
+    void asmifyTypeInfo(
+        StringBuffer buf,
+        String varName,
+        Map labelNames,
+        List infos,
+        String field)
+    {
+        if (infos.size() == 0) {
+            buf.append("List ")
+                    .append(field)
+                    .append(" = Collections.EMPTY_LIST;\n");
+        } else {
+            buf.append("List ").append(field).append(" = new ArrayList();\n");
+            buf.append("{\n");
+            for (int i = 0; i < infos.size(); i++) {
+                StackMapType typeInfo = (StackMapType) infos.get(i);
+                String localName = varName + "Info" + i;
+                int type = typeInfo.getType();
+                buf.append("StackMapType ")
+                        .append(localName)
+                        .append(" = StackMapType.getTypeInfo( StackMapType.ITEM_")
+                        .append(StackMapType.ITEM_NAMES[type])
+                        .append(");\n");
+
+                switch (type) {
+                    case StackMapType.ITEM_Object: //
+                        buf.append(localName)
+                                .append(".setObject(\"")
+                                .append(typeInfo.getObject())
+                                .append("\");\n");
+                        break;
+
+                    case StackMapType.ITEM_Uninitialized: //
+                        declareLabel(buf, labelNames, typeInfo.getLabel());
+                        buf.append(localName)
+                                .append(".setLabel(")
+                                .append(labelNames.get(typeInfo.getLabel()))
+                                .append(");\n");
+                        break;
+                }
+                buf.append(field)
+                        .append(".add(")
+                        .append(localName)
+                        .append(");\n");
+            }
+            buf.append("}\n");
+        }
+    }
+
+    static void declareLabel(StringBuffer buf, Map labelNames, Label l) {
+        String name = (String) labelNames.get(l);
+        if (name == null) {
+            name = "l" + labelNames.size();
+            labelNames.put(l, name);
+            buf.append("Label ").append(name).append(" = new Label();\n");
+        }
+    }
+
+    public void trace(StringBuffer buf, Map labelNames) {
+        List frames = getFrames();
+        buf.append("[\n");
+        for (int i = 0; i < frames.size(); i++) {
+            StackMapFrame f = (StackMapFrame) frames.get(i);
+
+            buf.append("    Frame:");
+            appendLabel(buf, labelNames, f.label);
+
+            buf.append(" locals[");
+            traceTypeInfo(buf, labelNames, f.locals);
+            buf.append("]");
+            buf.append(" stack[");
+            traceTypeInfo(buf, labelNames, f.stack);
+            buf.append("]\n");
+        }
+        buf.append("  ] length:").append(len).append("\n");
+    }
+
+    private void traceTypeInfo(StringBuffer buf, Map labelNames, List infos) {
+        String sep = "";
+        for (int i = 0; i < infos.size(); i++) {
+            StackMapType t = (StackMapType) infos.get(i);
+
+            buf.append(sep).append(StackMapType.ITEM_NAMES[t.getType()]);
+            sep = ", ";
+            if (t.getType() == StackMapType.ITEM_Object) {
+                buf.append(":").append(t.getObject());
+            }
+            if (t.getType() == StackMapType.ITEM_Uninitialized) {
+                buf.append(":");
+                appendLabel(buf, labelNames, t.getLabel());
+            }
+        }
+    }
+
+    protected void appendLabel(StringBuffer buf, Map labelNames, Label l) {
+        String name = (String) labelNames.get(l);
+        if (name == null) {
+            name = "L" + labelNames.size();
+            labelNames.put(l, name);
+        }
+        buf.append(name);
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/ASMifiable.java b/asmx/src/org/objectweb/asm/util/attrs/ASMifiable.java
new file mode 100644
index 0000000..149d7da
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/attrs/ASMifiable.java
@@ -0,0 +1,53 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util.attrs;
+
+import java.util.Map;
+
+/**
+ * An attribute that can print the ASM code to create an equivalent attribute.
+ * 
+ * Implementation should print the ASM code that generates attribute data
+ * structures for current attribute state.
+ * 
+ * @author Eugene Kuleshov
+ */
+public interface ASMifiable {
+
+    /**
+     * Prints the ASM code to create an attribute equal to this attribute.
+     * 
+     * @param buf A buffer used for printing Java code.
+     * @param varName name of the variable in a printed code used to store
+     *        attribute instance.
+     * @param labelNames map of label instances to their names.
+     */
+    void asmify(StringBuffer buf, String varName, Map labelNames);
+}
diff --git a/asmx/src/org/objectweb/asm/util/attrs/Traceable.java b/asmx/src/org/objectweb/asm/util/attrs/Traceable.java
new file mode 100644
index 0000000..c40d2a6
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/util/attrs/Traceable.java
@@ -0,0 +1,52 @@
+/**
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.util.attrs;
+
+import java.util.Map;
+
+/**
+ * An attribute that can print eadable representation of the attribute.
+ * 
+ * Implementation should construct readable output from an attribute data
+ * structures for current attribute state. Such representation could be used in
+ * unit test assertions.
+ * 
+ * @author Eugene Kuleshov
+ */
+public interface Traceable {
+
+    /**
+     * Build a human readable representation of the attribute.
+     * 
+     * @param buf A buffer used for printing Java code.
+     * @param labelNames map of label instances to their names.
+     */
+    void trace(StringBuffer buf, Map labelNames);
+}
diff --git a/asmx/src/org/objectweb/asm/xml/ASMContentHandler.java b/asmx/src/org/objectweb/asm/xml/ASMContentHandler.java
new file mode 100644
index 0000000..1f6fee0
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/xml/ASMContentHandler.java
@@ -0,0 +1,1215 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Type;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A {@link org.xml.sax.ContentHandler ContentHandler} that transforms XML
+ * document into Java class file. This class can be feeded by any kind of SAX
+ * 2.0 event producers, e.g. XML parser, XSLT or XPath engines, or custom code.
+ * 
+ * @see org.objectweb.asm.xml.SAXClassAdapter
+ * @see org.objectweb.asm.xml.Processor
+ * 
+ * @author Eugene Kuleshov
+ */
+public class ASMContentHandler extends DefaultHandler implements Opcodes {
+    /**
+     * Stack of the intermediate processing contexts.
+     */
+    private List stack = new ArrayList();
+
+    /**
+     * Complete name of the current element.
+     */
+    private String match = "";
+
+    /**
+     * <tt>true</tt> if the maximum stack size and number of local variables
+     * must be automatically computed.
+     */
+    protected boolean computeMax;
+
+    /**
+     * Output stream to write result bytecode.
+     */
+    protected OutputStream os;
+
+    /**
+     * Current instance of the {@link ClassWriter ClassWriter} used to write
+     * class bytecode.
+     */
+    protected ClassWriter cw;
+
+    /**
+     * Map of the active {@link Label Label} instances for current method.
+     */
+    protected Map labels;
+
+    private static final String BASE = "class";
+
+    private final RuleSet RULES = new RuleSet();
+    {
+        RULES.add(BASE, new ClassRule());
+        RULES.add(BASE + "/interfaces/interface", new InterfaceRule());
+        RULES.add(BASE + "/interfaces", new InterfacesRule());
+        RULES.add(BASE + "/outerclass", new OuterClassRule());
+        RULES.add(BASE + "/innerclass", new InnerClassRule());
+        RULES.add(BASE + "/source", new SourceRule());
+        RULES.add(BASE + "/field", new FieldRule());
+
+        RULES.add(BASE + "/method", new MethodRule());
+        RULES.add(BASE + "/method/exceptions/exception", new ExceptionRule());
+        RULES.add(BASE + "/method/exceptions", new ExceptionsRule());
+
+        RULES.add(BASE + "/method/annotationDefault",
+                new AnnotationDefaultRule());
+
+        RULES.add(BASE + "/method/code/*", new OpcodesRule()); // opcodes
+
+        RULES.add(BASE + "/method/code/TABLESWITCH", new TableSwitchRule());
+        RULES.add(BASE + "/method/code/TABLESWITCH/label",
+                new TableSwitchLabelRule());
+        RULES.add(BASE + "/method/code/LOOKUPSWITCH", new LookupSwitchRule());
+        RULES.add(BASE + "/method/code/LOOKUPSWITCH/label",
+                new LookupSwitchLabelRule());
+
+        RULES.add(BASE + "/method/code/Label", new LabelRule());
+        RULES.add(BASE + "/method/code/TryCatch", new TryCatchRule());
+        RULES.add(BASE + "/method/code/LineNumber", new LineNumberRule());
+        RULES.add(BASE + "/method/code/LocalVar", new LocalVarRule());
+        RULES.add(BASE + "/method/code/Max", new MaxRule());
+
+        RULES.add("*/annotation", new AnnotationRule());
+        RULES.add("*/parameterAnnotation", new AnnotationParameterRule());
+        RULES.add("*/annotationValue", new AnnotationValueRule());
+        RULES.add("*/annotationValueAnnotation",
+                new AnnotationValueAnnotationRule());
+        RULES.add("*/annotationValueEnum", new AnnotationValueEnumRule());
+        RULES.add("*/annotationValueArray", new AnnotationValueArrayRule());
+    };
+
+    private static interface OpcodeGroup {
+        public static final int INSN = 0;
+        public static final int INSN_INT = 1;
+        public static final int INSN_VAR = 2;
+        public static final int INSN_TYPE = 3;
+        public static final int INSN_FIELD = 4;
+        public static final int INSN_METHOD = 5;
+        public static final int INSN_JUMP = 6;
+        public static final int INSN_LDC = 7;
+        public static final int INSN_IINC = 8;
+        public static final int INSN_MULTIANEWARRAY = 9;
+    }
+
+    /**
+     * Map of the opcode names to opcode and opcode group
+     */
+    static final Map OPCODES = new HashMap();
+    static {
+        OPCODES.put("NOP", new Opcode(NOP, OpcodeGroup.INSN));
+        OPCODES.put("ACONST_NULL", new Opcode(ACONST_NULL, OpcodeGroup.INSN));
+        OPCODES.put("ICONST_M1", new Opcode(ICONST_M1, OpcodeGroup.INSN));
+        OPCODES.put("ICONST_0", new Opcode(ICONST_0, OpcodeGroup.INSN));
+        OPCODES.put("ICONST_1", new Opcode(ICONST_1, OpcodeGroup.INSN));
+        OPCODES.put("ICONST_2", new Opcode(ICONST_2, OpcodeGroup.INSN));
+        OPCODES.put("ICONST_3", new Opcode(ICONST_3, OpcodeGroup.INSN));
+        OPCODES.put("ICONST_4", new Opcode(ICONST_4, OpcodeGroup.INSN));
+        OPCODES.put("ICONST_5", new Opcode(ICONST_5, OpcodeGroup.INSN));
+        OPCODES.put("LCONST_0", new Opcode(LCONST_0, OpcodeGroup.INSN));
+        OPCODES.put("LCONST_1", new Opcode(LCONST_1, OpcodeGroup.INSN));
+        OPCODES.put("FCONST_0", new Opcode(FCONST_0, OpcodeGroup.INSN));
+        OPCODES.put("FCONST_1", new Opcode(FCONST_1, OpcodeGroup.INSN));
+        OPCODES.put("FCONST_2", new Opcode(FCONST_2, OpcodeGroup.INSN));
+        OPCODES.put("DCONST_0", new Opcode(DCONST_0, OpcodeGroup.INSN));
+        OPCODES.put("DCONST_1", new Opcode(DCONST_1, OpcodeGroup.INSN));
+        OPCODES.put("BIPUSH", new Opcode(BIPUSH, OpcodeGroup.INSN_INT));
+        OPCODES.put("SIPUSH", new Opcode(SIPUSH, OpcodeGroup.INSN_INT));
+        OPCODES.put("LDC", new Opcode(LDC, OpcodeGroup.INSN_LDC));
+        OPCODES.put("ILOAD", new Opcode(ILOAD, OpcodeGroup.INSN_VAR));
+        OPCODES.put("LLOAD", new Opcode(LLOAD, OpcodeGroup.INSN_VAR));
+        OPCODES.put("FLOAD", new Opcode(FLOAD, OpcodeGroup.INSN_VAR));
+        OPCODES.put("DLOAD", new Opcode(DLOAD, OpcodeGroup.INSN_VAR));
+        OPCODES.put("ALOAD", new Opcode(ALOAD, OpcodeGroup.INSN_VAR));
+        OPCODES.put("IALOAD", new Opcode(IALOAD, OpcodeGroup.INSN));
+        OPCODES.put("LALOAD", new Opcode(LALOAD, OpcodeGroup.INSN));
+        OPCODES.put("FALOAD", new Opcode(FALOAD, OpcodeGroup.INSN));
+        OPCODES.put("DALOAD", new Opcode(DALOAD, OpcodeGroup.INSN));
+        OPCODES.put("AALOAD", new Opcode(AALOAD, OpcodeGroup.INSN));
+        OPCODES.put("BALOAD", new Opcode(BALOAD, OpcodeGroup.INSN));
+        OPCODES.put("CALOAD", new Opcode(CALOAD, OpcodeGroup.INSN));
+        OPCODES.put("SALOAD", new Opcode(SALOAD, OpcodeGroup.INSN));
+        OPCODES.put("ISTORE", new Opcode(ISTORE, OpcodeGroup.INSN_VAR));
+        OPCODES.put("LSTORE", new Opcode(LSTORE, OpcodeGroup.INSN_VAR));
+        OPCODES.put("FSTORE", new Opcode(FSTORE, OpcodeGroup.INSN_VAR));
+        OPCODES.put("DSTORE", new Opcode(DSTORE, OpcodeGroup.INSN_VAR));
+        OPCODES.put("ASTORE", new Opcode(ASTORE, OpcodeGroup.INSN_VAR));
+        OPCODES.put("IASTORE", new Opcode(IASTORE, OpcodeGroup.INSN));
+        OPCODES.put("LASTORE", new Opcode(LASTORE, OpcodeGroup.INSN));
+        OPCODES.put("FASTORE", new Opcode(FASTORE, OpcodeGroup.INSN));
+        OPCODES.put("DASTORE", new Opcode(DASTORE, OpcodeGroup.INSN));
+        OPCODES.put("AASTORE", new Opcode(AASTORE, OpcodeGroup.INSN));
+        OPCODES.put("BASTORE", new Opcode(BASTORE, OpcodeGroup.INSN));
+        OPCODES.put("CASTORE", new Opcode(CASTORE, OpcodeGroup.INSN));
+        OPCODES.put("SASTORE", new Opcode(SASTORE, OpcodeGroup.INSN));
+        OPCODES.put("POP", new Opcode(POP, OpcodeGroup.INSN));
+        OPCODES.put("POP2", new Opcode(POP2, OpcodeGroup.INSN));
+        OPCODES.put("DUP", new Opcode(DUP, OpcodeGroup.INSN));
+        OPCODES.put("DUP_X1", new Opcode(DUP_X1, OpcodeGroup.INSN));
+        OPCODES.put("DUP_X2", new Opcode(DUP_X2, OpcodeGroup.INSN));
+        OPCODES.put("DUP2", new Opcode(DUP2, OpcodeGroup.INSN));
+        OPCODES.put("DUP2_X1", new Opcode(DUP2_X1, OpcodeGroup.INSN));
+        OPCODES.put("DUP2_X2", new Opcode(DUP2_X2, OpcodeGroup.INSN));
+        OPCODES.put("SWAP", new Opcode(SWAP, OpcodeGroup.INSN));
+        OPCODES.put("IADD", new Opcode(IADD, OpcodeGroup.INSN));
+        OPCODES.put("LADD", new Opcode(LADD, OpcodeGroup.INSN));
+        OPCODES.put("FADD", new Opcode(FADD, OpcodeGroup.INSN));
+        OPCODES.put("DADD", new Opcode(DADD, OpcodeGroup.INSN));
+        OPCODES.put("ISUB", new Opcode(ISUB, OpcodeGroup.INSN));
+        OPCODES.put("LSUB", new Opcode(LSUB, OpcodeGroup.INSN));
+        OPCODES.put("FSUB", new Opcode(FSUB, OpcodeGroup.INSN));
+        OPCODES.put("DSUB", new Opcode(DSUB, OpcodeGroup.INSN));
+        OPCODES.put("IMUL", new Opcode(IMUL, OpcodeGroup.INSN));
+        OPCODES.put("LMUL", new Opcode(LMUL, OpcodeGroup.INSN));
+        OPCODES.put("FMUL", new Opcode(FMUL, OpcodeGroup.INSN));
+        OPCODES.put("DMUL", new Opcode(DMUL, OpcodeGroup.INSN));
+        OPCODES.put("IDIV", new Opcode(IDIV, OpcodeGroup.INSN));
+        OPCODES.put("LDIV", new Opcode(LDIV, OpcodeGroup.INSN));
+        OPCODES.put("FDIV", new Opcode(FDIV, OpcodeGroup.INSN));
+        OPCODES.put("DDIV", new Opcode(DDIV, OpcodeGroup.INSN));
+        OPCODES.put("IREM", new Opcode(IREM, OpcodeGroup.INSN));
+        OPCODES.put("LREM", new Opcode(LREM, OpcodeGroup.INSN));
+        OPCODES.put("FREM", new Opcode(FREM, OpcodeGroup.INSN));
+        OPCODES.put("DREM", new Opcode(DREM, OpcodeGroup.INSN));
+        OPCODES.put("INEG", new Opcode(INEG, OpcodeGroup.INSN));
+        OPCODES.put("LNEG", new Opcode(LNEG, OpcodeGroup.INSN));
+        OPCODES.put("FNEG", new Opcode(FNEG, OpcodeGroup.INSN));
+        OPCODES.put("DNEG", new Opcode(DNEG, OpcodeGroup.INSN));
+        OPCODES.put("ISHL", new Opcode(ISHL, OpcodeGroup.INSN));
+        OPCODES.put("LSHL", new Opcode(LSHL, OpcodeGroup.INSN));
+        OPCODES.put("ISHR", new Opcode(ISHR, OpcodeGroup.INSN));
+        OPCODES.put("LSHR", new Opcode(LSHR, OpcodeGroup.INSN));
+        OPCODES.put("IUSHR", new Opcode(IUSHR, OpcodeGroup.INSN));
+        OPCODES.put("LUSHR", new Opcode(LUSHR, OpcodeGroup.INSN));
+        OPCODES.put("IAND", new Opcode(IAND, OpcodeGroup.INSN));
+        OPCODES.put("LAND", new Opcode(LAND, OpcodeGroup.INSN));
+        OPCODES.put("IOR", new Opcode(IOR, OpcodeGroup.INSN));
+        OPCODES.put("LOR", new Opcode(LOR, OpcodeGroup.INSN));
+        OPCODES.put("IXOR", new Opcode(IXOR, OpcodeGroup.INSN));
+        OPCODES.put("LXOR", new Opcode(LXOR, OpcodeGroup.INSN));
+        OPCODES.put("IINC", new Opcode(IINC, OpcodeGroup.INSN_IINC));
+        OPCODES.put("I2L", new Opcode(I2L, OpcodeGroup.INSN));
+        OPCODES.put("I2F", new Opcode(I2F, OpcodeGroup.INSN));
+        OPCODES.put("I2D", new Opcode(I2D, OpcodeGroup.INSN));
+        OPCODES.put("L2I", new Opcode(L2I, OpcodeGroup.INSN));
+        OPCODES.put("L2F", new Opcode(L2F, OpcodeGroup.INSN));
+        OPCODES.put("L2D", new Opcode(L2D, OpcodeGroup.INSN));
+        OPCODES.put("F2I", new Opcode(F2I, OpcodeGroup.INSN));
+        OPCODES.put("F2L", new Opcode(F2L, OpcodeGroup.INSN));
+        OPCODES.put("F2D", new Opcode(F2D, OpcodeGroup.INSN));
+        OPCODES.put("D2I", new Opcode(D2I, OpcodeGroup.INSN));
+        OPCODES.put("D2L", new Opcode(D2L, OpcodeGroup.INSN));
+        OPCODES.put("D2F", new Opcode(D2F, OpcodeGroup.INSN));
+        OPCODES.put("I2B", new Opcode(I2B, OpcodeGroup.INSN));
+        OPCODES.put("I2C", new Opcode(I2C, OpcodeGroup.INSN));
+        OPCODES.put("I2S", new Opcode(I2S, OpcodeGroup.INSN));
+        OPCODES.put("LCMP", new Opcode(LCMP, OpcodeGroup.INSN));
+        OPCODES.put("FCMPL", new Opcode(FCMPL, OpcodeGroup.INSN));
+        OPCODES.put("FCMPG", new Opcode(FCMPG, OpcodeGroup.INSN));
+        OPCODES.put("DCMPL", new Opcode(DCMPL, OpcodeGroup.INSN));
+        OPCODES.put("DCMPG", new Opcode(DCMPG, OpcodeGroup.INSN));
+        OPCODES.put("IFEQ", new Opcode(IFEQ, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IFNE", new Opcode(IFNE, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IFLT", new Opcode(IFLT, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IFGE", new Opcode(IFGE, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IFGT", new Opcode(IFGT, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IFLE", new Opcode(IFLE, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ICMPEQ", new Opcode(IF_ICMPEQ, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ICMPNE", new Opcode(IF_ICMPNE, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ICMPLT", new Opcode(IF_ICMPLT, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ICMPGE", new Opcode(IF_ICMPGE, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ICMPGT", new Opcode(IF_ICMPGT, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ICMPLE", new Opcode(IF_ICMPLE, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ACMPEQ", new Opcode(IF_ACMPEQ, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IF_ACMPNE", new Opcode(IF_ACMPNE, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("GOTO", new Opcode(GOTO, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("JSR", new Opcode(JSR, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("RET", new Opcode(RET, OpcodeGroup.INSN_VAR));
+        // OPCODES.put( "TABLESWITCH", new Opcode( TABLESWITCH,
+        // "visiTableSwitchInsn"));
+        // OPCODES.put( "LOOKUPSWITCH", new Opcode( LOOKUPSWITCH,
+        // "visitLookupSwitch"));
+        OPCODES.put("IRETURN", new Opcode(IRETURN, OpcodeGroup.INSN));
+        OPCODES.put("LRETURN", new Opcode(LRETURN, OpcodeGroup.INSN));
+        OPCODES.put("FRETURN", new Opcode(FRETURN, OpcodeGroup.INSN));
+        OPCODES.put("DRETURN", new Opcode(DRETURN, OpcodeGroup.INSN));
+        OPCODES.put("ARETURN", new Opcode(ARETURN, OpcodeGroup.INSN));
+        OPCODES.put("RETURN", new Opcode(RETURN, OpcodeGroup.INSN));
+        OPCODES.put("GETSTATIC", new Opcode(GETSTATIC, OpcodeGroup.INSN_FIELD));
+        OPCODES.put("PUTSTATIC", new Opcode(PUTSTATIC, OpcodeGroup.INSN_FIELD));
+        OPCODES.put("GETFIELD", new Opcode(GETFIELD, OpcodeGroup.INSN_FIELD));
+        OPCODES.put("PUTFIELD", new Opcode(PUTFIELD, OpcodeGroup.INSN_FIELD));
+        OPCODES.put("INVOKEVIRTUAL", new Opcode(INVOKEVIRTUAL,
+                OpcodeGroup.INSN_METHOD));
+        OPCODES.put("INVOKESPECIAL", new Opcode(INVOKESPECIAL,
+                OpcodeGroup.INSN_METHOD));
+        OPCODES.put("INVOKESTATIC", new Opcode(INVOKESTATIC,
+                OpcodeGroup.INSN_METHOD));
+        OPCODES.put("INVOKEINTERFACE", new Opcode(INVOKEINTERFACE,
+                OpcodeGroup.INSN_METHOD));
+        OPCODES.put("NEW", new Opcode(NEW, OpcodeGroup.INSN_TYPE));
+        OPCODES.put("NEWARRAY", new Opcode(NEWARRAY, OpcodeGroup.INSN_INT));
+        OPCODES.put("ANEWARRAY", new Opcode(ANEWARRAY, OpcodeGroup.INSN_TYPE));
+        OPCODES.put("ARRAYLENGTH", new Opcode(ARRAYLENGTH, OpcodeGroup.INSN));
+        OPCODES.put("ATHROW", new Opcode(ATHROW, OpcodeGroup.INSN));
+        OPCODES.put("CHECKCAST", new Opcode(CHECKCAST, OpcodeGroup.INSN_TYPE));
+        OPCODES.put("INSTANCEOF", new Opcode(INSTANCEOF, OpcodeGroup.INSN_TYPE));
+        OPCODES.put("MONITORENTER", new Opcode(MONITORENTER, OpcodeGroup.INSN));
+        OPCODES.put("MONITOREXIT", new Opcode(MONITOREXIT, OpcodeGroup.INSN));
+        OPCODES.put("MULTIANEWARRAY", new Opcode(MULTIANEWARRAY,
+                OpcodeGroup.INSN_MULTIANEWARRAY));
+        OPCODES.put("IFNULL", new Opcode(IFNULL, OpcodeGroup.INSN_JUMP));
+        OPCODES.put("IFNONNULL", new Opcode(IFNONNULL, OpcodeGroup.INSN_JUMP));
+    }
+
+    /**
+     * Constructs a new {@link ASMContentHandler ASMContentHandler} object.
+     * 
+     * @param os output stream to write generated class.
+     * @param computeMax <tt>true</tt> if the maximum stack size and the
+     *        maximum number of local variables must be automatically computed.
+     *        This value is passed to {@link ClassWriter ClassWriter} instance.
+     */
+    public ASMContentHandler(OutputStream os, boolean computeMax) {
+        this.os = os;
+        this.computeMax = computeMax;
+    }
+
+    /**
+     * Returns the bytecode of the class that was build with underneath class
+     * writer.
+     * 
+     * @return the bytecode of the class that was build with underneath class
+     *         writer or null if there are no classwriter created.
+     */
+    public byte[] toByteArray() {
+        return cw == null ? null : cw.toByteArray();
+    }
+
+    /**
+     * Process notification of the start of an XML element being reached.
+     * 
+     * @param ns - The Namespace URI, or the empty string if the element has no
+     *        Namespace URI or if Namespace processing is not being performed.
+     * @param localName - The local name (without prefix), or the empty string
+     *        if Namespace processing is not being performed.
+     * @param qName - The qualified name (with prefix), or the empty string if
+     *        qualified names are not available.
+     * @param list - The attributes attached to the element. If there are no
+     *        attributes, it shall be an empty Attributes object.
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public final void startElement(
+        String ns,
+        String localName,
+        String qName,
+        Attributes list) throws SAXException
+    {
+        // the actual element name is either in localName or qName, depending
+        // on whether the parser is namespace aware
+        String name = localName;
+        if (name == null || name.length() < 1) {
+            name = qName;
+        }
+
+        // Compute the current matching rule
+        StringBuffer sb = new StringBuffer(match);
+        if (match.length() > 0) {
+            sb.append('/');
+        }
+        sb.append(name);
+        match = sb.toString();
+
+        // Fire "begin" events for all relevant rules
+        Rule r = (Rule) RULES.match(match);
+        if (r != null)
+            r.begin(name, list);
+    }
+
+    /**
+     * Process notification of the end of an XML element being reached.
+     * 
+     * @param ns - The Namespace URI, or the empty string if the element has no
+     *        Namespace URI or if Namespace processing is not being performed.
+     * @param localName - The local name (without prefix), or the empty string
+     *        if Namespace processing is not being performed.
+     * @param qName - The qualified XML 1.0 name (with prefix), or the empty
+     *        string if qualified names are not available.
+     * 
+     * @exception SAXException if a parsing error is to be reported
+     */
+    public final void endElement(String ns, String localName, String qName)
+            throws SAXException
+    {
+        // the actual element name is either in localName or qName, depending
+        // on whether the parser is namespace aware
+        String name = localName;
+        if (name == null || name.length() < 1) {
+            name = qName;
+        }
+
+        // Fire "end" events for all relevant rules in reverse order
+        Rule r = (Rule) RULES.match(match);
+        if (r != null)
+            r.end(name);
+
+        // Recover the previous match expression
+        int slash = match.lastIndexOf('/');
+        if (slash >= 0) {
+            match = match.substring(0, slash);
+        } else {
+            match = "";
+        }
+    }
+
+    /**
+     * Process notification of the end of a document and write generated
+     * bytecode into output stream.
+     * 
+     * @exception SAXException if parsing or writing error is to be reported.
+     */
+    public final void endDocument() throws SAXException {
+        try {
+            os.write(cw.toByteArray());
+        } catch (IOException ex) {
+            throw new SAXException(ex.toString(), ex);
+        }
+    }
+
+    /**
+     * Return the top object on the stack without removing it. If there are no
+     * objects on the stack, return <code>null</code>.
+     * 
+     * @return the top object on the stack without removing it.
+     */
+    final Object peek() {
+        return stack.size() == 0 ? null : stack.get(stack.size() - 1);
+    }
+
+    /**
+     * Return the n'th object down the stack, where 0 is the top element and
+     * [getCount()-1] is the bottom element. If the specified index is out of
+     * range, return <code>null</code>.
+     * 
+     * @param n Index of the desired element, where 0 is the top of the stack, 1
+     *        is the next element down, and so on.
+     * @return the n'th object down the stack.
+     */
+    final Object peek(int n) {
+        return stack.size() < (n + 1) ? null : stack.get(n);
+    }
+
+    /**
+     * Pop the top object off of the stack, and return it. If there are no
+     * objects on the stack, return <code>null</code>.
+     * 
+     * @return the top object off of the stack.
+     */
+    final Object pop() {
+        return stack.size() == 0 ? null : stack.remove(stack.size() - 1);
+    }
+
+    /**
+     * Push a new object onto the top of the object stack.
+     * 
+     * @param object The new object
+     */
+    final void push(Object object) {
+        stack.add(object);
+    }
+
+    private static final class RuleSet {
+        private Map rules = new HashMap();
+
+        private List lpatterns = new ArrayList();
+
+        private List rpatterns = new ArrayList();
+
+        public void add(String path, Object rule) {
+            String pattern = path;
+            if (path.startsWith("*/")) {
+                pattern = path.substring(1);
+                lpatterns.add(pattern);
+            } else if (path.endsWith("/*")) {
+                pattern = path.substring(0, path.length() - 1);
+                rpatterns.add(pattern);
+            }
+            rules.put(pattern, rule);
+        }
+
+        public Object match(String path) {
+            if (rules.containsKey(path)) {
+                return rules.get(path);
+            }
+
+            int n = path.lastIndexOf('/');
+            for (Iterator it = lpatterns.iterator(); it.hasNext();) {
+                String pattern = (String) it.next();
+                if (path.substring(n).endsWith(pattern)) {
+                    return rules.get(pattern);
+                }
+            }
+
+            for (Iterator it = rpatterns.iterator(); it.hasNext();) {
+                String pattern = (String) it.next();
+                if (path.startsWith(pattern)) {
+                    return rules.get(pattern);
+                }
+            }
+
+            return null;
+        }
+
+    }
+
+    /**
+     * Rule
+     */
+    protected abstract class Rule {
+
+        public void begin(String name, Attributes attrs) {
+        }
+
+        public void end(String name) {
+        }
+
+        protected final Object getValue(String desc, String val) {
+            Object value = null;
+            if (val != null) {
+                if (desc.equals("Ljava/lang/String;")) {
+                    value = decode(val);
+                } else if ("Ljava/lang/Integer;".equals(desc)
+                        || "I".equals(desc) || "S".equals(desc)
+                        || "B".equals(desc) || "C".equals(desc)
+                        || desc.equals("Z"))
+                {
+                    value = new Integer(val);
+
+                } else if ("Ljava/lang/Short;".equals(desc)) {
+                    value = new Short(val);
+
+                } else if ("Ljava/lang/Byte;".equals(desc)) {
+                    value = new Byte(val);
+
+                } else if ("Ljava/lang/Character;".equals(desc)) {
+                    value = new Character(decode(val).charAt(0));
+
+                } else if ("Ljava/lang/Boolean;".equals(desc)) {
+                    value = Boolean.valueOf(val);
+
+                    // } else if ("Ljava/lang/Integer;".equals(desc)
+                    // || desc.equals("I"))
+                    // {
+                    // value = new Integer(val);
+                    // } else if ("Ljava/lang/Character;".equals(desc)
+                    // || desc.equals("C"))
+                    // {
+                    // value = new Character(decode(val).charAt(0));
+                    // } else if ("Ljava/lang/Short;".equals(desc) ||
+                    // desc.equals("S"))
+                    // {
+                    // value = Short.valueOf(val);
+                    // } else if ("Ljava/lang/Byte;".equals(desc) ||
+                    // desc.equals("B"))
+                    // {
+                    // value = Byte.valueOf(val);
+
+                } else if ("Ljava/lang/Long;".equals(desc) || desc.equals("J"))
+                {
+                    value = new Long(val);
+                } else if ("Ljava/lang/Float;".equals(desc) || desc.equals("F"))
+                {
+                    value = new Float(val);
+                } else if ("Ljava/lang/Double;".equals(desc)
+                        || desc.equals("D"))
+                {
+                    value = new Double(val);
+                } else if (Type.getDescriptor(Type.class).equals(desc)) {
+                    value = Type.getType(val);
+
+                    // } else if ("[I".equals(desc)) {
+                    // value = new int[0]; // TODO
+                    // } else if ("[C".equals(desc)) {
+                    // value = new char[0]; // TODO
+                    // } else if ("[Z".equals(desc)) {
+                    // value = new boolean[0]; // TODO
+                    // } else if ("[S".equals(desc)) {
+                    // value = new short[0]; // TODO
+                    // } else if ("[B".equals(desc)) {
+                    // value = new byte[0]; // TODO
+                    // } else if ("[J".equals(desc)) {
+                    // value = new long[0]; // TODO
+                    // } else if ("[F".equals(desc)) {
+                    // value = new float[0]; // TODO
+                    // } else if ("[D".equals(desc)) {
+                    // value = new double[0]; // TODO
+
+                } else {
+                    throw new RuntimeException("Invalid value:" + val
+                            + " desc:" + desc + " ctx:" + this);
+                }
+            }
+            return value;
+        }
+
+        private final String decode(String val) {
+            StringBuffer sb = new StringBuffer(val.length());
+            try {
+                int n = 0;
+                while (n < val.length()) {
+                    char c = val.charAt(n);
+                    if (c == '\\') {
+                        n++;
+                        c = val.charAt(n);
+                        if (c == '\\') {
+                            sb.append('\\');
+                        } else {
+                            n++; // skip 'u'
+                            sb.append((char) Integer.parseInt(val.substring(n,
+                                    n + 4), 16));
+                            n += 3;
+                        }
+                    } else {
+                        sb.append(c);
+                    }
+                    n++;
+                }
+
+            } catch (RuntimeException ex) {
+                System.err.println(val + "\n" + ex.toString());
+                ex.printStackTrace();
+                throw ex;
+            }
+            return sb.toString();
+        }
+
+        protected final Label getLabel(Object label) {
+            Label lbl = (Label) labels.get(label);
+            if (lbl == null) {
+                lbl = new Label();
+                labels.put(label, lbl);
+            }
+            return lbl;
+        }
+
+        // TODO verify move to stack
+        protected final MethodVisitor getCodeVisitor() {
+            return (MethodVisitor) peek();
+        }
+        
+        protected final int getAccess(String s) {
+            int access = 0;
+            if (s.indexOf("public") != -1)
+                access |= Opcodes.ACC_PUBLIC;
+            if (s.indexOf("private") != -1)
+                access |= Opcodes.ACC_PRIVATE;
+            if (s.indexOf("protected") != -1)
+                access |= Opcodes.ACC_PROTECTED;
+            if (s.indexOf("static") != -1)
+                access |= Opcodes.ACC_STATIC;
+            if (s.indexOf("final") != -1)
+                access |= Opcodes.ACC_FINAL;
+            if (s.indexOf("super") != -1)
+                access |= Opcodes.ACC_SUPER;
+            if (s.indexOf("synchronized") != -1)
+                access |= Opcodes.ACC_SYNCHRONIZED;
+            if (s.indexOf("volatile") != -1)
+                access |= Opcodes.ACC_VOLATILE;
+            if (s.indexOf("bridge") != -1)
+                access |= Opcodes.ACC_BRIDGE;
+            if (s.indexOf("varargs") != -1)
+                access |= Opcodes.ACC_VARARGS;
+            if (s.indexOf("transient") != -1)
+                access |= Opcodes.ACC_TRANSIENT;
+            if (s.indexOf("native") != -1)
+                access |= Opcodes.ACC_NATIVE;
+            if (s.indexOf("interface") != -1)
+                access |= Opcodes.ACC_INTERFACE;
+            if (s.indexOf("abstract") != -1)
+                access |= Opcodes.ACC_ABSTRACT;
+            if (s.indexOf("strict") != -1)
+                access |= Opcodes.ACC_STRICT;
+            if (s.indexOf("synthetic") != -1)
+                access |= Opcodes.ACC_SYNTHETIC;
+            if (s.indexOf("annotation") != -1)
+                access |= Opcodes.ACC_ANNOTATION;
+            if (s.indexOf("enum") != -1)
+                access |= Opcodes.ACC_ENUM;
+            if (s.indexOf("deprecated") != -1)
+                access |= Opcodes.ACC_DEPRECATED;
+            return access;
+        }
+
+    }
+
+    /**
+     * ClassRule
+     */
+    private final class ClassRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            int major = Integer.parseInt(attrs.getValue("major"));
+            int minor = Integer.parseInt(attrs.getValue("minor"));
+            cw = new ClassWriter(computeMax);
+            Map vals = new HashMap();
+            vals.put("version", new Integer(minor << 16 | major));
+            vals.put("access", attrs.getValue("access"));
+            vals.put("name", attrs.getValue("name"));
+            vals.put("parent", attrs.getValue("parent"));
+            vals.put("source", attrs.getValue("source"));
+            vals.put("signature", attrs.getValue("signature"));
+            vals.put("interfaces", new ArrayList());
+            push(vals);
+            // values will be extracted in InterfacesRule.end();
+        }
+
+    }
+
+    private final class SourceRule extends Rule {
+
+        public void begin(String name, Attributes attrs) {
+            String file = attrs.getValue("file");
+            String debug = attrs.getValue("debug");
+            cw.visitSource(file, debug);
+        }
+
+    }
+
+    /**
+     * InterfaceRule
+     */
+    private final class InterfaceRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            ((List) ((Map) peek()).get("interfaces")).add(attrs.getValue("name"));
+        }
+
+    }
+
+    /**
+     * InterfacesRule
+     */
+    private final class InterfacesRule extends Rule {
+
+        public final void end(String element) {
+            Map vals = (Map) pop();
+            int version = ((Integer) vals.get("version")).intValue();
+            int access = getAccess((String) vals.get("access"));
+            String name = (String) vals.get("name");
+            String signature = (String) vals.get("signature");
+            String parent = (String) vals.get("parent");
+            List infs = (List) vals.get("interfaces");
+            String[] interfaces = (String[]) infs.toArray(new String[infs.size()]);
+            cw.visit(version, access, name, signature, parent, interfaces);
+            push(cw);
+        }
+
+    }
+
+    /**
+     * OuterClassRule
+     */
+    private final class OuterClassRule extends Rule {
+
+        public final void begin(String element, Attributes attrs) {
+            String owner = attrs.getValue("owner");
+            String name = attrs.getValue("name");
+            String desc = attrs.getValue("desc");
+            cw.visitOuterClass(owner, name, desc);
+        }
+
+    }
+
+    /**
+     * InnerClassRule
+     */
+    private final class InnerClassRule extends Rule {
+
+        public final void begin(String element, Attributes attrs) {
+            int access = getAccess(attrs.getValue("access"));
+            String name = attrs.getValue("name");
+            String outerName = attrs.getValue("outerName");
+            String innerName = attrs.getValue("innerName");
+            cw.visitInnerClass(name, outerName, innerName, access);
+        }
+
+    }
+
+    /**
+     * FieldRule
+     */
+    private final class FieldRule extends Rule {
+
+        public final void begin(String element, Attributes attrs) {
+            int access = getAccess(attrs.getValue("access"));
+            String name = attrs.getValue("name");
+            String signature = attrs.getValue("signature");
+            String desc = attrs.getValue("desc");
+            Object value = getValue(desc, attrs.getValue("value"));
+            push(cw.visitField(access, name, desc, signature, value));
+        }
+
+        public void end(String name) {
+            ((FieldVisitor) pop()).visitEnd();
+        }
+
+    }
+
+    /**
+     * MethodRule
+     */
+    private final class MethodRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            labels = new HashMap();
+            Map vals = new HashMap();
+            vals.put("access", attrs.getValue("access"));
+            vals.put("name", attrs.getValue("name"));
+            vals.put("desc", attrs.getValue("desc"));
+            vals.put("signature", attrs.getValue("signature"));
+            vals.put("exceptions", new ArrayList());
+            push(vals);
+            // values will be extracted in ExceptionsRule.end();
+        }
+
+        public final void end(String name) {
+            ((MethodVisitor) pop()).visitEnd();
+            labels = null;
+        }
+
+    }
+
+    /**
+     * ExceptionRule
+     */
+    private final class ExceptionRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            ((List) ((Map) peek()).get("exceptions")).add(attrs.getValue("name"));
+        }
+
+    }
+
+    /**
+     * ExceptionsRule
+     */
+    private final class ExceptionsRule extends Rule {
+
+        public final void end(String element) {
+            Map vals = (Map) pop();
+            int access = getAccess((String) vals.get("access"));
+            String name = (String) vals.get("name");
+            String desc = (String) vals.get("desc");
+            String signature = (String) vals.get("signature");
+            List excs = (List) vals.get("exceptions");
+            String[] exceptions = (String[]) excs.toArray(new String[excs.size()]);
+
+            push(cw.visitMethod(access, name, desc, signature, exceptions));
+        }
+
+    }
+
+    /**
+     * TableSwitchRule
+     */
+    private class TableSwitchRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            Map vals = new HashMap();
+            vals.put("min", attrs.getValue("min"));
+            vals.put("max", attrs.getValue("max"));
+            vals.put("dflt", attrs.getValue("dflt"));
+            vals.put("labels", new ArrayList());
+            push(vals);
+        }
+
+        public final void end(String name) {
+            Map vals = (Map) pop();
+            int min = Integer.parseInt((String) vals.get("min"));
+            int max = Integer.parseInt((String) vals.get("max"));
+            Label dflt = getLabel(vals.get("dflt"));
+            List lbls = (List) vals.get("labels");
+            Label[] labels = (Label[]) lbls.toArray(new Label[lbls.size()]);
+            getCodeVisitor().visitTableSwitchInsn(min, max, dflt, labels);
+        }
+
+    }
+
+    /**
+     * TableSwitchLabelRule
+     */
+    private final class TableSwitchLabelRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            ((List) ((Map) peek()).get("labels")).add(getLabel(attrs.getValue("name")));
+        }
+
+    }
+
+    /**
+     * LookupSwitchRule
+     */
+    private final class LookupSwitchRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            Map vals = new HashMap();
+            vals.put("dflt", attrs.getValue("dflt"));
+            vals.put("labels", new ArrayList());
+            vals.put("keys", new ArrayList());
+            push(vals);
+        }
+
+        public final void end(String name) {
+            Map vals = (Map) pop();
+            Label dflt = getLabel(vals.get("dflt"));
+            List keyList = (List) vals.get("keys");
+            List lbls = (List) vals.get("labels");
+            Label[] labels = (Label[]) lbls.toArray(new Label[lbls.size()]);
+            int[] keys = new int[keyList.size()];
+            for (int i = 0; i < keys.length; i++) {
+                keys[i] = Integer.parseInt((String) keyList.get(i));
+            }
+            getCodeVisitor().visitLookupSwitchInsn(dflt, keys, labels);
+        }
+
+    }
+
+    /**
+     * LookupSwitchLabelRule
+     */
+    private final class LookupSwitchLabelRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            Map vals = (Map) peek();
+            ((List) vals.get("labels")).add(getLabel(attrs.getValue("name")));
+            ((List) vals.get("keys")).add(attrs.getValue("key"));
+        }
+
+    }
+
+    /**
+     * LabelRule
+     */
+    private final class LabelRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            getCodeVisitor().visitLabel(getLabel(attrs.getValue("name")));
+        }
+
+    }
+
+    /**
+     * TryCatchRule
+     */
+    private final class TryCatchRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            Label start = getLabel(attrs.getValue("start"));
+            Label end = getLabel(attrs.getValue("end"));
+            Label handler = getLabel(attrs.getValue("handler"));
+            String type = attrs.getValue("type");
+            getCodeVisitor().visitTryCatchBlock(start, end, handler, type);
+        }
+
+    }
+
+    /**
+     * LineNumberRule
+     */
+    private final class LineNumberRule extends Rule {
+
+        public final void begin(String name, Attributes attrs) {
+            int line = Integer.parseInt(attrs.getValue("line"));
+            Label start = getLabel(attrs.getValue("start"));
+            getCodeVisitor().visitLineNumber(line, start);
+        }
+
+    }
+
+    /**
+     * LocalVarRule
+     */
+    private final class LocalVarRule extends Rule {
+
+        public final void begin(String element, Attributes attrs) {
+            String name = attrs.getValue("name");
+            String desc = attrs.getValue("desc");
+            String signature = attrs.getValue("signature");
+            Label start = getLabel(attrs.getValue("start"));
+            Label end = getLabel(attrs.getValue("end"));
+            int var = Integer.parseInt(attrs.getValue("var"));
+            getCodeVisitor().visitLocalVariable(name,
+                    desc,
+                    signature,
+                    start,
+                    end,
+                    var);
+        }
+
+    }
+
+    /**
+     * OpcodesRule
+     */
+    private final class OpcodesRule extends Rule {
+
+        // public boolean match( String match, String element) {
+        // return match.startsWith( path) && OPCODES.containsKey( element);
+        // }
+
+        public final void begin(String element, Attributes attrs) {
+            Opcode o = ((Opcode) OPCODES.get(element));
+            if (o == null)
+                return;
+
+            switch (o.type) {
+                case OpcodeGroup.INSN:
+                    getCodeVisitor().visitInsn(o.opcode);
+                    break;
+
+                case OpcodeGroup.INSN_FIELD:
+                    getCodeVisitor().visitFieldInsn(o.opcode,
+                            attrs.getValue("owner"),
+                            attrs.getValue("name"),
+                            attrs.getValue("desc"));
+                    break;
+
+                case OpcodeGroup.INSN_INT:
+                    getCodeVisitor().visitIntInsn(o.opcode,
+                            Integer.parseInt(attrs.getValue("value")));
+                    break;
+
+                case OpcodeGroup.INSN_JUMP:
+                    getCodeVisitor().visitJumpInsn(o.opcode,
+                            getLabel(attrs.getValue("label")));
+                    break;
+
+                case OpcodeGroup.INSN_METHOD:
+                    getCodeVisitor().visitMethodInsn(o.opcode,
+                            attrs.getValue("owner"),
+                            attrs.getValue("name"),
+                            attrs.getValue("desc"));
+                    break;
+
+                case OpcodeGroup.INSN_TYPE:
+                    getCodeVisitor().visitTypeInsn(o.opcode,
+                            attrs.getValue("desc"));
+                    break;
+
+                case OpcodeGroup.INSN_VAR:
+                    getCodeVisitor().visitVarInsn(o.opcode,
+                            Integer.parseInt(attrs.getValue("var")));
+                    break;
+
+                case OpcodeGroup.INSN_IINC:
+                    getCodeVisitor().visitIincInsn(Integer.parseInt(attrs.getValue("var")),
+                            Integer.parseInt(attrs.getValue("inc")));
+                    break;
+
+                case OpcodeGroup.INSN_LDC:
+                    getCodeVisitor().visitLdcInsn(getValue(attrs.getValue("desc"),
+                            attrs.getValue("cst")));
+                    break;
+
+                case OpcodeGroup.INSN_MULTIANEWARRAY:
+                    getCodeVisitor().visitMultiANewArrayInsn(attrs.getValue("desc"),
+                            Integer.parseInt(attrs.getValue("dims")));
+                    break;
+
+                default:
+                    throw new RuntimeException("Invalid element: " + element
+                            + " at " + match);
+
+            }
+        }
+    }
+
+    /**
+     * MaxRule
+     */
+    private final class MaxRule extends Rule {
+
+        public final void begin(String element, Attributes attrs) {
+            int maxStack = Integer.parseInt(attrs.getValue("maxStack"));
+            int maxLocals = Integer.parseInt(attrs.getValue("maxLocals"));
+            getCodeVisitor().visitMaxs(maxStack, maxLocals);
+        }
+
+    }
+
+    private final class AnnotationRule extends Rule {
+
+        public void begin(String name, Attributes attrs) {
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+
+            Object v = peek();
+            if (v instanceof ClassVisitor) {
+                push(((ClassVisitor) v).visitAnnotation(desc, visible));
+            } else if (v instanceof FieldVisitor) {
+                push(((FieldVisitor) v).visitAnnotation(desc, visible));
+            } else if (v instanceof MethodVisitor) {
+                push(((MethodVisitor) v).visitAnnotation(desc, visible));
+            }
+        }
+
+        public void end(String name) {
+            ((AnnotationVisitor) pop()).visitEnd();
+        }
+
+    }
+
+    private final class AnnotationParameterRule extends Rule {
+
+        public void begin(String name, Attributes attrs) {
+            int parameter = Integer.parseInt(attrs.getValue("parameter"));
+            String desc = attrs.getValue("desc");
+            boolean visible = Boolean.valueOf(attrs.getValue("visible"))
+                    .booleanValue();
+
+            push(((MethodVisitor) peek()).visitParameterAnnotation(parameter,
+                    desc,
+                    visible));
+        }
+
+        public void end(String name) {
+            ((AnnotationVisitor) pop()).visitEnd();
+        }
+
+    }
+
+    private final class AnnotationValueRule extends Rule {
+
+        public void begin(String nm, Attributes attrs) {
+            String name = attrs.getValue("name");
+            String desc = attrs.getValue("desc");
+            String value = attrs.getValue("value");
+            ((AnnotationVisitor) peek()).visit(name, getValue(desc, value));
+        }
+
+    }
+
+    private final class AnnotationValueEnumRule extends Rule {
+
+        public void begin(String nm, Attributes attrs) {
+            String name = attrs.getValue("name");
+            String desc = attrs.getValue("desc");
+            String value = attrs.getValue("value");
+            ((AnnotationVisitor) peek()).visitEnum(name, desc, value);
+        }
+
+    }
+
+    private final class AnnotationValueAnnotationRule extends Rule {
+
+        public void begin(String nm, Attributes attrs) {
+            String name = attrs.getValue("name");
+            String desc = attrs.getValue("desc");
+            push(((AnnotationVisitor) peek()).visitAnnotation(name, desc));
+        }
+
+        public void end(String name) {
+            ((AnnotationVisitor) pop()).visitEnd();
+        }
+
+    }
+
+    private final class AnnotationValueArrayRule extends Rule {
+
+        public void begin(String nm, Attributes attrs) {
+            String name = attrs.getValue("name");
+            push(((AnnotationVisitor) peek()).visitArray(name));
+        }
+
+        public void end(String name) {
+            ((AnnotationVisitor) pop()).visitEnd();
+        }
+
+    }
+
+    private final class AnnotationDefaultRule extends Rule {
+
+        public void begin(String nm, Attributes attrs) {
+            push(((MethodVisitor) peek()).visitAnnotationDefault());
+        }
+
+        public void end(String name) {
+            ((AnnotationVisitor) pop()).visitEnd();
+        }
+
+    }
+
+    /**
+     * Opcode
+     */
+    private final static class Opcode {
+        public int opcode;
+
+        public int type;
+
+        public Opcode(int opcode, int type) {
+            this.opcode = opcode;
+            this.type = type;
+        }
+
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/xml/Processor.java b/asmx/src/org/objectweb/asm/xml/Processor.java
new file mode 100644
index 0000000..2baeb0f
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/xml/Processor.java
@@ -0,0 +1,1048 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Templates;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamSource;
+
+import org.objectweb.asm.ClassReader;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.XMLReaderFactory;
+
+/**
+ * Processor is a command line tool that can be used for bytecode waving
+ * directed by XSL transformation. <p> In order to use a concrete XSLT engine,
+ * system property <tt>javax.xml.transform.TransformerFactory</tt> must be set
+ * to one of the following values.
+ * 
+ * <blockquote> <table border="1" cellspacing="0" cellpadding="3"> <tr> <td>jd.xslt</td>
+ * <td>jd.xml.xslt.trax.TransformerFactoryImpl</td> </tr>
+ * 
+ * <tr> <td>Saxon</td> <td>net.sf.saxon.TransformerFactoryImpl</td> </tr>
+ * 
+ * <tr> <td>Caucho</td> <td>com.caucho.xsl.Xsl</td> </tr>
+ * 
+ * <tr> <td>Xalan interpeter</td> <td>org.apache.xalan.processor.TransformerFactory</td>
+ * </tr>
+ * 
+ * <tr> <td>Xalan xsltc</td> <td>org.apache.xalan.xsltc.trax.TransformerFactoryImpl</td>
+ * </tr> </table> </blockquote>
+ * 
+ * @author Eugene Kuleshov
+ */
+public class Processor {
+
+    public static final int BYTECODE = 1;
+
+    public static final int MULTI_XML = 2;
+
+    public static final int SINGLE_XML = 3;
+
+    private static final String SINGLE_XML_NAME = "classes.xml";
+
+    private int inRepresentation;
+
+    private int outRepresentation;
+
+    private InputStream input = null;
+
+    private OutputStream output = null;
+
+    private Source xslt = null;
+
+    private boolean computeMax;
+
+    private int n = 0;
+
+    public Processor(
+        int inRepresenation,
+        int outRepresentation,
+        InputStream input,
+        OutputStream output,
+        Source xslt)
+    {
+        this.inRepresentation = inRepresenation;
+        this.outRepresentation = outRepresentation;
+        this.input = input;
+        this.output = output;
+        this.xslt = xslt;
+        this.computeMax = true;
+    }
+
+    public int process() throws TransformerException, IOException, SAXException
+    {
+        ZipInputStream zis = new ZipInputStream(input);
+        final ZipOutputStream zos = new ZipOutputStream(output);
+        final OutputStreamWriter osw = new OutputStreamWriter(zos);
+
+        Thread.currentThread()
+                .setContextClassLoader(getClass().getClassLoader());
+
+        TransformerFactory tf = TransformerFactory.newInstance();
+        if (!tf.getFeature(SAXSource.FEATURE)
+                || !tf.getFeature(SAXResult.FEATURE))
+            return 0;
+
+        SAXTransformerFactory saxtf = (SAXTransformerFactory) tf;
+        Templates templates = null;
+        if (xslt != null) {
+            templates = saxtf.newTemplates(xslt);
+        }
+
+        // configuring outHandlerFactory
+        // ///////////////////////////////////////////////////////
+
+        EntryElement entryElement = getEntryElement(zos);
+
+        ContentHandler outDocHandler = null;
+        switch (outRepresentation) {
+            case BYTECODE:
+                outDocHandler = new OutputSlicingHandler(new ASMContentHandlerFactory(zos,
+                        computeMax),
+                        entryElement,
+                        false);
+                break;
+
+            case MULTI_XML:
+                outDocHandler = new OutputSlicingHandler(new SAXWriterFactory(osw,
+                        true),
+                        entryElement,
+                        true);
+                break;
+
+            case SINGLE_XML:
+                ZipEntry outputEntry = new ZipEntry(SINGLE_XML_NAME);
+                zos.putNextEntry(outputEntry);
+                outDocHandler = new SAXWriter(osw, false);
+                break;
+
+        }
+
+        // configuring inputDocHandlerFactory
+        // /////////////////////////////////////////////////
+        ContentHandler inDocHandler = null;
+        if (templates == null) {
+            inDocHandler = outDocHandler;
+        } else {
+            inDocHandler = new InputSlicingHandler("class",
+                    outDocHandler,
+                    new TransformerHandlerFactory(saxtf,
+                            templates,
+                            outDocHandler));
+        }
+        ContentHandlerFactory inDocHandlerFactory = new SubdocumentHandlerFactory(inDocHandler);
+
+        if (inDocHandler != null && inRepresentation != SINGLE_XML) {
+            inDocHandler.startDocument();
+            inDocHandler.startElement("",
+                    "classes",
+                    "classes",
+                    new AttributesImpl());
+        }
+
+        int i = 0;
+        ZipEntry ze = null;
+        while ((ze = zis.getNextEntry()) != null) {
+            update(ze.getName(), n++);
+            if (isClassEntry(ze)) {
+                processEntry(zis, ze, inDocHandlerFactory);
+            } else {
+                OutputStream os = entryElement.openEntry(getName(ze));
+                copyEntry(zis, os);
+                entryElement.closeEntry();
+            }
+
+            i++;
+        }
+
+        if (inDocHandler != null && inRepresentation != SINGLE_XML) {
+            inDocHandler.endElement("", "classes", "classes");
+            inDocHandler.endDocument();
+        }
+
+        if (outRepresentation == SINGLE_XML) {
+            zos.closeEntry();
+        }
+        zos.flush();
+        zos.close();
+
+        return i;
+    }
+
+    private void copyEntry(InputStream is, OutputStream os) throws IOException {
+        if (outRepresentation == SINGLE_XML)
+            return;
+
+        byte[] buff = new byte[2048];
+        int i;
+        while ((i = is.read(buff)) != -1) {
+            os.write(buff, 0, i);
+        }
+    }
+
+    private boolean isClassEntry(ZipEntry ze) {
+        String name = ze.getName();
+        return inRepresentation == SINGLE_XML && name.equals(SINGLE_XML_NAME)
+                || name.endsWith(".class") || name.endsWith(".class.xml");
+    }
+
+    private void processEntry(
+        final ZipInputStream zis,
+        ZipEntry ze,
+        ContentHandlerFactory handlerFactory)
+    {
+        ContentHandler handler = handlerFactory.createContentHandler();
+        try {
+
+            // if (CODE2ASM.equals(command)) { // read bytecode and process it
+            // // with TraceClassVisitor
+            // ClassReader cr = new ClassReader(readEntry(zis, ze));
+            // cr.accept(new TraceClassVisitor(null, new PrintWriter(os)),
+            // false);
+            // }
+
+            boolean singleInputDocument = inRepresentation == SINGLE_XML;
+            if (inRepresentation == BYTECODE) { // read bytecode and process it
+                // with handler
+                ClassReader cr = new ClassReader(readEntry(zis, ze));
+                cr.accept(new SAXClassAdapter(handler, singleInputDocument),
+                        false);
+
+            } else { // read XML and process it with handler
+                XMLReader reader = XMLReaderFactory.createXMLReader();
+                reader.setContentHandler(handler);
+                reader.parse(new InputSource(singleInputDocument
+                        ? (InputStream) new ProtectedInputStream(zis)
+                        : new ByteArrayInputStream(readEntry(zis, ze))));
+
+            }
+        } catch (Exception ex) {
+            update(ze.getName(), 0);
+            update(ex, 0);
+        }
+    }
+
+    private EntryElement getEntryElement(ZipOutputStream zos) {
+        if (outRepresentation == SINGLE_XML) {
+            return new SingleDocElement(zos);
+        }
+        return new ZipEntryElement(zos);
+    }
+
+    // private ContentHandlerFactory getHandlerFactory(
+    // OutputStream os,
+    // SAXTransformerFactory saxtf,
+    // Templates templates)
+    // {
+    // ContentHandlerFactory factory = null;
+    // if (templates == null) {
+    // if (outputRepresentation == BYTECODE) { // factory used to write
+    // // bytecode
+    // factory = new ASMContentHandlerFactory(os, computeMax);
+    // } else { // factory used to write XML
+    // factory = new SAXWriterFactory(os, true);
+    // }
+    // } else {
+    // if (outputRepresentation == BYTECODE) { // factory used to transform
+    // // and then write bytecode
+    // factory = new ASMTransformerHandlerFactory(saxtf,
+    // templates,
+    // os,
+    // computeMax);
+    // } else { // factory used to transformand then write XML
+    // factory = new TransformerHandlerFactory(saxtf,
+    // templates,
+    // os,
+    // outputRepresentation == SINGLE_XML);
+    // }
+    // }
+    // return factory;
+    // }
+
+    private String getName(ZipEntry ze) {
+        String name = ze.getName();
+        if (isClassEntry(ze)) {
+            if (inRepresentation != BYTECODE && outRepresentation == BYTECODE) {
+                name = name.substring(0, name.length() - 4); // .class.xml to
+                // .class
+            } else if (inRepresentation == BYTECODE
+                    && outRepresentation != BYTECODE)
+            {
+                name = name.concat(".xml"); // .class to .class.xml
+            }
+            // } else if( CODE2ASM.equals( command)) {
+            // name = name.substring( 0, name.length()-6).concat( ".asm");
+        }
+        return name;
+    }
+
+    private byte[] readEntry(ZipInputStream zis, ZipEntry ze)
+            throws IOException
+    {
+        long size = ze.getSize();
+        if (size > -1) {
+            byte[] buff = new byte[(int) size];
+            int k = 0;
+            int n;
+            while(( n = zis.read(buff, k, buff.length-k)) > 0) {
+              k += n;
+            }
+            return buff;
+        }
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        byte[] buff = new byte[4096];
+        int i;
+        while ((i = zis.read(buff)) != -1) {
+            bos.write(buff, 0, i);
+        }
+        return bos.toByteArray();
+    }
+
+    /*
+     * (non-Javadoc)
+     * 
+     * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
+     */
+    protected void update(Object arg, int n) {
+        if (arg instanceof Throwable) {
+            ((Throwable) arg).printStackTrace();
+        } else {
+            if ((n % 100) == 0) {
+                System.err.println(n + " " + arg);
+            }
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        if (args.length < 2) {
+            showUsage();
+            return;
+        }
+
+        int inRepresentation = getRepresentation(args[0]);
+        int outRepresentation = getRepresentation(args[1]);
+
+        InputStream is = System.in;
+        OutputStream os = new BufferedOutputStream(System.out);
+
+        Source xslt = null;
+        // boolean computeMax = true;
+
+        for (int i = 2; i < args.length; i++) {
+            if ("-in".equals(args[i])) {
+                is = new FileInputStream(args[++i]);
+
+            } else if ("-out".equals(args[i])) {
+                os = new BufferedOutputStream(new FileOutputStream(args[++i]));
+
+            } else if ("-xslt".equals(args[i])) {
+                xslt = new StreamSource(new FileInputStream(args[++i]));
+
+                // } else if( "-computemax".equals( args[ i].toLowerCase())) {
+                // computeMax = true;
+
+            } else {
+                showUsage();
+                return;
+
+            }
+        }
+
+        if (inRepresentation == 0 || outRepresentation == 0) {
+            showUsage();
+            return;
+        }
+
+        Processor m = new Processor(inRepresentation,
+                outRepresentation,
+                is,
+                os,
+                xslt);
+
+        long l1 = System.currentTimeMillis();
+        int n = m.process();
+        long l2 = System.currentTimeMillis();
+        System.err.println(n);
+        System.err.println("" + (l2 - l1) + "ms  " + (1000f * n / (l2 - l1))
+                + " resources/sec");
+    }
+
+    private static int getRepresentation(String s) {
+        if ("code".equals(s)) {
+            return BYTECODE;
+        } else if ("xml".equals(s)) {
+            return MULTI_XML;
+        } else if ("singlexml".equals(s)) {
+            return SINGLE_XML;
+        }
+        return 0;
+    }
+
+    private static void showUsage() {
+        System.err.println("Usage: Main <in format> <out format> [-in <input jar>] [-out <output jar>] [-xslt <xslt file>]");
+        System.err.println("  when -in or -out is omitted sysin and sysout would be used");
+        System.err.println("  <in format> and <out format> - code | xml | singlexml");
+    }
+
+    /**
+     * IputStream wrapper class used to protect input streams from being closed
+     * by some stupid XML parsers.
+     */
+    private static final class ProtectedInputStream extends InputStream {
+        private final InputStream is;
+
+        private ProtectedInputStream(InputStream is) {
+            super();
+            this.is = is;
+        }
+
+        public final void close() throws IOException {
+        }
+
+        public final int read() throws IOException {
+            return is.read();
+        }
+
+        public final int read(byte[] b, int off, int len) throws IOException {
+            return is.read(b, off, len);
+        }
+
+        public final int available() throws IOException {
+            return is.available();
+        }
+    }
+
+    /**
+     * A {@link ContentHandlerFactory ContentHandlerFactory} is used to create
+     * {@link org.xml.sax.ContentHandler ContentHandler} instances for concrete
+     * context.
+     */
+    private static interface ContentHandlerFactory {
+
+        /**
+         * Creates an instance of the content handler.
+         * 
+         * @return content handler
+         */
+        ContentHandler createContentHandler();
+
+    }
+
+    /**
+     * SAXWriterFactory
+     */
+    private static final class SAXWriterFactory implements
+            ContentHandlerFactory
+    {
+        private Writer w;
+
+        private boolean optimizeEmptyElements;
+
+        public SAXWriterFactory(Writer w, boolean optimizeEmptyElements) {
+            this.w = w;
+            this.optimizeEmptyElements = optimizeEmptyElements;
+        }
+
+        public final ContentHandler createContentHandler() {
+            return new SAXWriter(w, optimizeEmptyElements);
+        }
+
+    }
+
+    /**
+     * ASMContentHandlerFactory
+     */
+    private static final class ASMContentHandlerFactory implements
+            ContentHandlerFactory
+    {
+        private OutputStream os;
+
+        private boolean computeMax;
+
+        public ASMContentHandlerFactory(OutputStream os, boolean computeMax) {
+            this.os = os;
+            this.computeMax = computeMax;
+        }
+
+        public final ContentHandler createContentHandler() {
+            return new ASMContentHandler(os, computeMax);
+        }
+
+    }
+
+    /**
+     * TransformerHandlerFactory
+     */
+    private static final class TransformerHandlerFactory implements
+            ContentHandlerFactory
+    {
+        private SAXTransformerFactory saxtf;
+
+        private Templates templates;
+
+        private ContentHandler outputHandler;
+
+        public TransformerHandlerFactory(
+            SAXTransformerFactory saxtf,
+            Templates templates,
+            ContentHandler outputHandler)
+        {
+            this.saxtf = saxtf;
+            this.templates = templates;
+            this.outputHandler = outputHandler;
+        }
+
+        public final ContentHandler createContentHandler() {
+            try {
+                TransformerHandler handler = saxtf.newTransformerHandler(templates);
+                handler.setResult(new SAXResult(outputHandler));
+                return handler;
+            } catch (TransformerConfigurationException ex) {
+                throw new RuntimeException(ex.toString());
+            }
+        }
+    }
+
+    /**
+     * SubdocumentHandlerFactory
+     */
+    private final static class SubdocumentHandlerFactory implements
+            ContentHandlerFactory
+    {
+        private ContentHandler subdocumentHandler;
+
+        public SubdocumentHandlerFactory(ContentHandler subdocumentHandler) {
+            this.subdocumentHandler = subdocumentHandler;
+        }
+
+        public final ContentHandler createContentHandler() {
+            return subdocumentHandler;
+        }
+
+    }
+
+    /**
+     * A {@link org.xml.sax.ContentHandler ContentHandler} and
+     * {@link org.xml.sax.ext.LexicalHandler LexicalHandler} that serializes XML
+     * from SAX 2.0 events into {@link java.io.Writer Writer}.
+     * 
+     * <i><blockquote> This implementation does not support namespaces, entity
+     * definitions (uncluding DTD), CDATA and text elements. </blockquote></i>
+     */
+    private final static class SAXWriter extends DefaultHandler implements
+            LexicalHandler
+    {
+        private static final char[] OFF = "                                                                                                        ".toCharArray();
+
+        private Writer w;
+
+        private boolean optimizeEmptyElements;
+
+        private boolean openElement = false;
+
+        private int ident = 0;
+
+        /**
+         * Creates <code>SAXWriter</code>.
+         * 
+         * @param w writer
+         * @param optimizeEmptyElements if set to <code>true</code>, short
+         *        XML syntax will be used for empty elements
+         */
+        public SAXWriter(Writer w, boolean optimizeEmptyElements) {
+            this.w = w;
+            this.optimizeEmptyElements = optimizeEmptyElements;
+        }
+
+        public final void startElement(
+            String ns,
+            String localName,
+            String qName,
+            Attributes atts) throws SAXException
+        {
+            try {
+                closeElement();
+
+                writeIdent();
+                w.write("<".concat(qName));
+                if (atts != null && atts.getLength() > 0)
+                    writeAttributes(atts);
+
+                if (!optimizeEmptyElements) {
+                    w.write(">\n");
+                } else {
+                    openElement = true;
+                }
+                ident += 2;
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        public final void endElement(String ns, String localName, String qName)
+                throws SAXException
+        {
+            ident -= 2;
+            try {
+                if (openElement) {
+                    w.write("/>\n");
+                    openElement = false;
+                } else {
+                    writeIdent();
+                    w.write("</" + qName + ">\n");
+                }
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        public final void endDocument() throws SAXException {
+            try {
+                w.flush();
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        public final void comment(char[] ch, int off, int len)
+                throws SAXException
+        {
+            try {
+                closeElement();
+
+                writeIdent();
+                w.write("<!-- ");
+                w.write(ch, off, len);
+                w.write(" -->\n");
+
+            } catch (IOException ex) {
+                throw new SAXException(ex);
+
+            }
+        }
+
+        public final void startDTD(String arg0, String arg1, String arg2)
+                throws SAXException
+        {
+        }
+
+        public final void endDTD() throws SAXException {
+        }
+
+        public final void startEntity(String arg0) throws SAXException {
+        }
+
+        public final void endEntity(String arg0) throws SAXException {
+        }
+
+        public final void startCDATA() throws SAXException {
+        }
+
+        public final void endCDATA() throws SAXException {
+        }
+
+        private final void writeAttributes(Attributes atts) throws IOException {
+            StringBuffer sb = new StringBuffer();
+            int len = atts.getLength();
+            for (int i = 0; i < len; i++) {
+                sb.append(" ")
+                        .append(atts.getLocalName(i))
+                        .append("=\"")
+                        .append(esc(atts.getValue(i)))
+                        .append("\"");
+            }
+            w.write(sb.toString());
+        }
+
+        /**
+         * Encode string with escaping.
+         * 
+         * @param str string to encode.
+         * @return encoded string
+         */
+        private final String esc(String str) {
+            StringBuffer sb = new StringBuffer(str.length());
+            for (int i = 0; i < str.length(); i++) {
+                char ch = str.charAt(i);
+                switch (ch) {
+                    case '&':
+                        sb.append("&amp;");
+                        break;
+
+                    case '<':
+                        sb.append("&lt;");
+                        break;
+
+                    case '>':
+                        sb.append("&gt;");
+                        break;
+
+                    case '\"':
+                        sb.append("&quot;");
+                        break;
+
+                    default:
+                        if (ch > 0x7f) {
+                            sb.append("&#")
+                                    .append(Integer.toString(ch))
+                                    .append(';');
+                        } else {
+                            sb.append(ch);
+                        }
+
+                }
+            }
+            return sb.toString();
+        }
+
+        private final void writeIdent() throws IOException {
+            int n = ident;
+            while (n > 0) {
+                if (n > OFF.length) {
+                    w.write(OFF);
+                    n -= OFF.length;
+                } else {
+                    w.write(OFF, 0, n);
+                    n = 0;
+                }
+            }
+        }
+
+        private final void closeElement() throws IOException {
+            if (openElement) {
+                w.write(">\n");
+            }
+            openElement = false;
+        }
+
+    }
+
+    /**
+     * A {@link org.xml.sax.ContentHandler ContentHandler} that splits XML
+     * documents into smaller chunks. Each chunk is processed by the nested
+     * {@link org.xml.sax.ContentHandler ContentHandler} obtained from
+     * {@link java.net.ContentHandlerFactory ContentHandlerFactory}. This is
+     * useful for running XSLT engine against large XML document that will
+     * hardly fit into the memory all together. <p> TODO use complete path for
+     * subdocumentRoot
+     */
+    private final static class InputSlicingHandler extends DefaultHandler {
+        private String subdocumentRoot;
+
+        private ContentHandler rootHandler;
+
+        private ContentHandlerFactory subdocumentHandlerFactory;
+
+        private boolean subdocument = false;
+
+        private ContentHandler subdocumentHandler;
+
+        /**
+         * Constructs a new {@link InputSlicingHandler SubdocumentHandler}
+         * object.
+         * 
+         * @param subdocumentRoot name/path to the root element of the
+         *        subdocument
+         * @param rootHandler content handler for the entire document
+         *        (subdocument envelope).
+         * @param subdocumentHandlerFactory a
+         *        {@link ContentHandlerFactory ContentHandlerFactory} used to
+         *        create {@link ContentHandler ContentHandler} instances for
+         *        subdocuments.
+         */
+        public InputSlicingHandler(
+            String subdocumentRoot,
+            ContentHandler rootHandler,
+            ContentHandlerFactory subdocumentHandlerFactory)
+        {
+            this.subdocumentRoot = subdocumentRoot;
+            this.rootHandler = rootHandler;
+            this.subdocumentHandlerFactory = subdocumentHandlerFactory;
+        }
+
+        public final void startElement(
+            String namespaceURI,
+            String localName,
+            String qName,
+            Attributes list) throws SAXException
+        {
+            if (subdocument) {
+                subdocumentHandler.startElement(namespaceURI,
+                        localName,
+                        qName,
+                        list);
+            } else if (localName.equals(subdocumentRoot)) {
+                subdocumentHandler = subdocumentHandlerFactory.createContentHandler();
+                subdocumentHandler.startDocument();
+                subdocumentHandler.startElement(namespaceURI,
+                        localName,
+                        qName,
+                        list);
+                subdocument = true;
+            } else if (rootHandler != null) {
+                rootHandler.startElement(namespaceURI, localName, qName, list);
+            }
+        }
+
+        public final void endElement(
+            String namespaceURI,
+            String localName,
+            String qName) throws SAXException
+        {
+            if (subdocument) {
+                subdocumentHandler.endElement(namespaceURI, localName, qName);
+                if (localName.equals(subdocumentRoot)) {
+                    subdocumentHandler.endDocument();
+                    subdocument = false;
+                }
+            } else if (rootHandler != null) {
+                rootHandler.endElement(namespaceURI, localName, qName);
+            }
+        }
+
+        public final void startDocument() throws SAXException {
+            if (rootHandler != null) {
+                rootHandler.startDocument();
+            }
+        }
+
+        public final void endDocument() throws SAXException {
+            if (rootHandler != null) {
+                rootHandler.endDocument();
+
+            }
+        }
+
+        public final void characters(char[] buff, int offset, int size)
+                throws SAXException
+        {
+            if (subdocument) {
+                subdocumentHandler.characters(buff, offset, size);
+            } else if (rootHandler != null) {
+                rootHandler.characters(buff, offset, size);
+            }
+        }
+
+    }
+
+    /**
+     * A {@link org.xml.sax.ContentHandler ContentHandler} that splits XML
+     * documents into smaller chunks. Each chunk is processed by the nested
+     * {@link org.xml.sax.ContentHandler ContentHandler} obtained from
+     * {@link java.net.ContentHandlerFactory ContentHandlerFactory}. This is
+     * useful for running XSLT engine against large XML document that will
+     * hardly fit into the memory all together. <p> TODO use complete path for
+     * subdocumentRoot
+     */
+    private static final class OutputSlicingHandler extends DefaultHandler {
+        private String subdocumentRoot;
+
+        private ContentHandlerFactory subdocumentHandlerFactory;
+
+        private EntryElement entryElement;
+
+        private boolean isXml;
+
+        private boolean subdocument = false;
+
+        private ContentHandler subdocumentHandler;
+
+        /**
+         * Constructs a new {@link OutputSlicingHandler SubdocumentHandler}
+         * object.
+         * 
+         * @param subdocumentHandlerFactory a
+         *        {@link ContentHandlerFactory ContentHandlerFactory} used to
+         *        create {@link ContentHandler ContentHandler} instances for
+         *        subdocuments.
+         * @param entryElement TODO.
+         * @param isXml TODO.
+         */
+        public OutputSlicingHandler(
+            ContentHandlerFactory subdocumentHandlerFactory,
+            EntryElement entryElement,
+            boolean isXml)
+        {
+            this.subdocumentRoot = "class";
+            this.subdocumentHandlerFactory = subdocumentHandlerFactory;
+            this.entryElement = entryElement;
+            this.isXml = isXml;
+        }
+
+        public final void startElement(
+            String namespaceURI,
+            String localName,
+            String qName,
+            Attributes list) throws SAXException
+        {
+            if (subdocument) {
+                subdocumentHandler.startElement(namespaceURI,
+                        localName,
+                        qName,
+                        list);
+            } else if (localName.equals(subdocumentRoot)) {
+                String name = list.getValue("name");
+                if (name == null || name.length() == 0)
+                    throw new SAXException("Class element without name attribute.");
+                try {
+                    entryElement.openEntry(isXml
+                            ? name.concat(".class.xml")
+                            : name.concat(".class"));
+                } catch (IOException ex) {
+                    throw new SAXException(ex.toString(), ex);
+                }
+                subdocumentHandler = subdocumentHandlerFactory.createContentHandler();
+                subdocumentHandler.startDocument();
+                subdocumentHandler.startElement(namespaceURI,
+                        localName,
+                        qName,
+                        list);
+                subdocument = true;
+            }
+        }
+
+        public final void endElement(
+            String namespaceURI,
+            String localName,
+            String qName) throws SAXException
+        {
+            if (subdocument) {
+                subdocumentHandler.endElement(namespaceURI, localName, qName);
+                if (localName.equals(subdocumentRoot)) {
+                    subdocumentHandler.endDocument();
+                    subdocument = false;
+                    try {
+                        entryElement.closeEntry();
+                    } catch (IOException ex) {
+                        throw new SAXException(ex.toString(), ex);
+                    }
+                }
+            }
+        }
+
+        public final void startDocument() throws SAXException {
+        }
+
+        public final void endDocument() throws SAXException {
+        }
+
+        public final void characters(char[] buff, int offset, int size)
+                throws SAXException
+        {
+            if (subdocument) {
+                subdocumentHandler.characters(buff, offset, size);
+            }
+        }
+
+    }
+
+    private static interface EntryElement {
+
+        OutputStream openEntry(String name) throws IOException;
+
+        void closeEntry() throws IOException;
+
+    }
+
+    private static final class SingleDocElement implements EntryElement {
+        private OutputStream os;
+
+        public SingleDocElement(OutputStream os) {
+            this.os = os;
+        }
+
+        public OutputStream openEntry(String name) throws IOException {
+            return os;
+        }
+
+        public void closeEntry() throws IOException {
+            os.flush();
+        }
+
+    }
+
+    private static final class ZipEntryElement implements EntryElement {
+        private ZipOutputStream zos;
+
+        public ZipEntryElement(ZipOutputStream zos) {
+            this.zos = zos;
+        }
+
+        public OutputStream openEntry(String name) throws IOException {
+            ZipEntry entry = new ZipEntry(name);
+            zos.putNextEntry(entry);
+            return zos;
+        }
+
+        public void closeEntry() throws IOException {
+            zos.flush();
+            zos.closeEntry();
+        }
+
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXAdapter.java
new file mode 100644
index 0000000..c8f6ecb
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/xml/SAXAdapter.java
@@ -0,0 +1,91 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * SAXAdapter
+ * 
+ * @author Eugene Kuleshov
+ */
+public abstract class SAXAdapter {
+    private final ContentHandler h;
+
+    public SAXAdapter(ContentHandler h) {
+        this.h = h;
+    }
+
+    protected ContentHandler getContentHandler() {
+        return h;
+    }
+
+    protected final void addEnd(String name) {
+        try {
+            h.endElement("", name, name);
+        } catch (SAXException ex) {
+            throw new RuntimeException(ex.toString());
+        }
+    }
+
+    protected final void addStart(String name, Attributes attrs) {
+        try {
+            h.startElement("", name, name, attrs);
+        } catch (SAXException ex) {
+            throw new RuntimeException(ex.toString());
+        }
+    }
+
+    protected final void addElement(String name, Attributes attrs) {
+        addStart(name, attrs);
+        addEnd(name);
+    }
+
+    protected void addDocumentStart() {
+        try {
+            h.startDocument();
+        } catch (SAXException ex) {
+            throw new RuntimeException(ex.getException());
+        }
+    }
+
+    protected void addDocumentEnd() {
+        try {
+            h.endDocument();
+        } catch (SAXException ex) {
+            // ex.getException().printStackTrace();
+            // ex.printStackTrace();
+            throw new RuntimeException(ex.toString());
+        }
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXAnnotationAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXAnnotationAdapter.java
new file mode 100644
index 0000000..e738241
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/xml/SAXAnnotationAdapter.java
@@ -0,0 +1,191 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Type;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * SAXAnnotationAdapter
+ * 
+ * @author Eugene Kuleshov
+ */
+public class SAXAnnotationAdapter extends SAXAdapter implements
+        AnnotationVisitor
+{
+    private final String elementName;
+
+    public SAXAnnotationAdapter(
+        ContentHandler h,
+        String elementName,
+        int visible,
+        String name,
+        String desc)
+    {
+        this(h, elementName, visible, desc, name, -1);
+    }
+
+    public SAXAnnotationAdapter(
+        ContentHandler h,
+        String elementName,
+        int visible,
+        int parameter,
+        String desc)
+    {
+        this(h, elementName, visible, desc, null, parameter);
+    }
+
+    private SAXAnnotationAdapter(
+        ContentHandler h,
+        String elementName,
+        int visible,
+        String desc,
+        String name,
+        int parameter)
+    {
+        super(h);
+        this.elementName = elementName;
+
+        AttributesImpl att = new AttributesImpl();
+        if (name != null)
+            att.addAttribute("", "name", "name", "", name);
+        if (visible != 0)
+            att.addAttribute("", "visible", "visible", "", visible > 0
+                    ? "true"
+                    : "false");
+        if (parameter != -1)
+            att.addAttribute("",
+                    "parameter",
+                    "parameter",
+                    "",
+                    Integer.toString(parameter));
+        if (desc != null)
+            att.addAttribute("", "desc", "desc", "", desc);
+
+        addStart(elementName, att);
+    }
+
+    public void visit(String name, Object value) {
+        Class c = value.getClass();
+        if (c.isArray()) {
+            AnnotationVisitor av = visitArray(name);
+            if (value instanceof byte[]) {
+                byte[] b = (byte[]) value;
+                for (int i = 0; i < b.length; i++)
+                    av.visit(null, new Byte(b[i]));
+
+            } else if (value instanceof char[]) {
+                char[] b = (char[]) value;
+                for (int i = 0; i < b.length; i++)
+                    av.visit(null, new Character(b[i]));
+
+            } else if (value instanceof boolean[]) {
+                boolean[] b = (boolean[]) value;
+                for (int i = 0; i < b.length; i++)
+                    av.visit(null, Boolean.valueOf(b[i]));
+
+            } else if (value instanceof int[]) {
+                int[] b = (int[]) value;
+                for (int i = 0; i < b.length; i++)
+                    av.visit(null, new Integer(b[i]));
+
+            } else if (value instanceof long[]) {
+                long[] b = (long[]) value;
+                for (int i = 0; i < b.length; i++)
+                    av.visit(null, new Long(b[i]));
+
+            } else if (value instanceof float[]) {
+                float[] b = (float[]) value;
+                for (int i = 0; i < b.length; i++)
+                    av.visit(null, new Float(b[i]));
+
+            } else if (value instanceof double[]) {
+                double[] b = (double[]) value;
+                for (int i = 0; i < b.length; i++)
+                    av.visit(null, new Double(b[i]));
+
+            }
+            av.visitEnd();
+        } else {
+            addValueElement("annotationValue",
+                    name,
+                    Type.getDescriptor(c),
+                    value.toString());
+        }
+    }
+
+    public void visitEnum(String name, String desc, String value) {
+        addValueElement("annotationValueEnum", name, desc, value);
+    }
+
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+        return new SAXAnnotationAdapter(getContentHandler(),
+                "annotationValueAnnotation",
+                0,
+                name,
+                desc);
+    }
+
+    public AnnotationVisitor visitArray(String name) {
+        return new SAXAnnotationAdapter(getContentHandler(),
+                "annotationValueArray",
+                0,
+                name,
+                null);
+    }
+
+    public void visitEnd() {
+        addEnd(elementName);
+    }
+
+    private void addValueElement(
+        String element,
+        String name,
+        String desc,
+        String value)
+    {
+        AttributesImpl att = new AttributesImpl();
+        if (name != null)
+            att.addAttribute("", "name", "name", "", name);
+        if (desc != null)
+            att.addAttribute("", "desc", "desc", "", desc);
+        if (value != null)
+            att.addAttribute("",
+                    "value",
+                    "value",
+                    "",
+                    SAXClassAdapter.encode(value));
+
+        addElement(element, att);
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXClassAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXClassAdapter.java
new file mode 100644
index 0000000..ed7fd6a
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/xml/SAXClassAdapter.java
@@ -0,0 +1,357 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * A {@link org.objectweb.asm.ClassVisitor ClassVisitor} that generates SAX 2.0
+ * events from the visited class. It can feed any kind of
+ * {@link org.xml.sax.ContentHandler ContentHandler}, e.g. XML serializer, XSLT
+ * or XQuery engines.
+ * 
+ * @see org.objectweb.asm.xml.Processor
+ * @see org.objectweb.asm.xml.ASMContentHandler
+ * 
+ * @author Eugene Kuleshov
+ */
+public final class SAXClassAdapter extends SAXAdapter implements ClassVisitor {
+    private boolean singleDocument;
+
+    /**
+     * Constructs a new {@link SAXClassAdapter SAXClassAdapter} object.
+     * 
+     * @param h content handler that will be used to send SAX 2.0 events.
+     * @param singleDocument if <tt>true</tt> adapter will not produce
+     *        {@link ContentHandler#startDocument() startDocument()} and
+     *        {@link ContentHandler#endDocument() endDocument()} events.
+     */
+    public SAXClassAdapter(ContentHandler h, boolean singleDocument) {
+        super(h);
+        this.singleDocument = singleDocument;
+        if (!singleDocument) {
+            addDocumentStart();
+        }
+    }
+
+    public void visitSource(String source, String debug) {
+        if (source == null && debug == null) {
+            return;
+        }
+
+        AttributesImpl att = new AttributesImpl();
+        if (source != null)
+            att.addAttribute("", "file", "file", "", encode(source));
+        if (debug != null)
+            att.addAttribute("", "debug", "debug", "", encode(debug));
+
+        addElement("source", att);
+    }
+
+    public void visitOuterClass(String owner, String name, String desc) {
+        AttributesImpl att = new AttributesImpl();
+        att.addAttribute("", "owner", "owner", "", owner);
+        if (name != null)
+            att.addAttribute("", "name", "name", "", name);
+        if (desc != null)
+            att.addAttribute("", "desc", "desc", "", desc);
+
+        addElement("outerclass", att);
+    }
+
+    public final void visitAttribute(Attribute attr) {
+        // TODO Auto-generated SAXClassAdapter.visitAttribute
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return new SAXAnnotationAdapter(getContentHandler(),
+                "annotation",
+                visible ? 1 : -1,
+                null,
+                desc);
+    }
+
+    public TypeAnnotationVisitor visitTypeAnnotation(
+        String desc, boolean visible, boolean inCode) {
+        throw new RuntimeException("Jaime did not implement yet");
+    }
+
+    public void visit(
+        int version,
+        int access,
+        String name,
+        String signature,
+        String superName,
+        String[] interfaces)
+    {
+        StringBuffer sb = new StringBuffer();
+        if ((access & Opcodes.ACC_PUBLIC) != 0)
+            sb.append("public ");
+        if ((access & Opcodes.ACC_PRIVATE) != 0)
+            sb.append("private ");
+        if ((access & Opcodes.ACC_PROTECTED) != 0)
+            sb.append("protected ");
+        if ((access & Opcodes.ACC_FINAL) != 0)
+            sb.append("final ");
+        if ((access & Opcodes.ACC_SUPER) != 0)
+            sb.append("super ");
+        if ((access & Opcodes.ACC_INTERFACE) != 0)
+            sb.append("interface ");
+        if ((access & Opcodes.ACC_ABSTRACT) != 0)
+            sb.append("abstract ");
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0)
+            sb.append("synthetic ");
+        if ((access & Opcodes.ACC_ANNOTATION) != 0)
+            sb.append("annotation ");
+        if ((access & Opcodes.ACC_ENUM) != 0)
+            sb.append("enum ");
+        if ((access & Opcodes.ACC_DEPRECATED) != 0)
+            sb.append("deprecated ");
+
+        AttributesImpl att = new AttributesImpl();
+        att.addAttribute("", "access", "access", "", sb.toString());
+        if (name != null)
+            att.addAttribute("", "name", "name", "", name);
+        if (signature != null)
+            att.addAttribute("",
+                    "signature",
+                    "signature",
+                    "",
+                    encode(signature));
+        if (superName != null)
+            att.addAttribute("", "parent", "parent", "", superName);
+        att.addAttribute("",
+                "major",
+                "major",
+                "",
+                Integer.toString(version & 0xFFFF));
+        att.addAttribute("",
+                "minor",
+                "minor",
+                "",
+                Integer.toString(version >>> 16));
+        addStart("class", att);
+
+        addStart("interfaces", new AttributesImpl());
+        if (interfaces != null && interfaces.length > 0) {
+            for (int i = 0; i < interfaces.length; i++) {
+                AttributesImpl att2 = new AttributesImpl();
+                att2.addAttribute("", "name", "name", "", interfaces[i]);
+                addElement("interface", att2);
+            }
+        }
+        addEnd("interfaces");
+    }
+
+    public FieldVisitor visitField(
+        int access,
+        String name,
+        String desc,
+        String signature,
+        Object value)
+    {
+        StringBuffer sb = new StringBuffer();
+        if ((access & Opcodes.ACC_PUBLIC) != 0)
+            sb.append("public ");
+        if ((access & Opcodes.ACC_PRIVATE) != 0)
+            sb.append("private ");
+        if ((access & Opcodes.ACC_PROTECTED) != 0)
+            sb.append("protected ");
+        if ((access & Opcodes.ACC_STATIC) != 0)
+            sb.append("static ");
+        if ((access & Opcodes.ACC_FINAL) != 0)
+            sb.append("final ");
+        if ((access & Opcodes.ACC_VOLATILE) != 0)
+            sb.append("volatile ");
+        if ((access & Opcodes.ACC_TRANSIENT) != 0)
+            sb.append("transient ");
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0)
+            sb.append("synthetic ");
+        if ((access & Opcodes.ACC_ENUM) != 0)
+            sb.append("enum ");
+        if ((access & Opcodes.ACC_DEPRECATED) != 0)
+            sb.append("deprecated ");
+
+        AttributesImpl att = new AttributesImpl();
+        att.addAttribute("", "access", "access", "", sb.toString());
+        att.addAttribute("", "name", "name", "", name);
+        att.addAttribute("", "desc", "desc", "", desc);
+        if (signature != null)
+            att.addAttribute("",
+                    "signature",
+                    "signature",
+                    "",
+                    encode(signature));
+        if (value != null) {
+            att.addAttribute("", "value", "value", "", encode(value.toString()));
+        }
+
+        return new SAXFieldAdapter(getContentHandler(), att);
+    }
+
+    public MethodVisitor visitMethod(
+        int access,
+        String name,
+        String desc,
+        String signature,
+        String[] exceptions)
+    {
+        StringBuffer sb = new StringBuffer();
+        if ((access & Opcodes.ACC_PUBLIC) != 0)
+            sb.append("public ");
+        if ((access & Opcodes.ACC_PRIVATE) != 0)
+            sb.append("private ");
+        if ((access & Opcodes.ACC_PROTECTED) != 0)
+            sb.append("protected ");
+        if ((access & Opcodes.ACC_STATIC) != 0)
+            sb.append("static ");
+        if ((access & Opcodes.ACC_FINAL) != 0)
+            sb.append("final ");
+        if ((access & Opcodes.ACC_SYNCHRONIZED) != 0)
+            sb.append("synchronized ");
+        if ((access & Opcodes.ACC_BRIDGE) != 0)
+            sb.append("bridge ");
+        if ((access & Opcodes.ACC_VARARGS) != 0)
+            sb.append("varargs ");
+        if ((access & Opcodes.ACC_NATIVE) != 0)
+            sb.append("native ");
+        if ((access & Opcodes.ACC_ABSTRACT) != 0)
+            sb.append("abstract ");
+        if ((access & Opcodes.ACC_STRICT) != 0)
+            sb.append("strict ");
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0)
+            sb.append("synthetic ");
+        if ((access & Opcodes.ACC_DEPRECATED) != 0)
+            sb.append("deprecated ");
+
+        AttributesImpl att = new AttributesImpl();
+        att.addAttribute("", "access", "access", "", sb.toString());
+        att.addAttribute("", "name", "name", "", name);
+        att.addAttribute("", "desc", "desc", "", desc);
+        if (signature != null) {
+            att.addAttribute("", "signature", "signature", "", signature);
+        }
+        addStart("method", att);
+
+        addStart("exceptions", new AttributesImpl());
+        if (exceptions != null && exceptions.length > 0) {
+            for (int i = 0; i < exceptions.length; i++) {
+                AttributesImpl att2 = new AttributesImpl();
+                att2.addAttribute("", "name", "name", "", exceptions[i]);
+                addElement("exception", att2);
+            }
+        }
+        addEnd("exceptions");
+
+        return new SAXCodeAdapter(getContentHandler(), access);
+    }
+
+    public final void visitInnerClass(
+        String name,
+        String outerName,
+        String innerName,
+        int access)
+    {
+        StringBuffer sb = new StringBuffer();
+        if ((access & Opcodes.ACC_PUBLIC) != 0)
+            sb.append("public ");
+        if ((access & Opcodes.ACC_PRIVATE) != 0)
+            sb.append("private ");
+        if ((access & Opcodes.ACC_PROTECTED) != 0)
+            sb.append("protected ");
+        if ((access & Opcodes.ACC_STATIC) != 0)
+            sb.append("static ");
+        if ((access & Opcodes.ACC_FINAL) != 0)
+            sb.append("final ");
+        if ((access & Opcodes.ACC_SUPER) != 0)
+            sb.append("super ");
+        if ((access & Opcodes.ACC_INTERFACE) != 0)
+            sb.append("interface ");
+        if ((access & Opcodes.ACC_ABSTRACT) != 0)
+            sb.append("abstract ");
+        if ((access & Opcodes.ACC_SYNTHETIC) != 0)
+            sb.append("synthetic ");
+        if ((access & Opcodes.ACC_ANNOTATION) != 0)
+            sb.append("annotation ");
+        if ((access & Opcodes.ACC_ENUM) != 0)
+            sb.append("enum ");
+        if ((access & Opcodes.ACC_DEPRECATED) != 0)
+            sb.append("deprecated ");
+
+        AttributesImpl att = new AttributesImpl();
+        att.addAttribute("", "access", "access", "", sb.toString());
+        if (name != null)
+            att.addAttribute("", "name", "name", "", name);
+        if (outerName != null)
+            att.addAttribute("", "outerName", "outerName", "", outerName);
+        if (innerName != null)
+            att.addAttribute("", "innerName", "innerName", "", innerName);
+        addElement("innerclass", att);
+    }
+
+    public final void visitEnd() {
+        addEnd("class");
+        if (!singleDocument) {
+            addDocumentEnd();
+        }
+    }
+
+    static final String encode(String s) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            if (c == '\\') {
+                sb.append("\\\\");
+            } else if (c < 0x20 || c > 0x7f) {
+                sb.append("\\u");
+                if (c < 0x10) {
+                    sb.append("000");
+                } else if (c < 0x100) {
+                    sb.append("00");
+                } else if (c < 0x1000) {
+                    sb.append("0");
+                }
+                sb.append(Integer.toString(c, 16));
+            } else {
+                sb.append(c);
+            }
+        }
+        return sb.toString();
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXCodeAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXCodeAdapter.java
new file mode 100644
index 0000000..ce7f83e
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/xml/SAXCodeAdapter.java
@@ -0,0 +1,353 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.TypePath;
+import org.objectweb.asm.util.AbstractVisitor;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * A {@link MethodVisitor} that generates SAX 2.0 events from the visited
+ * method.
+ * 
+ * @see org.objectweb.asm.xml.SAXClassAdapter
+ * @see org.objectweb.asm.xml.Processor
+ * 
+ * @author Eugene Kuleshov
+ */
+public final class SAXCodeAdapter extends SAXAdapter implements MethodVisitor {
+    private Map labelNames;
+
+    /**
+     * Constructs a new {@link SAXCodeAdapter SAXCodeAdapter} object.
+     * 
+     * @param h content handler that will be used to send SAX 2.0 events.
+     * @param access
+     */
+    public SAXCodeAdapter(ContentHandler h, int access) {
+        super(h);
+        labelNames = new HashMap();
+
+        if ((access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE | Opcodes.ACC_NATIVE)) == 0)
+        {
+            addStart("code", new AttributesImpl());
+        }
+    }
+
+    public final void visitCode() {
+    }
+
+    public final void visitInsn(int opcode) {
+        addElement(AbstractVisitor.OPCODES[opcode], new AttributesImpl());
+    }
+
+    public final void visitIntInsn(int opcode, int operand) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "value", "value", "", Integer.toString(operand));
+        addElement(AbstractVisitor.OPCODES[opcode], attrs);
+    }
+
+    public final void visitVarInsn(int opcode, int var) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "var", "var", "", Integer.toString(var));
+        addElement(AbstractVisitor.OPCODES[opcode], attrs);
+    }
+
+    public final void visitTypeInsn(int opcode, String desc) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "desc", "desc", "", desc);
+        addElement(AbstractVisitor.OPCODES[opcode], attrs);
+    }
+
+    public final void visitFieldInsn(
+        int opcode,
+        String owner,
+        String name,
+        String desc)
+    {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "owner", "owner", "", owner);
+        attrs.addAttribute("", "name", "name", "", name);
+        attrs.addAttribute("", "desc", "desc", "", desc);
+        addElement(AbstractVisitor.OPCODES[opcode], attrs);
+    }
+
+    public final void visitMethodInsn(
+        int opcode,
+        String owner,
+        String name,
+        String desc)
+    {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "owner", "owner", "", owner);
+        attrs.addAttribute("", "name", "name", "", name);
+        attrs.addAttribute("", "desc", "desc", "", desc);
+        addElement(AbstractVisitor.OPCODES[opcode], attrs);
+    }
+
+    @Override
+    public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+            Object... bsmArgs) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "name", "name", "", name);
+        attrs.addAttribute("", "desc", "desc", "", desc);
+        attrs.addAttribute("", "bsm", "bsm", "",
+                SAXClassAdapter.encode(bsm.toString()));
+        String o = AbstractVisitor.OPCODES[Opcodes.INVOKEDYNAMIC];
+        addStart(o, attrs);
+        for (int i = 0; i < bsmArgs.length; i++) {
+            AttributesImpl cattrs = new AttributesImpl();
+            cattrs.addAttribute("", "cst", "cst", "",
+                    SAXClassAdapter.encode(bsmArgs[i].toString()));
+            cattrs.addAttribute("", "desc", "desc", "",
+                    Type.getDescriptor(bsmArgs[i].getClass()));
+            addElement("bsmArg", cattrs);
+        }
+        addEnd(o);
+    }
+
+    public final void visitJumpInsn(int opcode, Label label) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "label", "label", "", getLabel(label));
+        addElement(AbstractVisitor.OPCODES[opcode], attrs);
+    }
+
+    public final void visitLabel(Label label) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "name", "name", "", getLabel(label));
+        addElement("Label", attrs);
+    }
+
+    public final void visitLdcInsn(Object cst) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("",
+                "cst",
+                "cst",
+                "",
+                SAXClassAdapter.encode(cst.toString()));
+        attrs.addAttribute("",
+                "desc",
+                "desc",
+                "",
+                Type.getDescriptor(cst.getClass()));
+        addElement(AbstractVisitor.OPCODES[Opcodes.LDC], attrs);
+    }
+
+    public final void visitIincInsn(int var, int increment) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "var", "var", "", Integer.toString(var));
+        attrs.addAttribute("", "inc", "inc", "", Integer.toString(increment));
+        addElement(AbstractVisitor.OPCODES[Opcodes.IINC], attrs);
+    }
+
+    public final void visitTableSwitchInsn(
+        int min,
+        int max,
+        Label dflt,
+        Label[] labels)
+    {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "min", "min", "", Integer.toString(min));
+        attrs.addAttribute("", "max", "max", "", Integer.toString(max));
+        attrs.addAttribute("", "dflt", "dflt", "", getLabel(dflt));
+        String o = AbstractVisitor.OPCODES[Opcodes.TABLESWITCH];
+        addStart(o, attrs);
+        for (int i = 0; i < labels.length; i++) {
+            AttributesImpl att2 = new AttributesImpl();
+            att2.addAttribute("", "name", "name", "", getLabel(labels[i]));
+            addElement("label", att2);
+        }
+        addEnd(o);
+    }
+
+    public final void visitLookupSwitchInsn(
+        Label dflt,
+        int[] keys,
+        Label[] labels)
+    {
+        AttributesImpl att = new AttributesImpl();
+        att.addAttribute("", "dflt", "dflt", "", getLabel(dflt));
+        String o = AbstractVisitor.OPCODES[Opcodes.LOOKUPSWITCH];
+        addStart(o, att);
+        for (int i = 0; i < labels.length; i++) {
+            AttributesImpl att2 = new AttributesImpl();
+            att2.addAttribute("", "name", "name", "", getLabel(labels[i]));
+            att2.addAttribute("", "key", "key", "", Integer.toString(keys[i]));
+            addElement("label", att2);
+        }
+        addEnd(o);
+    }
+
+    public final void visitMultiANewArrayInsn(String desc, int dims) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "desc", "desc", "", desc);
+        attrs.addAttribute("", "dims", "dims", "", Integer.toString(dims));
+        addElement(AbstractVisitor.OPCODES[Opcodes.MULTIANEWARRAY], attrs);
+    }
+
+    public final void visitTryCatchBlock(
+        Label start,
+        Label end,
+        Label handler,
+        String type)
+    {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "start", "start", "", getLabel(start));
+        attrs.addAttribute("", "end", "end", "", getLabel(end));
+        attrs.addAttribute("", "handler", "handler", "", getLabel(handler));
+        if (type != null)
+            attrs.addAttribute("", "type", "type", "", type);
+        addElement("TryCatch", attrs);
+    }
+
+    public final void visitMaxs(int maxStack, int maxLocals) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("",
+                "maxStack",
+                "maxStack",
+                "",
+                Integer.toString(maxStack));
+        attrs.addAttribute("",
+                "maxLocals",
+                "maxLocals",
+                "",
+                Integer.toString(maxLocals));
+        addElement("Max", attrs);
+
+        addEnd("code");
+    }
+
+    public void visitLocalVariable(
+        String name,
+        String desc,
+        String signature,
+        Label start,
+        Label end,
+        int index)
+    {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "name", "name", "", name);
+        attrs.addAttribute("", "desc", "desc", "", desc);
+        if (signature != null)
+            attrs.addAttribute("",
+                    "signature",
+                    "signature",
+                    "",
+                    SAXClassAdapter.encode(signature));
+        attrs.addAttribute("", "start", "start", "", getLabel(start));
+        attrs.addAttribute("", "end", "end", "", getLabel(end));
+        attrs.addAttribute("", "var", "var", "", Integer.toString(index));
+        addElement("LocalVar", attrs);
+    }
+
+    public final void visitLineNumber(int line, Label start) {
+        AttributesImpl attrs = new AttributesImpl();
+        attrs.addAttribute("", "line", "line", "", Integer.toString(line));
+        attrs.addAttribute("", "start", "start", "", getLabel(start));
+        addElement("LineNumber", attrs);
+    }
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        return new SAXAnnotationAdapter(getContentHandler(),
+                "annotationDefault",
+                0,
+                null,
+                null);
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return new SAXAnnotationAdapter(getContentHandler(),
+                "annotation",
+                visible ? 1 : -1,
+                null,
+                desc);
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, 
+        boolean visible,
+        boolean inCode)
+    {
+        return null;
+    }
+    //end jaime
+
+    public AnnotationVisitor visitParameterAnnotation(
+        int parameter,
+        String desc,
+        boolean visible)
+    {
+        return new SAXAnnotationAdapter(getContentHandler(),
+                "parameterAnnotation",
+                visible ? 1 : -1,
+                parameter,
+                desc);
+    }
+
+    public void visitEnd() {
+        addEnd("method");
+    }
+
+    public final void visitAttribute(Attribute attr) {
+        // TODO Auto-generated SAXCodeAdapter.visitAttribute
+    }
+
+    private final String getLabel(Label label) {
+        String name = (String) labelNames.get(label);
+        if (name == null) {
+            name = Integer.toString(labelNames.size());
+            labelNames.put(label, name);
+        }
+        return name;
+    }
+
+    @Override
+    public AnnotationVisitor visitInsnAnnotation(
+        int typeRef,
+        TypePath typePath,
+        String desc,
+        boolean visible)
+    {
+        return null;
+    }
+
+}
diff --git a/asmx/src/org/objectweb/asm/xml/SAXFieldAdapter.java b/asmx/src/org/objectweb/asm/xml/SAXFieldAdapter.java
new file mode 100644
index 0000000..195fd96
--- /dev/null
+++ b/asmx/src/org/objectweb/asm/xml/SAXFieldAdapter.java
@@ -0,0 +1,87 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * SAXFieldAdapter
+ * 
+ * @author Eugene Kuleshov
+ */
+public class SAXFieldAdapter implements FieldVisitor {
+    private final ContentHandler h;
+
+    public SAXFieldAdapter(ContentHandler h, AttributesImpl att) {
+        this.h = h;
+
+        try {
+            h.startElement("", "field", "field", att);
+        } catch (SAXException ex) {
+            throw new RuntimeException(ex.toString());
+        }
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+        return new SAXAnnotationAdapter(h,
+                "annotation",
+                visible ? 1 : -1,
+                null,
+                desc);
+    }
+
+    // jaime
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+        boolean visible,
+        boolean inCode)
+    {
+        throw new RuntimeException("Jaime did not implement yet");
+    }
+    //end jaime
+
+    public void visitAttribute(Attribute attr) {
+        // TODO Auto-generated method stub
+    }
+
+    public void visitEnd() {
+        try {
+            h.endElement("", "field", "field");
+        } catch (SAXException ex) {
+            throw new RuntimeException(ex.toString());
+        }
+    }
+
+}
diff --git a/asmx/test/README.txt b/asmx/test/README.txt
new file mode 100644
index 0000000..f4d1e7e
--- /dev/null
+++ b/asmx/test/README.txt
@@ -0,0 +1,16 @@
+This directory contains the tests of the product. 
+It contains(*) the following items:
+
+- lib: external libraries required to run the tests
+- conform: conformance tests (unit tests)
+- deviance: deviance tests (unit tests)
+- thread: multi-threading tests (unit tests)
+- stress: stress tests
+- perf: performance tests
+
+Each sub directory contains:
+- the source of the tests, with package struture if there is one,
+- the xml descriptors to launch test suites. An xml desriptor describes
+  the execution of a test suite. The default task is called on this file.
+
+(*) some items may not be present, depending on the product.
\ No newline at end of file
diff --git a/asmx/test/build.xml b/asmx/test/build.xml
new file mode 100644
index 0000000..0f92ec1
--- /dev/null
+++ b/asmx/test/build.xml
@@ -0,0 +1,205 @@
+<!--
+ ! ASM: a very small and fast Java bytecode manipulation framework
+ ! Copyright (c) 2000-2005 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.
+-->
+
+<project name="test" default="test">
+
+  <!-- ==================================== -->
+  <!-- ======== PROPERTY DEFINITION ======= -->
+  <!-- ==================================== -->
+
+  <property name="test.conform"   value="${test}/conform"/>
+  <property name="test.deviance"  value="${test}/deviance"/>
+  <property name="test.thread"    value="${test}/thread"/>
+  <property name="test.stress"    value="${test}/stress"/>
+  <property name="test.perf"      value="${test}/perf"/>
+
+  <target name="properties">
+    <condition property="asm.test" value="${java.home}/lib/rt.jar,test/conform/cases">
+      <not><isset property="asm.test"/></not>
+    </condition>
+    
+    <condition property="asm.test.class" value="">
+      <not><isset property="asm.test.class"/></not>
+    </condition>
+
+    <condition property="java5">
+      <available classname="java.lang.annotation.Annotation"/> 
+    </condition>
+      
+    <condition property="test.conform.exist">
+      <available file="${test.conform}"/>
+    </condition>
+
+    <condition property="test.deviance.exist">
+      <available file="${test.deviance}"/>
+    </condition>
+
+    <condition property="test.thread.exist">
+      <available file="${test.thread}"/>
+    </condition>
+
+    <condition property="test.stress.exist">
+      <available file="${test.stress}"/>
+    </condition>
+
+    <condition property="test.perf.exist">
+      <available file="${test.perf}"/>
+    </condition>
+    
+    <condition property="test.all">
+      <and>
+        <not><isset property="test.type"/></not>
+        <not><isset property="test.group"/></not>
+        <not><isset property="test.name"/></not>
+      </and>
+    </condition>
+
+    <condition property="test.paths.configured">
+      <and>
+        <isset property="bcel.path"/>
+        <isset property="serp.path"/>
+        <isset property="javassist.path"/>
+        <isset property="janino.path"/>
+      </and>
+    </condition>
+  </target>
+
+  <!-- ================================== -->
+  <!-- ========  INITIALIZATION   ======= -->
+  <!-- ================================== -->
+  
+  <target name="check" unless="test.paths.configured">
+    <echo message="The 'build.properties' file must be configured"/>
+    <fail/>
+  </target>
+  
+  <target name="init" depends="properties,check">  
+    <mkdir dir="${out.test}"/>
+    <mkdir dir="${out.test}/reports"/>
+    <path id="test.classpath">
+      <pathelement location="${classes}"/>
+      <pathelement location="${out.test}"/>
+      <pathelement path="${bcel.path}"/>
+      <pathelement path="${serp.path}"/>  
+      <pathelement path="${javassist.path}"/>
+      <pathelement path="${janino.path}"/>
+      <path refid="cobertura.classpath"/>
+    </path>
+  </target>
+  
+  <!-- ==================================== -->
+  <!-- =========== COMPILATION ============ -->
+  <!-- ==================================== -->
+
+  <target name="compile.test.conform" depends="init" if="test.conform.exist">
+    <javac srcdir="${test.conform}" destdir="${out.test}" 
+        debug="on" debuglevel="lines,vars,source">
+      <classpath refid="test.classpath"/>
+      <include name="**/*.java"/>
+      <exclude name="annotations/**/*.java" unless="java5"/>
+      <exclude name="**/AnnotationTest.java" unless="java5"/>
+    </javac>
+    <copy todir="${out.test}">
+  	  <fileset dir="${test.conform}">
+        <include name="**/*.txt"/>
+        <include name="**/*.data"/>
+      </fileset>
+    </copy>
+  </target>
+
+  <target name="compile.test.deviance" depends="init" if="test.deviance.exist">
+    <javac srcdir="${test.deviance}" destdir="${out.test}" debug="on" source="1.3" target="1.2">
+      <classpath refid="test.classpath"/>
+      <include name="**/*.java"/>
+    </javac>
+  </target>
+
+  <target name="compile.test.thread" depends="init" if="test.thread.exist">
+    <javac srcdir="${test.thread}" destdir="${out.test}" debug="on" source="1.3" target="1.2">
+      <classpath refid="test.classpath"/>
+      <include name="**/*.java"/>
+    </javac>
+  </target>
+
+  <target name="compile.test.stress" depends="init" if="test.stress.exist">
+    <javac srcdir="${test.stress}" destdir="${out.test}" debug="on" source="1.3" target="1.2">
+      <classpath refid="test.classpath"/>
+      <include name="**/*.java"/>
+    </javac>
+  </target>
+
+  <target name="compile.test.perf" depends="init" if="test.perf.exist">
+    <javac srcdir="${test.perf}" destdir="${out.test}" debug="on" source="1.3" target="1.2">
+      <classpath refid="test.classpath"/>
+      <include name="**/*.java"/>
+      <exclude name="**/xml/*.java"/>
+    </javac>
+  </target>
+
+  <target name="compile" depends="compile.test.conform,compile.test.deviance,compile.test.thread,compile.test.stress,compile.test.perf"/>
+
+  <!-- ============================= -->
+  <!-- =========== TEST ============ -->
+  <!-- ============================= -->
+
+  <target name="testAll" depends="compile" if="test.all">
+    <multipleAnt dir="${test.conform}" inheritRefs="true"/>
+    <!--multipleAnt dir="${test.deviance}" inheritRefs="true"/>
+    <multipleAnt dir="${test.thread}" inheritRefs="true"/>
+    <multipleAnt dir="${test.stress}" inheritRefs="true"/-->
+    <multipleAnt dir="${test.perf}" inheritRefs="true"/>
+  </target>
+
+  <target name="testType" depends="compile" if="test.type">
+    <multipleAnt dir="${test}/${test.type}" inheritRefs="true"/>
+  </target>
+
+  <target name="testGroup" depends="compile" if="test.group">
+    <ant antfile="test/${test.group}.xml" inheritRefs="true"/>
+  </target>
+
+  <target name="testName" depends="compile" if="test.name">
+    <multipleAnt dir="${test.conform}" target="${test.name}" inheritRefs="true"/>
+    <!--multipleAnt dir="${test.deviance}" target="${test.name}" inheritRefs="true"/>
+    <multipleAnt dir="${test.thread}" target="${test.name}" inheritRefs="true"/>
+    <multipleAnt dir="${test.stress}" target="${test.name}" inheritRefs="true"/-->
+    <multipleAnt dir="${test.perf}" target="${test.name}" inheritRefs="true"/>
+  </target>
+
+  <target name="test" depends="testAll,testType,testGroup,testName">
+    <!--junitreport todir="${out.test}/reports">
+      <fileset dir="${out.test}/reports">
+        <include name="TEST-*.xml"/>
+      </fileset>
+      <report todir="${out.test}/reports"/>
+    </junitreport-->
+  </target>
+  
+</project>
diff --git a/asmx/test/conform/adviceadapter.xml b/asmx/test/conform/adviceadapter.xml
new file mode 100644
index 0000000..58d4ef7
--- /dev/null
+++ b/asmx/test/conform/adviceadapter.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/AdviceAdapterTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/adviceadapter2.xml b/asmx/test/conform/adviceadapter2.xml
new file mode 100644
index 0000000..fd0abcf
--- /dev/null
+++ b/asmx/test/conform/adviceadapter2.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/AdviceAdapterTest2.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/annotation.xml b/asmx/test/conform/annotation.xml
new file mode 100644
index 0000000..ea76d1e
--- /dev/null
+++ b/asmx/test/conform/annotation.xml
@@ -0,0 +1,21 @@
+<project name="conform" default="test">
+
+  <target name="test" if="java5">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/AnnotationTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/annotations/ExtendedValues.class b/asmx/test/conform/annotations/ExtendedValues.class
new file mode 100644
index 0000000..f229464
--- /dev/null
+++ b/asmx/test/conform/annotations/ExtendedValues.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ExtendedValues.java b/asmx/test/conform/annotations/ExtendedValues.java
new file mode 100644
index 0000000..f763ae5
--- /dev/null
+++ b/asmx/test/conform/annotations/ExtendedValues.java
@@ -0,0 +1,82 @@
+
+package annotations;
+
+import java.util.*;
+
+@ValuesAnnotation(
+  byteValue = 1,
+  charValue = 'A',
+  booleanValue = true,
+  intValue = 1,
+  shortValue = 1,
+  longValue = 1L,
+  floatValue = 1.0f,
+  doubleValue = 1.0d,
+  stringValue = "A",
+
+  enumValue = ValuesEnum.ONE,
+  annotationValue = @ValueAttrAnnotation( "annotation"),
+  classValue = Values.class,
+
+  byteArrayValue = { 1, -1},
+  charArrayValue = { 'c', 'b', (char)-1},
+  booleanArrayValue = {true, false},
+  intArrayValue = { 1, -1},
+  shortArrayValue = { (short)1, (short)-1},
+  longArrayValue = { 1L, -1L},
+  floatArrayValue = { 1.0f, -1.0f},
+  doubleArrayValue = { 1.0d, -1.0d},
+  stringArrayValue = { "aa", "bb"},
+
+  enumArrayValue = {ValuesEnum.ONE, ValuesEnum.TWO},
+  annotationArrayValue = {@ValueAttrAnnotation( "annotation1"), @ValueAttrAnnotation( "annotation2")},
+  classArrayValue = {Values.class, Values.class}
+)
+@ValueAttrAnnotation1( "classAnnotation1")
+@ValueAttrAnnotation2( "classAnnotation2")
+public class ExtendedValues {
+
+  @ValueAttrAnnotation1( "fieldAnnotation1")
+  @ValueAttrAnnotation2( "fieldAnnotation2")
+  public String testfield = "test";
+
+  public Set<String> testSetOfString;
+  public Set<Map<String,String>> testSetOfMapOfStringString;
+
+  @SuppressWarnings("unchecked")
+  @ValueAttrAnnotation1( "methodAnnotation1")
+  @ValueAttrAnnotation2( "methodAnnotation2")
+  @ValueAttrAnnotation()
+  public void testMethod(
+      @ValueAttrAnnotation1( "param1Annotation1")
+      @ValueAttrAnnotation2( "param1Annotation2") String param1,
+      @ValueAttrAnnotation1( "param2Annotation1")
+      @ValueAttrAnnotation2( "param2Annotation2") int param2) {
+    // @ValueAttrAnnotation( "codeAnnotation")
+
+    Object o;
+    Integer testLocalVariable;
+    Map<String, Set<String>> testLocalMap;
+
+    o = new Object();
+
+    testLocalMap = new HashMap<String, Set<String>>();
+
+    if(o instanceof Map) {
+
+    }
+
+    if(o instanceof Set[][]) {
+      
+    }
+
+    testLocalVariable = (Integer) o;
+    testLocalMap = (HashMap<String, Set<String>>) o;
+
+  }
+
+  public Set<String> testMethod2() {
+    return null;
+  }
+
+}
diff --git a/asmx/test/conform/annotations/ExtendedValuesDump.class b/asmx/test/conform/annotations/ExtendedValuesDump.class
new file mode 100644
index 0000000..e57afca
--- /dev/null
+++ b/asmx/test/conform/annotations/ExtendedValuesDump.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ExtendedValuesDump.java b/asmx/test/conform/annotations/ExtendedValuesDump.java
new file mode 100644
index 0000000..3ff440f
--- /dev/null
+++ b/asmx/test/conform/annotations/ExtendedValuesDump.java
@@ -0,0 +1,3283 @@
+package annotations;
+import org.objectweb.asm.*;
+public class ExtendedValuesDump implements Opcodes {
+
+    public static byte[] dump () throws Exception {
+
+        ClassWriter cw = new ClassWriter(false);
+        FieldVisitor fv;
+        MethodVisitor mv;
+        AnnotationVisitor av0;
+
+        cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/Values", null, "java/lang/Object", null);
+
+        cw.visitSource("Values.java", null);
+
+        {
+            av0 = cw.visitAnnotation("Lannotations/ValuesAnnotation;", true);
+            av0.visit("byteValue", new Byte((byte)1));
+            av0.visit("charValue", new Character((char)65));
+            av0.visit("booleanValue", new Boolean(true));
+            av0.visit("intValue", new Integer(1));
+            av0.visit("shortValue", new Short((short)1));
+            av0.visit("longValue", new Long(1L));
+            av0.visit("floatValue", new Float("1.0"));
+            av0.visit("doubleValue", new Double("1.0"));
+            av0.visit("stringValue", "A");
+            av0.visitEnum("enumValue", "Lannotations/ValuesEnum;", "ONE");
+            {
+                AnnotationVisitor av1 = av0.visitAnnotation("annotationValue", "Lannotations/ValueAttrAnnotation;");
+                av1.visit("value", "annotation");
+                av1.visitEnd();
+            }
+            av0.visit("classValue", Type.getType("Lannotations/Values;"));
+            av0.visit("byteArrayValue", new byte[] {1,-1});
+            av0.visit("charArrayValue", new char[] {(char)99,(char)98,(char)65535});
+            av0.visit("booleanArrayValue", new boolean[] {true,false});
+            av0.visit("intArrayValue", new int[] {1,-1});
+            av0.visit("shortArrayValue", new short[] {(short)1,(short)-1});
+            av0.visit("longArrayValue", new long[] {1L,-1L});
+            av0.visit("floatArrayValue", new float[] {1.0f,-1.0f});
+            av0.visit("doubleArrayValue", new double[] {1.0d,-1.0d});
+            {
+                AnnotationVisitor av1 = av0.visitArray("stringArrayValue");
+                av1.visit(null, "aa");
+                av1.visit(null, "bb");
+                av1.visitEnd();
+            }
+            {
+                AnnotationVisitor av1 = av0.visitArray("enumArrayValue");
+                av1.visitEnum(null, "Lannotations/ValuesEnum;", "ONE");
+                av1.visitEnum(null, "Lannotations/ValuesEnum;", "TWO");
+                av1.visitEnd();
+            }
+            {
+                AnnotationVisitor av1 = av0.visitArray("annotationArrayValue");
+                {
+                    AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;");
+                    av2.visit("value", "annotation1");
+                    av2.visitEnd();
+                }
+                {
+                    AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;");
+                    av2.visit("value", "annotation2");
+                    av2.visitEnd();
+                }
+                av1.visitEnd();
+            }
+            {
+                AnnotationVisitor av1 = av0.visitArray("classArrayValue");
+                av1.visit(null, Type.getType("Lannotations/Values;"));
+                av1.visit(null, Type.getType("Lannotations/Values;"));
+                av1.visitEnd();
+            }
+            av0.visitEnd();
+        }
+        {
+            av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true);
+            av0.visit("value", "classAnnotation1");
+            av0.visitEnd();
+        }
+        {
+            av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true);
+            av0.visit("value", "classAnnotation2");
+            av0.visitEnd();
+        }
+        {
+            fv = cw.visitField(ACC_PUBLIC, "testfield", "Ljava/lang/String;", null, null);
+            {
+                av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true);
+                av0.visit("value", "fieldAnnotation1");
+                av0.visitEnd();
+            }
+            {
+                av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true);
+                av0.visit("value", "fieldAnnotation2");
+                av0.visitEnd();
+            }
+            fv.visitEnd();
+        }
+        {
+            mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitLdcInsn("test");
+            mv.visitFieldInsn(PUTFIELD, "annotations/Values", "testfield", "Ljava/lang/String;");
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(2, 1);
+            mv.visitEnd();
+        }
+        {
+            mv = cw.visitMethod(ACC_PUBLIC, "testMethod", "(Ljava/lang/String;I)V", null, null);
+            {
+                av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true);
+                av0.visit("value", "methodAnnotation1");
+                av0.visitEnd();
+            }
+            {
+                av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true);
+                av0.visit("value", "methodAnnotation2");
+                av0.visitEnd();
+            }
+            {
+                av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation1;", true);
+                av0.visit("value", "param1Annotation1");
+                av0.visitEnd();
+            }
+            {
+                av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation2;", true);
+                av0.visit("value", "param1Annotation2");
+                av0.visitEnd();
+            }
+            {
+                av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation1;", true);
+                av0.visit("value", "param2Annotation1");
+                av0.visitEnd();
+            }
+            {
+                av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation2;", true);
+                av0.visit("value", "param2Annotation2");
+                av0.visitEnd();
+            }
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(0, 3);
+            mv.visitEnd();
+        }
+        cw.visitEnd();
+
+        return cw.toByteArray();
+    }
+
+    public static byte[] dumpClassEmpty () throws Exception {
+      
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestClassEmpty", null, "java/lang/Object", null);
+
+      cw.visitSource("TestClassEmpty.java", null);
+
+      {
+        av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+        av0.visitEnd();
+      }
+      {
+        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+        mv.visitCode();
+        Label l0 = new Label();
+        mv.visitLabel(l0);
+        mv.visitLineNumber(3, l0);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+        mv.visitInsn(RETURN);
+        Label l1 = new Label();
+        mv.visitLabel(l1);
+        mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassEmpty;", null, l0, l1, 0);
+        mv.visitMaxs(1, 1);
+        mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+    }
+
+    public static byte[] dumpClassNonEmpty () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestClassNonEmpty", null, "java/lang/Object", null);
+
+      cw.visitSource("TestClassNonEmpty.java", null);
+
+      {
+      av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      av0.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "i", "I", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PRIVATE, "a", "Ljava/lang/String;", null, null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PRIVATE, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(7, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(8, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ICONST_0);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "i", "I");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(9, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l3, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PROTECTED, "<init>", "(Ljava/lang/String;)V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(11, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(12, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "a", "Ljava/lang/String;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(13, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l3, 0);
+      mv.visitLocalVariable("s", "Ljava/lang/String;", null, l0, l3, 1);
+      mv.visitMaxs(2, 2);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "i", "()I", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(16, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "i", "I");
+      mv.visitInsn(IRETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "a", "()Ljava/lang/String;", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(20, l0);
+      mv.visitTypeInsn(NEW, "java/lang/String");
+      mv.visitInsn(DUP);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestClassNonEmpty", "a", "Ljava/lang/String;");
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "(Ljava/lang/String;)V");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(21, l1);
+      mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
+      mv.visitInsn(DUP);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitMethodInsn(INVOKESTATIC, "java/lang/String", "valueOf", "(Ljava/lang/Object;)Ljava/lang/String;");
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "(Ljava/lang/String;)V");
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(22, l2);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitInsn(ARETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestClassNonEmpty;", null, l0, l3, 0);
+      mv.visitLocalVariable("s", "Ljava/lang/String;", null, l1, l3, 1);
+      mv.visitMaxs(3, 2);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    public static byte[] dumpFieldSimple () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestFieldSimple", null, "java/lang/Object", null);
+
+      cw.visitSource("TestFieldSimple.java", null);
+
+      {
+      av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      av0.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "i", "I", null, null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      av0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PRIVATE, "j", "I", null, null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      av0.visit("value", "Hello");
+      av0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PROTECTED, "o", "Ljava/lang/Object;", null, null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      av0.visitEnd();
+      }
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      av0.visit("value", "H");
+      av0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "s", "Ljava/lang/String;", null, null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      av0.visit("value", "E");
+      av0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "f", "Lannotations/tests/classfile/cases/TestFieldSimple;", null, null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(3, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(7, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ACONST_NULL);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestFieldSimple", "s", "Ljava/lang/String;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(8, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ACONST_NULL);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestFieldSimple", "f", "Lannotations/tests/classfile/cases/TestFieldSimple;");
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(3, l3);
+      mv.visitInsn(RETURN);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestFieldSimple;", null, l0, l4, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    
+    public static byte[] dumpFieldGeneric () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestFieldGeneric", "<T:Ljava/lang/Object;>Ljava/lang/Object;", "java/lang/Object", null);
+
+      cw.visitSource("TestFieldGeneric.java", null);
+
+      {
+      av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      av0.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "s", "Ljava/lang/String;", null, null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/F;", true);
+      av0.visit("fieldA", new Integer(1));
+      av0.visit("fieldB", "fi");
+      av0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "list", "Ljava/util/List;", "Ljava/util/List<Ljava/lang/String;>;", null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/G;", true);
+      av0.visit("fieldA", new Integer(3));
+      av0.visit("fieldB", "three");
+      av0.visit("fieldC", new boolean[] {true,false});
+      av0.visit("fieldD", new Integer(2));
+      av0.visit("fieldE", new Integer(4));
+      av0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "set", "Ljava/util/Set;", "Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric;>;", null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/E;", true);
+      av0.visit("fieldA", new Integer(2));
+      av0.visit("fieldB", "rh");
+      av0.visitEnd();
+      }
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/F;", true);
+      av0.visit("fieldA", new Integer(1));
+      av0.visit("fieldB", "if");
+      av0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "testFieldGeneric", "Lannotations/tests/classfile/cases/TestFieldGeneric;", "Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;", null);
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "otherSet", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "nestedSet", "Ljava/util/Set;", "Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric<Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric;>;>;>;", null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      av0.visit("value", "nested");
+      av0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/F;", true);
+      xav0.visit("fieldA", new Integer(1));
+      xav0.visit("fieldB", "n");
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(-2));
+      xav0.visit("fieldB", "nl");
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "nil");
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/D;", true);
+      xav0.visit("fieldA", new Integer(-1));
+      xav0.visit("fieldB", "hello");
+      xav0.visit("fieldC", new int[] {3,2,4});
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(3));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "nestedMap", "Ljava/util/Map;", "Ljava/util/Map<Ljava/util/Set<Lannotations/tests/classfile/cases/TestFieldGeneric;>;Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;>;", null);
+      {
+      av0 = fv.visitAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      av0.visit("fieldA", new Integer(1));
+      av0.visit("fieldB", "nested");
+      av0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "inner most T");
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(256));
+      xav0.visit("fieldB", "hello");
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = fv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "inner most F");
+      xav0.visitXTargetType(new Integer(11));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(13, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(11, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "annotations/tests/classfile/cases/TestFieldGeneric");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestFieldGeneric", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestFieldGeneric", "testFieldGeneric", "Lannotations/tests/classfile/cases/TestFieldGeneric;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(15, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestFieldGeneric;", "Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;", l0, l3, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "toString", "()Ljava/lang/String;", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(20, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestFieldGeneric", "s", "Ljava/lang/String;");
+      mv.visitInsn(ARETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestFieldGeneric;", "Lannotations/tests/classfile/cases/TestFieldGeneric<TT;>;", l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    public static byte[] dumpLocalVariable () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestLocalVariable", "<T:Ljava/lang/Object;>Ljava/lang/Object;", "java/lang/Object", null);
+
+      cw.visitSource("TestLocalVariable.java", null);
+
+      {
+      av0 = cw.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      av0.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "i", "I", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/util/Set;>;", null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(166));
+      xav0.visit("fieldB", "good");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(6));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(10, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(11, l1);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ISTORE, 1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(12, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitInsn(ICONST_0);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I");
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(13, l3);
+      mv.visitInsn(RETURN);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l4, 0);
+      mv.visitLocalVariable("t", "I", null, l2, l4, 1);
+      mv.visitMaxs(2, 2);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(I)V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(15, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(16, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ILOAD, 1);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(17, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l3, 0);
+      mv.visitLocalVariable("i", "I", null, l0, l3, 1);
+      mv.visitMaxs(2, 2);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/Integer;)V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(19, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(20, l1);
+      mv.visitInsn(ICONST_1);
+      mv.visitVarInsn(ISTORE, 2);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(21, l2);
+      mv.visitIincInsn(2, 1);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(22, l3);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I");
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLineNumber(23, l4);
+      mv.visitIincInsn(2, -1);
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLineNumber(24, l5);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ILOAD, 2);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I");
+      Label l6 = new Label();
+      mv.visitLabel(l6);
+      mv.visitLineNumber(25, l6);
+      mv.visitInsn(RETURN);
+      Label l7 = new Label();
+      mv.visitLabel(l7);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l7, 0);
+      mv.visitLocalVariable("j", "Ljava/lang/Integer;", null, l0, l7, 1);
+      mv.visitLocalVariable("k", "I", null, l2, l7, 2);
+      mv.visitMaxs(2, 3);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "i", "()I", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(28, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariable", "i", "I");
+      mv.visitInsn(IRETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "j", "()I", null, null);
+      {
+      av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      av0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "hello");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(2));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(32, l0);
+      mv.visitInsn(ICONST_1);
+      mv.visitVarInsn(ISTORE, 1);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(33, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "annotations/tests/classfile/cases/TestLocalVariable", "j", "()I");
+      mv.visitInsn(IRETURN);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariable;", "Lannotations/tests/classfile/cases/TestLocalVariable<TT;>;", l0, l2, 0);
+      mv.visitLocalVariable("temp", "I", null, l1, l2, 1);
+      mv.visitMaxs(1, 2);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "someMethod", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(37, l0);
+      mv.visitTypeInsn(NEW, "annotations/tests/classfile/cases/TestLocalVariable");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestLocalVariable", "<init>", "()V");
+      mv.visitVarInsn(ASTORE, 0);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(38, l1);
+      mv.visitTypeInsn(NEW, "java/lang/String");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "()V");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(39, l2);
+      mv.visitLdcInsn(new Double("2.0"));
+      mv.visitMethodInsn(INVOKESTATIC, "java/lang/Double", "valueOf", "(D)Ljava/lang/Double;");
+      mv.visitVarInsn(ASTORE, 2);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(40, l3);
+      mv.visitInsn(RETURN);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLocalVariable("t", "Lannotations/tests/classfile/cases/TestLocalVariable;", null, l1, l4, 0);
+      mv.visitLocalVariable("s", "Ljava/lang/String;", null, l2, l4, 1);
+      mv.visitLocalVariable("d", "Ljava/lang/Double;", null, l3, l4, 2);
+      mv.visitMaxs(2, 3);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(43, l0);
+      mv.visitInsn(ICONST_1);
+      mv.visitVarInsn(ISTORE, 1);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(44, l1);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
+      mv.visitVarInsn(ISTORE, 2);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(45, l2);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
+      mv.visitVarInsn(ISTORE, 3);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(46, l3);
+      mv.visitVarInsn(ILOAD, 2);
+      Label l4 = new Label();
+      mv.visitJumpInsn(IFEQ, l4);
+      mv.visitVarInsn(ILOAD, 3);
+      mv.visitJumpInsn(IFEQ, l4);
+      mv.visitInsn(ICONST_1);
+      Label l5 = new Label();
+      mv.visitJumpInsn(GOTO, l5);
+      mv.visitLabel(l4);
+      mv.visitInsn(ICONST_0);
+      mv.visitLabel(l5);
+      mv.visitVarInsn(ISTORE, 1);
+      Label l6 = new Label();
+      mv.visitLabel(l6);
+      mv.visitLineNumber(47, l6);
+      mv.visitVarInsn(ILOAD, 1);
+      Label l7 = new Label();
+      mv.visitJumpInsn(IFNE, l7);
+      mv.visitVarInsn(ILOAD, 3);
+      Label l8 = new Label();
+      mv.visitJumpInsn(IFEQ, l8);
+      mv.visitLabel(l7);
+      mv.visitLineNumber(48, l7);
+      mv.visitVarInsn(ILOAD, 1);
+      mv.visitVarInsn(ISTORE, 2);
+      mv.visitLabel(l8);
+      mv.visitLineNumber(50, l8);
+      mv.visitVarInsn(ILOAD, 2);
+      Label l9 = new Label();
+      mv.visitJumpInsn(IFEQ, l9);
+      Label l10 = new Label();
+      mv.visitLabel(l10);
+      mv.visitLineNumber(51, l10);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitLdcInsn("Message");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
+      mv.visitLabel(l9);
+      mv.visitLineNumber(53, l9);
+      mv.visitInsn(RETURN);
+      Label l11 = new Label();
+      mv.visitLabel(l11);
+      mv.visitLocalVariable("args", "[Ljava/lang/String;", null, l0, l11, 0);
+      mv.visitLocalVariable("b", "Z", null, l1, l11, 1);
+      mv.visitLocalVariable("b1", "Z", null, l2, l11, 2);
+      mv.visitLocalVariable("b2", "Z", null, l3, l11, 3);
+      mv.visitMaxs(2, 4);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    
+    public static byte[] dumpLocalVariableGenericArray () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", null, "java/lang/Object", null);
+
+      cw.visitSource("TestLocalVariableGenericArray.java", null);
+
+      {
+      fv = cw.visitField(0, "i", "Ljava/lang/Integer;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "map1", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;", null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(0, "map2", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/ArrayList<Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;>;>;", null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "good");
+      xav0.visitXTargetType(new Integer(8));
+      xav0.visitXStartPc(new Integer(37));
+      xav0.visitXLength(new Integer(55));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "first param");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(37));
+      xav0.visitXLength(new Integer(55));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "second param");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(37));
+      xav0.visitXLength(new Integer(55));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(17, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(18, l1);
+      mv.visitInsn(ICONST_1);
+      mv.visitVarInsn(ISTORE, 1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(19, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "map2", "Ljava/util/Map;");
+      mv.visitLdcInsn("4gf");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/ArrayList");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "iterator", "()Ljava/util/Iterator;");
+      mv.visitVarInsn(ASTORE, 3);
+      Label l3 = new Label();
+      mv.visitJumpInsn(GOTO, l3);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitVarInsn(ALOAD, 3);
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Map");
+      mv.visitVarInsn(ASTORE, 2);
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLineNumber(20, l5);
+      mv.visitVarInsn(ILOAD, 1);
+      mv.visitInsn(ICONST_5);
+      Label l6 = new Label();
+      mv.visitJumpInsn(IF_ICMPGE, l6);
+      Label l7 = new Label();
+      mv.visitLabel(l7);
+      mv.visitLineNumber(21, l7);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "map2", "Ljava/util/Map;");
+      mv.visitLdcInsn("");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/ArrayList");
+      mv.visitTypeInsn(NEW, "java/util/ArrayList");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/util/ArrayList", "indexOf", "(Ljava/lang/Object;)I");
+      mv.visitVarInsn(ISTORE, 1);
+      Label l8 = new Label();
+      mv.visitJumpInsn(GOTO, l8);
+      mv.visitLabel(l6);
+      mv.visitLineNumber(23, l6);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "i", "Ljava/lang/Integer;");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
+      mv.visitInsn(ICONST_5);
+      mv.visitInsn(IADD);
+      mv.visitVarInsn(ISTORE, 1);
+      mv.visitLabel(l8);
+      mv.visitLineNumber(25, l8);
+      mv.visitIincInsn(1, 1);
+      mv.visitLabel(l3);
+      mv.visitLineNumber(19, l3);
+      mv.visitVarInsn(ALOAD, 3);
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z");
+      mv.visitJumpInsn(IFNE, l4);
+      Label l9 = new Label();
+      mv.visitLabel(l9);
+      mv.visitLineNumber(27, l9);
+      mv.visitInsn(RETURN);
+      Label l10 = new Label();
+      mv.visitLabel(l10);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l10, 0);
+      mv.visitLocalVariable("k", "I", null, l2, l10, 1);
+      mv.visitLocalVariable("e", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;", l5, l9, 2);
+      mv.visitMaxs(3, 4);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "someMethod", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "bad");
+      xav0.visitXTargetType(new Integer(8));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(26));
+      xav0.visitXIndex(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(0));
+      xav0.visit("fieldB", "String");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(26));
+      xav0.visitXIndex(new Integer(1));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(30, l0);
+      mv.visitTypeInsn(NEW, "java/util/HashSet");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(31, l1);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitTypeInsn(NEW, "java/lang/String");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "()V");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add", "(Ljava/lang/Object;)Z");
+      mv.visitInsn(POP);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(32, l2);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add", "(Ljava/lang/Object;)Z");
+      mv.visitInsn(POP);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(33, l3);
+      mv.visitInsn(RETURN);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l4, 0);
+      mv.visitLocalVariable("s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", l1, l4, 1);
+      mv.visitMaxs(3, 2);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PRIVATE, "someMethod2", "(I)I", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(0));
+      xav0.visit("fieldB", "Boolean");
+      xav0.visitXTargetType(new Integer(8));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(66));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(66));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(8));
+      xav0.visitXStartPc(new Integer(16));
+      xav0.visitXLength(new Integer(58));
+      xav0.visitXIndex(new Integer(3));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "inner-type");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(16));
+      xav0.visitXLength(new Integer(58));
+      xav0.visitXIndex(new Integer(3));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(36, l0);
+      mv.visitTypeInsn(NEW, "java/util/HashSet");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V");
+      mv.visitVarInsn(ASTORE, 2);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(37, l1);
+      mv.visitTypeInsn(NEW, "java/util/HashSet");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V");
+      mv.visitVarInsn(ASTORE, 3);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(38, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "someMethod3", "()Z");
+      mv.visitVarInsn(ISTORE, 4);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(39, l3);
+      mv.visitVarInsn(ALOAD, 2);
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/lang/Boolean");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z");
+      mv.visitVarInsn(ILOAD, 4);
+      mv.visitInsn(IAND);
+      Label l4 = new Label();
+      mv.visitJumpInsn(IFEQ, l4);
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLineNumber(40, l5);
+      mv.visitVarInsn(ILOAD, 4);
+      Label l6 = new Label();
+      mv.visitJumpInsn(IFEQ, l6);
+      mv.visitVarInsn(ILOAD, 1);
+      Label l7 = new Label();
+      mv.visitJumpInsn(GOTO, l7);
+      mv.visitLabel(l6);
+      mv.visitVarInsn(ALOAD, 3);
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I");
+      mv.visitLabel(l7);
+      mv.visitInsn(IRETURN);
+      mv.visitLabel(l4);
+      mv.visitLineNumber(42, l4);
+      mv.visitVarInsn(ILOAD, 1);
+      mv.visitInsn(IRETURN);
+      Label l8 = new Label();
+      mv.visitLabel(l8);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l8, 0);
+      mv.visitLocalVariable("i", "I", null, l0, l8, 1);
+      mv.visitLocalVariable("s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/Boolean;>;", l1, l8, 2);
+      mv.visitLocalVariable("ints", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/Integer;>;", l2, l8, 3);
+      mv.visitLocalVariable("b", "Z", null, l3, l8, 4);
+      mv.visitMaxs(2, 5);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PRIVATE, "someMethod3", "()Z", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "t");
+      xav0.visitXTargetType(new Integer(8));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(70));
+      xav0.visitXIndex(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "map key string");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(70));
+      xav0.visitXIndex(new Integer(1));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "map value set");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(70));
+      xav0.visitXIndex(new Integer(1));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(70));
+      xav0.visitXIndex(new Integer(1));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(10));
+      xav0.visitXLength(new Integer(68));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(1));
+      xav0.visit("fieldB", "set of maps");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(10));
+      xav0.visitXLength(new Integer(68));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "maps");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(10));
+      xav0.visitXLength(new Integer(68));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "map key is integer");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(10));
+      xav0.visitXLength(new Integer(68));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(3));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "map value is 2-d array");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(10));
+      xav0.visitXLength(new Integer(68));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(3));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "first dimension");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(10));
+      xav0.visitXLength(new Integer(68));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(4));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "second dimension");
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(10));
+      xav0.visitXLength(new Integer(68));
+      xav0.visitXIndex(new Integer(2));
+      xav0.visitXLocationLength(new Integer(4));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(46, l0);
+      mv.visitTypeInsn(NEW, "java/util/HashMap");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(47, l1);
+      mv.visitInsn(ACONST_NULL);
+      mv.visitVarInsn(ASTORE, 2);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(49, l2);
+      mv.visitVarInsn(ALOAD, 2);
+      mv.visitLdcInsn("3");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Set");
+      mv.visitTypeInsn(NEW, "java/util/HashMap");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "add", "(Ljava/lang/Object;)Z");
+      mv.visitInsn(POP);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(51, l3);
+      mv.visitVarInsn(ALOAD, 2);
+      mv.visitLdcInsn("4");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Set");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Map");
+      mv.visitInsn(ICONST_3);
+      mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "[[Ljava/lang/String;");
+      mv.visitInsn(ICONST_2);
+      mv.visitInsn(AALOAD);
+      mv.visitInsn(ICONST_4);
+      mv.visitLdcInsn("Hello");
+      mv.visitInsn(AASTORE);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLineNumber(53, l4);
+      mv.visitInsn(ICONST_1);
+      mv.visitInsn(IRETURN);
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l5, 0);
+      mv.visitLocalVariable("t", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;", l1, l5, 1);
+      mv.visitLocalVariable("s", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/util/Map<Ljava/lang/Integer;[[Ljava/lang/String;>;>;>;", l2, l5, 2);
+      mv.visitMaxs(3, 3);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PROTECTED, "someMethod4", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(9));
+      xav0.visitXStartPc(new Integer(8));
+      xav0.visitXLength(new Integer(10));
+      xav0.visitXIndex(new Integer(1));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      Label l1 = new Label();
+      mv.visitTryCatchBlock(l0, l1, l1, "java/lang/Exception");
+      mv.visitLabel(l0);
+      mv.visitLineNumber(58, l0);
+      mv.visitTypeInsn(NEW, "java/util/HashSet");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(60, l2);
+      mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+      mv.visitInsn(DUP);
+      mv.visitLdcInsn("Hello");
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
+      mv.visitInsn(ATHROW);
+      mv.visitLabel(l1);
+      mv.visitLineNumber(61, l1);
+      mv.visitVarInsn(ASTORE, 1);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(62, l3);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestLocalVariableGenericArray", "i", "Ljava/lang/Integer;");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V");
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLineNumber(64, l4);
+      mv.visitInsn(RETURN);
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestLocalVariableGenericArray;", null, l0, l5, 0);
+      mv.visitLocalVariable("s", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", l2, l1, 1);
+      mv.visitLocalVariable("e", "Ljava/lang/Exception;", null, l3, l4, 1);
+      mv.visitMaxs(3, 2);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    public static byte[] dumpMethodReceiver () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestMethodReceiver", null, "java/lang/Object", null);
+
+      cw.visitSource("TestMethodReceiver.java", null);
+
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(9, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitInsn(RETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(6));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "first method");
+      xav0.visitXTargetType(new Integer(6));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(12, l0);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitLdcInsn("test()");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(13, l1);
+      mv.visitInsn(RETURN);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PRIVATE, "test2", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(2));
+      xav0.visit("fieldB", "rec");
+      xav0.visitXTargetType(new Integer(6));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(16, l0);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitLdcInsn("test2()");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(17, l1);
+      mv.visitInsn(RETURN);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PROTECTED, "test3", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(6));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(20, l0);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitLdcInsn("test3()");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(21, l1);
+      mv.visitInsn(RETURN);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(0, "test4", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "last method");
+      xav0.visitXTargetType(new Integer(6));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(24, l0);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitLdcInsn("test4()");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(25, l1);
+      mv.visitInsn(RETURN);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReceiver;", null, l0, l2, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+
+    public static byte[] dumpMethodReturnTypeGenericArray () throws Exception {
+
+    ClassWriter cw = new ClassWriter(false);
+    FieldVisitor fv;
+    MethodVisitor mv;
+    AnnotationVisitor av0;
+    TypeAnnotationVisitor xav0;
+
+    cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray", null, "java/lang/Object", null);
+
+    cw.visitSource("TestMethodReturnTypeGenericArray.java", null);
+
+    {
+    mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+    mv.visitCode();
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitLineNumber(7, l0);
+    mv.visitVarInsn(ALOAD, 0);
+    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+    mv.visitInsn(RETURN);
+    Label l1 = new Label();
+    mv.visitLabel(l1);
+    mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+    }
+    {
+    mv = cw.visitMethod(ACC_PUBLIC, "test", "()Ljava/util/List;", null, null);
+    {
+    av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+    av0.visitEnd();
+    }
+    mv.visitCode();
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitLineNumber(10, l0);
+    mv.visitInsn(ACONST_NULL);
+    mv.visitInsn(ARETURN);
+    Label l1 = new Label();
+    mv.visitLabel(l1);
+    mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+    }
+    {
+    mv = cw.visitMethod(ACC_PUBLIC, "test2", "()Ljava/util/List;", "()Ljava/util/List<Ljava/lang/String;>;", null);
+    {
+    av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    av0.visit("value", "single-depth");
+    av0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    mv.visitCode();
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitLineNumber(14, l0);
+    mv.visitInsn(ACONST_NULL);
+    mv.visitInsn(ARETURN);
+    Label l1 = new Label();
+    mv.visitLabel(l1);
+    mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+    }
+    {
+    mv = cw.visitMethod(ACC_PUBLIC, "test3", "()[Ljava/lang/String;", null, null);
+    {
+    av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+    av0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "on array element");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    mv.visitCode();
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitLineNumber(18, l0);
+    mv.visitInsn(ACONST_NULL);
+    mv.visitInsn(ARETURN);
+    Label l1 = new Label();
+    mv.visitLabel(l1);
+    mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+    }
+    {
+    mv = cw.visitMethod(ACC_PUBLIC, "test4", "()[[Ljava/lang/String;", null, null);
+    {
+    av0 = mv.visitAnnotation("Lannotations/tests/classfile/foo/A;", true);
+    av0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "on");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "in");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(1));
+    xav0.visitXLocation(new Integer(1));
+    xav0.visitEnd();
+    }
+    mv.visitCode();
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitLineNumber(22, l0);
+    mv.visitInsn(ACONST_NULL);
+    mv.visitInsn(ARETURN);
+    Label l1 = new Label();
+    mv.visitLabel(l1);
+    mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+    }
+    {
+    mv = cw.visitMethod(ACC_PUBLIC, "test5", "()Ljava/util/Set;", "()Ljava/util/Set<[Ljava/lang/String;>;", null);
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "two-deep");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(2));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    mv.visitCode();
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitLineNumber(26, l0);
+    mv.visitInsn(ACONST_NULL);
+    mv.visitInsn(ARETURN);
+    Label l1 = new Label();
+    mv.visitLabel(l1);
+    mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+    }
+    {
+    mv = cw.visitMethod(ACC_PUBLIC, "test6", "()Ljava/util/Map;", "()Ljava/util/Map<Ljava/util/Map<[Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;Ljava/util/Set<[Ljava/lang/String;>;>;", null);
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "map as key");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "array of value");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(2));
+    xav0.visitXLocation(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "inner-most value");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(3));
+    xav0.visitXLocation(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "set as value");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(1));
+    xav0.visitXLocation(new Integer(1));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+    xav0.visit("value", "innermost key or key");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(3));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(2));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+    xav0.visit("fieldA", new Integer(1));
+    xav0.visit("fieldB", "value of key");
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(2));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitXLocation(new Integer(1));
+    xav0.visitEnd();
+    }
+    {
+    xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+    xav0.visitXTargetType(new Integer(11));
+    xav0.visitXLocationLength(new Integer(3));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitXLocation(new Integer(1));
+    xav0.visitXLocation(new Integer(0));
+    xav0.visitEnd();
+    }
+    mv.visitCode();
+    Label l0 = new Label();
+    mv.visitLabel(l0);
+    mv.visitLineNumber(30, l0);
+    mv.visitInsn(ACONST_NULL);
+    mv.visitInsn(ARETURN);
+    Label l1 = new Label();
+    mv.visitLabel(l1);
+    mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestMethodReturnTypeGenericArray;", null, l0, l1, 0);
+    mv.visitMaxs(1, 1);
+    mv.visitEnd();
+    }
+    cw.visitEnd();
+
+    return cw.toByteArray();
+    }
+    
+    public static byte[] dumpObjectCreation () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestObjectCreation", null, "java/lang/Object", null);
+
+      cw.visitSource("TestObjectCreation.java", null);
+
+      {
+      fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(7, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitInsn(RETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "first new");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "a string");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(23));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(11, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/lang/Object");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(12, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/lang/String");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(13, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/lang/String");
+      mv.visitInsn(DUP);
+      mv.visitLdcInsn("");
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/String", "<init>", "(Ljava/lang/String;)V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(14, l3);
+      mv.visitInsn(RETURN);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l4, 0);
+      mv.visitMaxs(4, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(7));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(14));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(17, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitLdcInsn("str");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(18, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/util/ArrayList");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(19, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l3, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "new");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(22, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/util/HashSet");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(23, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/util/HashMap");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(24, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l3, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "self test");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(13));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(27, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/lang/Integer");
+      mv.visitInsn(DUP);
+      mv.visitInsn(ICONST_2);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Integer", "<init>", "(I)V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(28, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "annotations/tests/classfile/cases/TestObjectCreation");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "annotations/tests/classfile/cases/TestObjectCreation", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreation", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(29, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreation;", null, l0, l3, 0);
+      mv.visitMaxs(4, 1);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    
+    public static byte[] dumpObjectCreationGenericArray () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", null, "java/lang/Object", null);
+
+      cw.visitSource("TestObjectCreationGenericArray.java", null);
+
+      {
+      fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(9, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitInsn(RETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "first new");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(3));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(3));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(13, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitIntInsn(BIPUSH, 10);
+      mv.visitIntInsn(NEWARRAY, T_INT);
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(14, l1);
+      mv.visitInsn(RETURN);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l2, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(23));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "str");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(23));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(17, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitLdcInsn("str");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(18, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/util/ArrayList");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(19, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l3, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "new");
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "map");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "map key string");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "first level");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "value");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "on the array");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "on array elements");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitXLocationLength(new Integer(3));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(22, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/util/HashSet");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashSet", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(23, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/util/HashMap");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(24, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l3, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(4));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "key");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "value");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "key element");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "value array");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "value array element");
+      xav0.visitXTargetType(new Integer(5));
+      xav0.visitXOffset(new Integer(1));
+      xav0.visitXLocationLength(new Integer(3));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(27, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/util/HashMap");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestObjectCreationGenericArray", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(28, l1);
+      mv.visitInsn(RETURN);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestObjectCreationGenericArray;", null, l0, l2, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    
+    public static byte[] dumpTypecast () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestTypecast", null, "java/lang/Object", null);
+
+      cw.visitSource("TestTypecast.java", null);
+
+      {
+      fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "s", "Ljava/lang/String;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "i", "Ljava/lang/Integer;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "b", "Ljava/lang/Boolean;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "set", "Ljava/util/Set;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "hset", "Ljava/util/HashSet;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "map", "Ljava/util/Map;", null, null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(7, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitInsn(RETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecast;", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(21));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "second cast");
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(32));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(3));
+      xav0.visit("fieldB", "cast");
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(59));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(4));
+      xav0.visit("fieldB", "cast");
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(70));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(17, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(18, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "s", "Ljava/lang/String;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(19, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/lang/String");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "s", "Ljava/lang/String;");
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(20, l3);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "i", "Ljava/lang/Integer;");
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLineNumber(21, l4);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "b", "Ljava/lang/Boolean;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "b", "Ljava/lang/Boolean;");
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLineNumber(22, l5);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "hset", "Ljava/util/HashSet;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "set", "Ljava/util/Set;");
+      Label l6 = new Label();
+      mv.visitLabel(l6);
+      mv.visitLineNumber(23, l6);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "set", "Ljava/util/Set;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/HashSet");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "hset", "Ljava/util/HashSet;");
+      Label l7 = new Label();
+      mv.visitLabel(l7);
+      mv.visitLineNumber(24, l7);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecast", "hset", "Ljava/util/HashSet;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Map");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "map", "Ljava/util/Map;");
+      Label l8 = new Label();
+      mv.visitLabel(l8);
+      mv.visitLineNumber(25, l8);
+      mv.visitInsn(ICONST_0);
+      mv.visitVarInsn(ISTORE, 1);
+      Label l9 = new Label();
+      mv.visitLabel(l9);
+      mv.visitLineNumber(26, l9);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ILOAD, 1);
+      mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "i", "Ljava/lang/Integer;");
+      Label l10 = new Label();
+      mv.visitLabel(l10);
+      mv.visitLineNumber(27, l10);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ILOAD, 1);
+      mv.visitMethodInsn(INVOKESTATIC, "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecast", "o", "Ljava/lang/Object;");
+      Label l11 = new Label();
+      mv.visitLabel(l11);
+      mv.visitLineNumber(28, l11);
+      mv.visitInsn(RETURN);
+      Label l12 = new Label();
+      mv.visitLabel(l12);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecast;", null, l0, l12, 0);
+      mv.visitLocalVariable("pi", "I", null, l9, l12, 1);
+      mv.visitMaxs(2, 2);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    public static byte[] dumpTypecastGenericArray () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestTypecastGenericArray", null, "java/lang/Object", null);
+
+      cw.visitSource("TestTypecastGenericArray.java", null);
+
+      {
+      fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "s", "Ljava/lang/String;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "i", "Ljava/lang/Integer;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "b", "Ljava/lang/Boolean;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "set", "Ljava/util/Set;", "Ljava/util/Set<Ljava/lang/String;>;", null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "hset", "Ljava/util/HashSet;", "Ljava/util/HashSet<Ljava/util/Set<Ljava/lang/String;>;>;", null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_PUBLIC, "map", "Ljava/util/Map;", "Ljava/util/Map<Ljava/util/Set<Ljava/lang/String;>;Ljava/util/Set<Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;>;>;", null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(8, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitInsn(RETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(21));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "second");
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(32));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(18, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(19, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "s", "Ljava/lang/String;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(20, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/lang/String");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "s", "Ljava/lang/String;");
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(21, l3);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/lang/Integer");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "i", "Ljava/lang/Integer;");
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLineNumber(22, l4);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "b", "Ljava/lang/Boolean;");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "b", "Ljava/lang/Boolean;");
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLineNumber(23, l5);
+      mv.visitInsn(RETURN);
+      Label l6 = new Label();
+      mv.visitLabel(l6);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l6, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(5));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "B");
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(16));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/C;", true);
+      xav0.visit("fieldA", new Integer(2));
+      xav0.visit("fieldB", "");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(16));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(27, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/HashSet");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(28, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Set");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(29, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l3, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(20));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "v");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(23));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(33, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "map", "Ljava/util/Map;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "keySet", "()Ljava/util/Set;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/HashSet");
+      mv.visitTypeInsn(CHECKCAST, "java/util/HashSet");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(34, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/HashSet");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "hset", "Ljava/util/HashSet;");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(35, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l3, 0);
+      mv.visitMaxs(2, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(5));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "second");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(5));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(15));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(15));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "set");
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(30));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(43));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "on the set");
+      xav0.visitXTargetType(new Integer(0));
+      xav0.visitXOffset(new Integer(53));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "on value");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(53));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(39, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Map");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "map", "Ljava/util/Map;");
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(40, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Set");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(41, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "map", "Ljava/util/Map;");
+      mv.visitInsn(ACONST_NULL);
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Set");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Set", "iterator", "()Ljava/util/Iterator;");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Map");
+      mv.visitLdcInsn("");
+      mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Set");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "set", "Ljava/util/Set;");
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(42, l3);
+      mv.visitInsn(RETURN);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l4, 0);
+      mv.visitLocalVariable("t", "Ljava/util/Set;", "Ljava/util/Set<Ljava/util/Map<Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;>;>;", l2, l4, 1);
+      mv.visitMaxs(3, 2);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test5", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "string is key");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(4));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "2d-array is value");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(4));
+      xav0.visitXLocationLength(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "first dimension");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(4));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(0));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "second dimension");
+      xav0.visitXTargetType(new Integer(1));
+      xav0.visitXOffset(new Integer(4));
+      xav0.visitXLocationLength(new Integer(2));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitXLocation(new Integer(1));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(47, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypecastGenericArray", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(CHECKCAST, "java/util/Map");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(48, l1);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V");
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(49, l2);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypecastGenericArray;", null, l0, l3, 0);
+      mv.visitLocalVariable("m", "Ljava/util/Map;", "Ljava/util/Map<Ljava/lang/String;[[Ljava/lang/String;>;", l1, l3, 1);
+      mv.visitMaxs(2, 2);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+    
+    public static byte[] dumpTypeTest () throws Exception {
+
+      ClassWriter cw = new ClassWriter(false);
+      FieldVisitor fv;
+      MethodVisitor mv;
+      AnnotationVisitor av0;
+      TypeAnnotationVisitor xav0;
+
+      cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/tests/classfile/cases/TestTypeTest", null, "java/lang/Object", null);
+
+      cw.visitSource("TestTypeTest.java", null);
+
+      {
+      fv = cw.visitField(ACC_PUBLIC, "o", "Ljava/lang/Object;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$0", "Ljava/lang/Class;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$1", "Ljava/lang/Class;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$2", "Ljava/lang/Class;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$3", "Ljava/lang/Class;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$4", "Ljava/lang/Class;", null, null);
+      fv.visitEnd();
+      }
+      {
+      fv = cw.visitField(ACC_STATIC + ACC_SYNTHETIC, "class$5", "Ljava/lang/Class;", null, null);
+      fv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(8, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitInsn(RETURN);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l1, 0);
+      mv.visitMaxs(1, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "ismap");
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(4));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(14));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "islist");
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(24));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(12, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/util/Map");
+      Label l1 = new Label();
+      mv.visitJumpInsn(IFEQ, l1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(13, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/util/Set");
+      mv.visitJumpInsn(IFEQ, l1);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(14, l3);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/util/List");
+      mv.visitJumpInsn(IFEQ, l1);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLineNumber(15, l4);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/lang/Object");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitLabel(l1);
+      mv.visitLineNumber(19, l1);
+      mv.visitInsn(RETURN);
+      Label l5 = new Label();
+      mv.visitLabel(l5);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l5, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test2", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(4));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(14));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(22, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/util/List");
+      Label l1 = new Label();
+      mv.visitJumpInsn(IFEQ, l1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(23, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/util/ArrayList");
+      mv.visitJumpInsn(IFEQ, l1);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(24, l3);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/lang/Object");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitLabel(l1);
+      mv.visitLineNumber(27, l1);
+      mv.visitInsn(RETURN);
+      Label l4 = new Label();
+      mv.visitLabel(l4);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l4, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test3", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "instanceof object");
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(4));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(30, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/lang/Object");
+      Label l1 = new Label();
+      mv.visitJumpInsn(IFNE, l1);
+      Label l2 = new Label();
+      mv.visitLabel(l2);
+      mv.visitLineNumber(31, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitTypeInsn(NEW, "java/lang/Object");
+      mv.visitInsn(DUP);
+      mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+      mv.visitFieldInsn(PUTFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitLabel(l1);
+      mv.visitLineNumber(33, l1);
+      mv.visitInsn(RETURN);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l3, 0);
+      mv.visitMaxs(3, 1);
+      mv.visitEnd();
+      }
+      {
+      mv = cw.visitMethod(ACC_PUBLIC, "test4", "()V", null, null);
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(12));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "second");
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(28));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(44));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/B;", true);
+      xav0.visit("value", "fourth");
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(60));
+      xav0.visitEnd();
+      }
+      {
+      xav0 = mv.visitTypeAnnotation("Lannotations/tests/classfile/foo/A;", true);
+      xav0.visitXTargetType(new Integer(2));
+      xav0.visitXOffset(new Integer(76));
+      xav0.visitEnd();
+      }
+      mv.visitCode();
+      Label l0 = new Label();
+      mv.visitLabel(l0);
+      mv.visitLineNumber(36, l0);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;");
+      mv.visitVarInsn(ASTORE, 1);
+      Label l1 = new Label();
+      mv.visitLabel(l1);
+      mv.visitLineNumber(37, l1);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/lang/Boolean");
+      Label l2 = new Label();
+      mv.visitJumpInsn(IFEQ, l2);
+      Label l3 = new Label();
+      mv.visitLabel(l3);
+      mv.visitLineNumber(38, l3);
+      mv.visitLdcInsn(Type.getType("Ljava/lang/Boolean;"));
+      mv.visitVarInsn(ASTORE, 1);
+      Label l4 = new Label();
+      mv.visitJumpInsn(GOTO, l4);
+      mv.visitLabel(l2);
+      mv.visitLineNumber(39, l2);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/lang/Integer");
+      Label l5 = new Label();
+      mv.visitJumpInsn(IFEQ, l5);
+      Label l6 = new Label();
+      mv.visitLabel(l6);
+      mv.visitLineNumber(40, l6);
+      mv.visitLdcInsn(Type.getType("Ljava/lang/Integer;"));
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitJumpInsn(GOTO, l4);
+      mv.visitLabel(l5);
+      mv.visitLineNumber(41, l5);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/lang/Character");
+      Label l7 = new Label();
+      mv.visitJumpInsn(IFEQ, l7);
+      Label l8 = new Label();
+      mv.visitLabel(l8);
+      mv.visitLineNumber(42, l8);
+      mv.visitLdcInsn(Type.getType("Ljava/lang/Character;"));
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitJumpInsn(GOTO, l4);
+      mv.visitLabel(l7);
+      mv.visitLineNumber(43, l7);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/lang/String");
+      Label l9 = new Label();
+      mv.visitJumpInsn(IFEQ, l9);
+      Label l10 = new Label();
+      mv.visitLabel(l10);
+      mv.visitLineNumber(44, l10);
+      mv.visitLdcInsn(Type.getType("Ljava/lang/String;"));
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitJumpInsn(GOTO, l4);
+      mv.visitLabel(l9);
+      mv.visitLineNumber(45, l9);
+      mv.visitVarInsn(ALOAD, 0);
+      mv.visitFieldInsn(GETFIELD, "annotations/tests/classfile/cases/TestTypeTest", "o", "Ljava/lang/Object;");
+      mv.visitTypeInsn(INSTANCEOF, "java/util/List");
+      Label l11 = new Label();
+      mv.visitJumpInsn(IFEQ, l11);
+      Label l12 = new Label();
+      mv.visitLabel(l12);
+      mv.visitLineNumber(46, l12);
+      mv.visitLdcInsn(Type.getType("Ljava/util/List;"));
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitJumpInsn(GOTO, l4);
+      mv.visitLabel(l11);
+      mv.visitLineNumber(48, l11);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
+      mv.visitVarInsn(ASTORE, 1);
+      mv.visitLabel(l4);
+      mv.visitLineNumber(50, l4);
+      mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      mv.visitVarInsn(ALOAD, 1);
+      mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/Object;)V");
+      Label l13 = new Label();
+      mv.visitLabel(l13);
+      mv.visitLineNumber(51, l13);
+      mv.visitInsn(RETURN);
+      Label l14 = new Label();
+      mv.visitLabel(l14);
+      mv.visitLocalVariable("this", "Lannotations/tests/classfile/cases/TestTypeTest;", null, l0, l14, 0);
+      mv.visitLocalVariable("c", "Ljava/lang/Class;", null, l1, l14, 1);
+      mv.visitMaxs(2, 2);
+      mv.visitEnd();
+      }
+      cw.visitEnd();
+
+      return cw.toByteArray();
+      }
+}
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation.class b/asmx/test/conform/annotations/ValueAttrAnnotation.class
new file mode 100644
index 0000000..90795d8
--- /dev/null
+++ b/asmx/test/conform/annotations/ValueAttrAnnotation.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation.java b/asmx/test/conform/annotations/ValueAttrAnnotation.java
new file mode 100644
index 0000000..843cf44
--- /dev/null
+++ b/asmx/test/conform/annotations/ValueAttrAnnotation.java
@@ -0,0 +1,12 @@
+
+package annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ValueAttrAnnotation {
+  String value() default "defaultValue";
+}
+
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation1.class b/asmx/test/conform/annotations/ValueAttrAnnotation1.class
new file mode 100644
index 0000000..dfbd274
--- /dev/null
+++ b/asmx/test/conform/annotations/ValueAttrAnnotation1.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation1.java b/asmx/test/conform/annotations/ValueAttrAnnotation1.java
new file mode 100644
index 0000000..d6dbd81
--- /dev/null
+++ b/asmx/test/conform/annotations/ValueAttrAnnotation1.java
@@ -0,0 +1,12 @@
+
+package annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ValueAttrAnnotation1 {
+  String value();
+}
+
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation2.class b/asmx/test/conform/annotations/ValueAttrAnnotation2.class
new file mode 100644
index 0000000..3c46fd2
--- /dev/null
+++ b/asmx/test/conform/annotations/ValueAttrAnnotation2.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ValueAttrAnnotation2.java b/asmx/test/conform/annotations/ValueAttrAnnotation2.java
new file mode 100644
index 0000000..27bed02
--- /dev/null
+++ b/asmx/test/conform/annotations/ValueAttrAnnotation2.java
@@ -0,0 +1,12 @@
+
+package annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ValueAttrAnnotation2 {
+  String value();
+}
+
diff --git a/asmx/test/conform/annotations/Values.class b/asmx/test/conform/annotations/Values.class
new file mode 100644
index 0000000..f2b5881
--- /dev/null
+++ b/asmx/test/conform/annotations/Values.class
Binary files differ
diff --git a/asmx/test/conform/annotations/Values.java b/asmx/test/conform/annotations/Values.java
new file mode 100644
index 0000000..2c30a30
--- /dev/null
+++ b/asmx/test/conform/annotations/Values.java
@@ -0,0 +1,54 @@
+
+package annotations;
+
+
+@ValuesAnnotation(
+  byteValue = 1,
+  charValue = 'A',
+  booleanValue = true,
+  intValue = 1,
+  shortValue = 1,
+  longValue = 1L,
+  floatValue = 1.0f,
+  doubleValue = 1.0d,
+  stringValue = "A",
+
+  enumValue = ValuesEnum.ONE,
+  annotationValue = @ValueAttrAnnotation( "annotation"),
+  classValue = Values.class,
+
+  byteArrayValue = { 1, -1},
+  charArrayValue = { 'c', 'b', (char)-1},
+  booleanArrayValue = {true, false},
+  intArrayValue = { 1, -1},
+  shortArrayValue = { (short)1, (short)-1},
+  longArrayValue = { 1L, -1L},
+  floatArrayValue = { 1.0f, -1.0f},
+  doubleArrayValue = { 1.0d, -1.0d},
+  stringArrayValue = { "aa", "bb"},
+
+  enumArrayValue = {ValuesEnum.ONE, ValuesEnum.TWO},
+  annotationArrayValue = {@ValueAttrAnnotation( "annotation1"), @ValueAttrAnnotation( "annotation2")},
+  classArrayValue = {Values.class, Values.class}
+)
+@ValueAttrAnnotation1( "classAnnotation1")
+@ValueAttrAnnotation2( "classAnnotation2")
+public class Values {
+
+  @ValueAttrAnnotation1( "fieldAnnotation1")
+  @ValueAttrAnnotation2( "fieldAnnotation2")
+  public String testfield = "test";
+
+  @ValueAttrAnnotation1( "methodAnnotation1")
+  @ValueAttrAnnotation2( "methodAnnotation2")
+  @ValueAttrAnnotation()
+  public void testMethod( 
+      @ValueAttrAnnotation1( "param1Annotation1") 
+      @ValueAttrAnnotation2( "param1Annotation2") String param1, 
+      @ValueAttrAnnotation1( "param2Annotation1") 
+      @ValueAttrAnnotation2( "param2Annotation2") int param2) {
+    // @ValueAttrAnnotation( "codeAnnotation")
+  }
+
+}
+
diff --git a/asmx/test/conform/annotations/ValuesAnnotation.class b/asmx/test/conform/annotations/ValuesAnnotation.class
new file mode 100644
index 0000000..eb35bcb
--- /dev/null
+++ b/asmx/test/conform/annotations/ValuesAnnotation.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ValuesAnnotation.java b/asmx/test/conform/annotations/ValuesAnnotation.java
new file mode 100644
index 0000000..066399d
--- /dev/null
+++ b/asmx/test/conform/annotations/ValuesAnnotation.java
@@ -0,0 +1,42 @@
+
+package annotations;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+
+/**
+ * Annotation declaration with different values
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ValuesAnnotation {
+  byte    byteValue()    default (byte) 255;
+  char    charValue()    default (char) 128;
+  boolean booleanValue() default true;
+  int     intValue()     default 10;
+  short   shortValue()   default (short) 20;
+  long    longValue()    default 100L;
+  float   floatValue()   default 10.0F;
+  double  doubleValue()  default 20.0D;
+  String  stringValue()  default "defaultString";
+
+  Class classValue()     default Values.class;
+  ValuesEnum enumValue() default ValuesEnum.ONE;
+  ValueAttrAnnotation annotationValue() default @ValueAttrAnnotation;
+
+  byte[]    byteArrayValue()    default {(byte) 128, (byte) 129};
+  char[]    charArrayValue()    default { '1', '2'};
+  boolean[] booleanArrayValue() default { true, false};
+  int[]     intArrayValue()     default { 500, 501};
+  short[]   shortArrayValue()   default { (short) 20000, (short) 2001};
+  long[]    longArrayValue()    default { 101L, 102L};
+  float[]   floatArrayValue()   default { 11.0F, 12.0F};
+  double[]  doubleArrayValue()  default { 21.0D, 22.0D};
+  String[]  stringArrayValue()  default { "11", "22"};
+
+  ValuesEnum[] enumArrayValue() default { ValuesEnum.ONE, ValuesEnum.TWO};
+  ValueAttrAnnotation[] annotationArrayValue() default { @ValueAttrAnnotation(), @ValueAttrAnnotation("1")};
+  Class[] classArrayValue() default { Values.class, Values.class};
+
+}
+
diff --git a/asmx/test/conform/annotations/ValuesDump.class b/asmx/test/conform/annotations/ValuesDump.class
new file mode 100644
index 0000000..7245142
--- /dev/null
+++ b/asmx/test/conform/annotations/ValuesDump.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ValuesDump.java b/asmx/test/conform/annotations/ValuesDump.java
new file mode 100644
index 0000000..146a45c
--- /dev/null
+++ b/asmx/test/conform/annotations/ValuesDump.java
@@ -0,0 +1,151 @@
+package annotations;
+import org.objectweb.asm.*;
+public class ValuesDump implements Opcodes {
+
+public static byte[] dump () throws Exception {
+
+ClassWriter cw = new ClassWriter(false);
+FieldVisitor fv;
+MethodVisitor mv;
+AnnotationVisitor av0;
+
+cw.visit(V1_5, ACC_PUBLIC + ACC_SUPER, "annotations/Values", null, "java/lang/Object", null);
+
+cw.visitSource("Values.java", null);
+
+{
+av0 = cw.visitAnnotation("Lannotations/ValuesAnnotation;", true);
+av0.visit("byteValue", new Byte((byte)1));
+av0.visit("charValue", new Character((char)65));
+av0.visit("booleanValue", new Boolean(true));
+av0.visit("intValue", new Integer(1));
+av0.visit("shortValue", new Short((short)1));
+av0.visit("longValue", new Long(1L));
+av0.visit("floatValue", new Float("1.0"));
+av0.visit("doubleValue", new Double("1.0"));
+av0.visit("stringValue", "A");
+av0.visitEnum("enumValue", "Lannotations/ValuesEnum;", "ONE");
+{
+AnnotationVisitor av1 = av0.visitAnnotation("annotationValue", "Lannotations/ValueAttrAnnotation;");
+av1.visit("value", "annotation");
+av1.visitEnd();
+}
+av0.visit("classValue", Type.getType("Lannotations/Values;"));
+av0.visit("byteArrayValue", new byte[] {1,-1});
+av0.visit("charArrayValue", new char[] {(char)99,(char)98,(char)65535});
+av0.visit("booleanArrayValue", new boolean[] {true,false});
+av0.visit("intArrayValue", new int[] {1,-1});
+av0.visit("shortArrayValue", new short[] {(short)1,(short)-1});
+av0.visit("longArrayValue", new long[] {1L,-1L});
+av0.visit("floatArrayValue", new float[] {1.0f,-1.0f});
+av0.visit("doubleArrayValue", new double[] {1.0d,-1.0d});
+{
+AnnotationVisitor av1 = av0.visitArray("stringArrayValue");
+av1.visit(null, "aa");
+av1.visit(null, "bb");
+av1.visitEnd();
+}
+{
+AnnotationVisitor av1 = av0.visitArray("enumArrayValue");
+av1.visitEnum(null, "Lannotations/ValuesEnum;", "ONE");
+av1.visitEnum(null, "Lannotations/ValuesEnum;", "TWO");
+av1.visitEnd();
+}
+{
+AnnotationVisitor av1 = av0.visitArray("annotationArrayValue");
+{
+AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;");
+av2.visit("value", "annotation1");
+av2.visitEnd();
+}
+{
+AnnotationVisitor av2 = av1.visitAnnotation(null, "Lannotations/ValueAttrAnnotation;");
+av2.visit("value", "annotation2");
+av2.visitEnd();
+}
+av1.visitEnd();
+}
+{
+AnnotationVisitor av1 = av0.visitArray("classArrayValue");
+av1.visit(null, Type.getType("Lannotations/Values;"));
+av1.visit(null, Type.getType("Lannotations/Values;"));
+av1.visitEnd();
+}
+av0.visitEnd();
+}
+{
+av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true);
+av0.visit("value", "classAnnotation1");
+av0.visitEnd();
+}
+{
+av0 = cw.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true);
+av0.visit("value", "classAnnotation2");
+av0.visitEnd();
+}
+{
+fv = cw.visitField(ACC_PUBLIC, "testfield", "Ljava/lang/String;", null, null);
+{
+av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true);
+av0.visit("value", "fieldAnnotation1");
+av0.visitEnd();
+}
+{
+av0 = fv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true);
+av0.visit("value", "fieldAnnotation2");
+av0.visitEnd();
+}
+fv.visitEnd();
+}
+{
+mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+mv.visitVarInsn(ALOAD, 0);
+mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+mv.visitVarInsn(ALOAD, 0);
+mv.visitLdcInsn("test");
+mv.visitFieldInsn(PUTFIELD, "annotations/Values", "testfield", "Ljava/lang/String;");
+mv.visitInsn(RETURN);
+mv.visitMaxs(2, 1);
+mv.visitEnd();
+}
+{
+mv = cw.visitMethod(ACC_PUBLIC, "testMethod", "(Ljava/lang/String;I)V", null, null);
+{
+av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation1;", true);
+av0.visit("value", "methodAnnotation1");
+av0.visitEnd();
+}
+{
+av0 = mv.visitAnnotation("Lannotations/ValueAttrAnnotation2;", true);
+av0.visit("value", "methodAnnotation2");
+av0.visitEnd();
+}
+{
+av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation1;", true);
+av0.visit("value", "param1Annotation1");
+av0.visitEnd();
+}
+{
+av0 = mv.visitParameterAnnotation(0, "Lannotations/ValueAttrAnnotation2;", true);
+av0.visit("value", "param1Annotation2");
+av0.visitEnd();
+}
+{
+av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation1;", true);
+av0.visit("value", "param2Annotation1");
+av0.visitEnd();
+}
+{
+av0 = mv.visitParameterAnnotation(1, "Lannotations/ValueAttrAnnotation2;", true);
+av0.visit("value", "param2Annotation2");
+av0.visitEnd();
+}
+mv.visitInsn(RETURN);
+mv.visitMaxs(0, 3);
+mv.visitEnd();
+}
+cw.visitEnd();
+
+return cw.toByteArray();
+}
+}
diff --git a/asmx/test/conform/annotations/ValuesEnum.class b/asmx/test/conform/annotations/ValuesEnum.class
new file mode 100644
index 0000000..358fbd9
--- /dev/null
+++ b/asmx/test/conform/annotations/ValuesEnum.class
Binary files differ
diff --git a/asmx/test/conform/annotations/ValuesEnum.java b/asmx/test/conform/annotations/ValuesEnum.java
new file mode 100644
index 0000000..3d59552
--- /dev/null
+++ b/asmx/test/conform/annotations/ValuesEnum.java
@@ -0,0 +1,7 @@
+
+package annotations;
+
+
+public enum ValuesEnum {
+  ONE, TWO, THREE;
+}
diff --git a/asmx/test/conform/asmifier.xml b/asmx/test/conform/asmifier.xml
new file mode 100644
index 0000000..18157e8
--- /dev/null
+++ b/asmx/test/conform/asmifier.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/ASMifierTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/basicverifier.xml b/asmx/test/conform/basicverifier.xml
new file mode 100644
index 0000000..d122238
--- /dev/null
+++ b/asmx/test/conform/basicverifier.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/BasicVerifierTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/cases/TestClassEmpty.expected b/asmx/test/conform/cases/TestClassEmpty.expected
new file mode 100644
index 0000000..726ee54
--- /dev/null
+++ b/asmx/test/conform/cases/TestClassEmpty.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestClassNonEmpty.expected b/asmx/test/conform/cases/TestClassNonEmpty.expected
new file mode 100644
index 0000000..20f3fda
--- /dev/null
+++ b/asmx/test/conform/cases/TestClassNonEmpty.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestFieldGeneric.expected b/asmx/test/conform/cases/TestFieldGeneric.expected
new file mode 100644
index 0000000..6aa2078
--- /dev/null
+++ b/asmx/test/conform/cases/TestFieldGeneric.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestFieldSimple.expected b/asmx/test/conform/cases/TestFieldSimple.expected
new file mode 100644
index 0000000..4a84ae2
--- /dev/null
+++ b/asmx/test/conform/cases/TestFieldSimple.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestLocalVariable.expected b/asmx/test/conform/cases/TestLocalVariable.expected
new file mode 100644
index 0000000..68b5c35
--- /dev/null
+++ b/asmx/test/conform/cases/TestLocalVariable.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestLocalVariableGenericArray.expected b/asmx/test/conform/cases/TestLocalVariableGenericArray.expected
new file mode 100644
index 0000000..7a4ad30
--- /dev/null
+++ b/asmx/test/conform/cases/TestLocalVariableGenericArray.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestMethodReceiver.expected b/asmx/test/conform/cases/TestMethodReceiver.expected
new file mode 100644
index 0000000..a5ea7b3
--- /dev/null
+++ b/asmx/test/conform/cases/TestMethodReceiver.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestMethodReturnTypeGenericArray.expected b/asmx/test/conform/cases/TestMethodReturnTypeGenericArray.expected
new file mode 100644
index 0000000..02a0b94
--- /dev/null
+++ b/asmx/test/conform/cases/TestMethodReturnTypeGenericArray.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestObjectCreation.expected b/asmx/test/conform/cases/TestObjectCreation.expected
new file mode 100644
index 0000000..ac884b8
--- /dev/null
+++ b/asmx/test/conform/cases/TestObjectCreation.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestObjectCreationGenericArray.expected b/asmx/test/conform/cases/TestObjectCreationGenericArray.expected
new file mode 100644
index 0000000..0346bc6
--- /dev/null
+++ b/asmx/test/conform/cases/TestObjectCreationGenericArray.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestTypeTest.expected b/asmx/test/conform/cases/TestTypeTest.expected
new file mode 100644
index 0000000..bd38146
--- /dev/null
+++ b/asmx/test/conform/cases/TestTypeTest.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestTypecast.expected b/asmx/test/conform/cases/TestTypecast.expected
new file mode 100644
index 0000000..ae93319
--- /dev/null
+++ b/asmx/test/conform/cases/TestTypecast.expected
Binary files differ
diff --git a/asmx/test/conform/cases/TestTypecastGenericArray.expected b/asmx/test/conform/cases/TestTypecastGenericArray.expected
new file mode 100644
index 0000000..c83404e
--- /dev/null
+++ b/asmx/test/conform/cases/TestTypecastGenericArray.expected
Binary files differ
diff --git a/asmx/test/conform/cases/largemethod.class b/asmx/test/conform/cases/largemethod.class
new file mode 100644
index 0000000..c3921e7
--- /dev/null
+++ b/asmx/test/conform/cases/largemethod.class
Binary files differ
diff --git a/asmx/test/conform/cases/unknownattributes.class b/asmx/test/conform/cases/unknownattributes.class
new file mode 100644
index 0000000..30934d1
--- /dev/null
+++ b/asmx/test/conform/cases/unknownattributes.class
Binary files differ
diff --git a/asmx/test/conform/checkclassadapter.xml b/asmx/test/conform/checkclassadapter.xml
new file mode 100644
index 0000000..42b40b2
--- /dev/null
+++ b/asmx/test/conform/checkclassadapter.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/CheckClassAdapterTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/classadapter.xml b/asmx/test/conform/classadapter.xml
new file mode 100644
index 0000000..d9bb0da
--- /dev/null
+++ b/asmx/test/conform/classadapter.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/ClassAdapterTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>
+  </target>
+
+</project>
diff --git a/asmx/test/conform/classnode.xml b/asmx/test/conform/classnode.xml
new file mode 100644
index 0000000..29dc74e
--- /dev/null
+++ b/asmx/test/conform/classnode.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/ClassNodeTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/classreader.xml b/asmx/test/conform/classreader.xml
new file mode 100644
index 0000000..570da9d
--- /dev/null
+++ b/asmx/test/conform/classreader.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/ClassReaderTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/classwriter.xml b/asmx/test/conform/classwriter.xml
new file mode 100644
index 0000000..6c35686
--- /dev/null
+++ b/asmx/test/conform/classwriter.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/ClassWriterTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>
+  </target>
+
+</project>
diff --git a/asmx/test/conform/classwriter2.xml b/asmx/test/conform/classwriter2.xml
new file mode 100644
index 0000000..1d1cf4b
--- /dev/null
+++ b/asmx/test/conform/classwriter2.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/ClassWriterTest2.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>
+  </target>
+
+</project>
diff --git a/asmx/test/conform/dataflow.xml b/asmx/test/conform/dataflow.xml
new file mode 100644
index 0000000..4aed226
--- /dev/null
+++ b/asmx/test/conform/dataflow.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/DataflowTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/gasmifier.xml b/asmx/test/conform/gasmifier.xml
new file mode 100644
index 0000000..aa679c9
--- /dev/null
+++ b/asmx/test/conform/gasmifier.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/GASMifierTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/localvariablessorter.xml b/asmx/test/conform/localvariablessorter.xml
new file mode 100644
index 0000000..0c41f68
--- /dev/null
+++ b/asmx/test/conform/localvariablessorter.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/LocalVariablesSorterTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>
+  </target>
+
+</project>
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest$ClassFilter.class b/asmx/test/conform/org/objectweb/asm/AbstractTest$ClassFilter.class
new file mode 100644
index 0000000..22d133d
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AbstractTest$ClassFilter.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest$FieldFilter.class b/asmx/test/conform/org/objectweb/asm/AbstractTest$FieldFilter.class
new file mode 100644
index 0000000..9022d53
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AbstractTest$FieldFilter.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest$MethodFilter.class b/asmx/test/conform/org/objectweb/asm/AbstractTest$MethodFilter.class
new file mode 100644
index 0000000..613c0ad
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AbstractTest$MethodFilter.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest.class b/asmx/test/conform/org/objectweb/asm/AbstractTest.class
new file mode 100644
index 0000000..233e19a
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AbstractTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AbstractTest.java b/asmx/test/conform/org/objectweb/asm/AbstractTest.java
new file mode 100644
index 0000000..f78acf6
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AbstractTest.java
@@ -0,0 +1,223 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.objectweb.asm.util.TraceClassVisitor;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Super class for test suites based on a jar file.
+ *
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public abstract class AbstractTest extends TestCase {
+
+    protected String n;
+
+    protected InputStream is;
+
+    public AbstractTest() {
+        super("test");
+    }
+
+    protected void init(final String n, final InputStream is) {
+        this.n = n;
+        this.is = is;
+    }
+
+    protected TestSuite getSuite() throws Exception {
+        TestSuite suite = new TestSuite(getClass().getName());
+        String files = System.getProperty("asm.test");
+        String clazz = System.getProperty("asm.test.class");
+        if(files==null) {
+            files = System.getProperty("java.home") + File.separator + "lib" + File.separator + "rt.jar";
+            if(clazz==null) {
+                clazz = "java.lang.";
+            }
+        }
+        files += ",";
+
+        while (files.indexOf(',') != -1) {
+            String file = files.substring(0, files.indexOf(','));
+            files = files.substring(files.indexOf(',') + 1);
+            File f = new File(file);
+            if (f.isDirectory()) {
+                File[] fs = f.listFiles();
+                for (int i = 0; i < fs.length; ++i) {
+                    String n = fs[i].getName();
+                    if (n.endsWith(".class")) {
+                        n = n.substring(0, n.length() - 6).replace('/', '.');
+                        if (clazz == null || n.indexOf(clazz) != -1) {
+                            InputStream is = new FileInputStream(fs[i]);
+                            AbstractTest t = (AbstractTest) getClass().newInstance();
+                            t.init(n, is);
+                            suite.addTest(t);
+                        }
+                    }
+                }
+            } else {
+                ZipFile zip = new ZipFile(file);
+                Enumeration entries = zip.entries();
+                while (entries.hasMoreElements()) {
+                    ZipEntry e = (ZipEntry) entries.nextElement();
+                    String n = e.getName();
+                    if (n.endsWith(".class")) {
+                        n = n.substring(0, n.length() - 6).replace('/', '.');
+                        if (clazz == null || n.indexOf(clazz) != -1) {
+                            InputStream is = zip.getInputStream(e);
+                            AbstractTest t = (AbstractTest) getClass().newInstance();
+                            t.init(n, is);
+                            suite.addTest(t);
+                        }
+                    }
+                }
+            }
+        }
+        return suite;
+    }
+
+    public abstract void test() throws Exception;
+
+    public void assertEquals(final ClassReader cr1, final ClassReader cr2)
+            throws Exception
+    {
+        if (!Arrays.equals(cr1.b, cr2.b)) {
+            StringWriter sw1 = new StringWriter();
+            StringWriter sw2 = new StringWriter();
+            ClassVisitor cv1 = new TraceClassVisitor(new PrintWriter(sw1));
+            ClassVisitor cv2 = new TraceClassVisitor(new PrintWriter(sw2));
+            cr1.accept(new ClassFilter(cv1), false);
+            cr2.accept(new ClassFilter(cv2), false);
+            String s1 = sw1.toString();
+            String s2 = sw2.toString();
+            assertEquals("different data", s1, s2);
+        }
+    }
+
+    public String getName() {
+        return super.getName() + ": " + n;
+    }
+
+    // -------------------------------------------------------------------------
+
+    static class ClassFilter extends ClassAdapter {
+
+        public ClassFilter(final ClassVisitor cv) {
+            super(cv);
+        }
+
+        public void visitAttribute(final Attribute attr) {
+            // remove unknown attributes
+        }
+
+        public FieldVisitor visitField(
+            final int access,
+            final String name,
+            final String desc,
+            final String signature,
+            final Object value)
+        {
+            return new FieldFilter(cv.visitField(access,
+                    name,
+                    desc,
+                    signature,
+                    value));
+        }
+
+        public MethodVisitor visitMethod(
+            final int access,
+            final String name,
+            final String desc,
+            final String signature,
+            final String[] exceptions)
+        {
+            return new MethodFilter(cv.visitMethod(access,
+                    name,
+                    desc,
+                    signature,
+                    exceptions));
+        }
+    }
+
+    static class MethodFilter extends MethodAdapter {
+
+        public MethodFilter(final MethodVisitor mv) {
+            super(mv);
+        }
+
+        public void visitAttribute(final Attribute attr) {
+            // remove unknown attributes
+        }
+    }
+
+    static class FieldFilter implements FieldVisitor {
+
+        FieldVisitor fv;
+
+        public FieldFilter(final FieldVisitor fv) {
+            this.fv = fv;
+        }
+
+        public AnnotationVisitor visitAnnotation(
+            final String desc,
+            final boolean visible)
+        {
+            return fv.visitAnnotation(desc, visible);
+        }
+
+        public TypeAnnotationVisitor visitTypeAnnotation(
+            final String desc,
+            final boolean visible)
+        {
+            return fv.visitTypeAnnotation(desc, visible);
+        }
+
+        public void visitAttribute(final Attribute attr) {
+            // remove unknown attributes
+        }
+
+        public void visitEnd() {
+            fv.visitEnd();
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/AnnotationTest$TestClassLoader.class
new file mode 100644
index 0000000..0b46563
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationTest$TestClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationTest.class b/asmx/test/conform/org/objectweb/asm/AnnotationTest.class
new file mode 100644
index 0000000..c46bdfe
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationTest.java b/asmx/test/conform/org/objectweb/asm/AnnotationTest.java
new file mode 100644
index 0000000..49d9a38
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationTest.java
@@ -0,0 +1,233 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+
+import annotations.ValueAttrAnnotation;
+import annotations.Values;
+import annotations.ValuesAnnotation;
+import annotations.ValuesDump;
+import annotations.ValuesEnum;
+
+import junit.framework.TestCase;
+
+public class AnnotationTest extends TestCase {
+
+    private ValuesAnnotation a;
+
+    public AnnotationTest(String name) {
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+        TestClassLoader cl = new TestClassLoader("annotations.Values",
+                getClass().getClassLoader());
+        Class c = cl.loadClass("annotations.Values");
+        Annotation[] annotations = c.getAnnotations();
+        for (int i = 0; i < annotations.length; ++i) {
+            if (annotations[i] instanceof ValuesAnnotation) {
+                a = (ValuesAnnotation) annotations[i];
+            }
+        }
+        if (a == null) {
+            fail();
+        }
+    }
+
+    public void testByteValue() {
+        assertEquals(1, a.byteValue());
+    }
+
+    public void testCharValue() {
+        assertEquals('A', a.charValue());
+    }
+
+    public void testBooleanValue() {
+        assertEquals(true, a.booleanValue());
+    }
+
+    public void testIntValue() {
+        assertEquals(1, a.intValue());
+    }
+
+    public void testShortValue() {
+        assertEquals(1, a.shortValue());
+    }
+
+    public void testLongValue() {
+        assertEquals(1L, a.longValue());
+    }
+
+    public void testFloatValue() {
+        assertEquals(1.0f, a.floatValue(), 0.1f);
+    }
+
+    public void testDoubleValue() {
+        assertEquals(1.0d, a.doubleValue(), 0.1d);
+    }
+
+    public void testStringValue() {
+        assertEquals("A", a.stringValue());
+    }
+
+    public void testAnnotationValue() {
+        ValueAttrAnnotation ann = a.annotationValue();
+        assertEquals("annotation", ann.value());
+    }
+
+    public void testEnumValue() {
+        ValuesEnum en = a.enumValue();
+        assertEquals(ValuesEnum.ONE, en);
+    }
+
+    public void testClassValue() {
+        Class c = a.classValue();
+        assertEquals(Values.class.getName(), c.getName());
+    }
+
+    public void testByteArrayValue() {
+        byte[] bs = a.byteArrayValue();
+        assertEquals(1, bs[0]);
+        assertEquals(-1, bs[1]);
+    }
+
+    public void testCharArrayValue() {
+        char[] bs = a.charArrayValue();
+        assertEquals('c', bs[0]);
+        assertEquals('b', bs[1]);
+        assertEquals((char) -1, bs[2]);
+    }
+
+    public void testBooleanArrayValue() {
+        boolean[] bs = a.booleanArrayValue();
+        assertEquals(true, bs[0]);
+        assertEquals(false, bs[1]);
+    }
+
+    public void testIntArrayValue() {
+        int[] bs = a.intArrayValue();
+        assertEquals(1, bs[0]);
+        assertEquals(-1, bs[1]);
+    }
+
+    public void testShortArrayValue() {
+        short[] bs = a.shortArrayValue();
+        assertEquals(1, bs[0]);
+        assertEquals(-1, bs[1]);
+    }
+
+    public void testLongArrayValue() {
+        long[] bs = a.longArrayValue();
+        assertEquals(1L, bs[0]);
+        assertEquals(-1L, bs[1]);
+    }
+
+    public void testFloatArrayValue() {
+        float[] bs = a.floatArrayValue();
+        assertEquals(1.0f, bs[0], 0.1f);
+        assertEquals(-1.0f, bs[1], 0.1f);
+    }
+
+    public void testDoubleArrayValue() {
+        double[] bs = a.doubleArrayValue();
+        assertEquals(1.0d, bs[0], 0.1d);
+        assertEquals(-1.0d, bs[1], 0.1d);
+    }
+
+    public void testStringArrayValue() {
+        String[] s = a.stringArrayValue();
+        assertEquals("aa", s[0]);
+        assertEquals("bb", s[1]);
+    }
+
+    public void testAnnotationArrayValue() {
+        ValueAttrAnnotation[] ann = a.annotationArrayValue();
+        assertEquals("annotation1", ann[0].value());
+        assertEquals("annotation2", ann[1].value());
+    }
+
+    public void testEnumArrayValue() {
+        ValuesEnum[] en = a.enumArrayValue();
+        assertEquals(ValuesEnum.ONE, en[0]);
+        assertEquals(ValuesEnum.TWO, en[1]);
+    }
+
+    public void testClassArrayValue() {
+        Class[] c = a.classArrayValue();
+        assertEquals(Values.class.getName(), c[0].getName());
+        assertEquals(Values.class.getName(), c[1].getName());
+    }
+
+    // issue 303711
+    public void testMethodNode() throws Exception {
+        InputStream is = getClass().getResourceAsStream("/"
+                + annotations.Values.class.getName().replace('.', '/')
+                + ".class");
+        ClassReader cr = new ClassReader(is);
+        ClassNode cn = new ClassNode();
+        cr.accept(cn, false);
+    }
+
+    private static final class TestClassLoader extends ClassLoader {
+
+        private final String className;
+
+        private final ClassLoader loader;
+
+        public TestClassLoader(String className, ClassLoader loader) {
+            super();
+            this.className = className;
+            this.loader = loader;
+        }
+
+        public Class loadClass(String name) throws ClassNotFoundException {
+            if (className.equals(name)) {
+                try {
+                    byte[] bytecode = ValuesDump.dump();
+                    return super.defineClass(className,
+                            bytecode,
+                            0,
+                            bytecode.length);
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException("Load error: "
+                            + ex.toString(), ex);
+                }
+            }
+
+            return loader.loadClass(name);
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationMismatchException.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationMismatchException.class
new file mode 100644
index 0000000..36f0df4
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationMismatchException.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationRecorder.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationRecorder.class
new file mode 100644
index 0000000..19ca304
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$AnnotationRecorder.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ClassRecorder.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ClassRecorder.class
new file mode 100644
index 0000000..9ec0502
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ClassRecorder.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ParameterDescription.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ParameterDescription.class
new file mode 100644
index 0000000..d4de753
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier$ParameterDescription.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.class b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.class
new file mode 100644
index 0000000..0a3ab8b
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.java b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.java
new file mode 100644
index 0000000..4703099
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/AnnotationVerifier.java
@@ -0,0 +1,517 @@
+package org.objectweb.asm;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * An <code>AnnotationVerifier</code> provides a way to check to see if two
+ * versions of the same class (from two different <code>.class</code> files),
+ * have the same annotations on the same elements.
+ */
+public class AnnotationVerifier {
+
+  /**
+   * The "correct" version of the class to verify against.
+   */
+  private ClassRecorder originalVisitor;
+
+  /**
+   * The uncertain version of the class to verify.
+   */
+  private ClassRecorder newVisitor;
+
+  /**
+   * Constructs a new <code>AnnotationVerifier</code> that does not yet have
+   * any information about the class.
+   */
+  public AnnotationVerifier() {
+    originalVisitor = new ClassRecorder();
+    newVisitor = new ClassRecorder();
+  }
+
+  /**
+   * Returns the <code>ClassVisitor</code> which should be made to visit the
+   * version of the class known to be correct.
+   *
+   * @return a visitor for the good version of the class
+   */
+  public ClassVisitor originalVisitor() {
+    return originalVisitor;
+  }
+
+  /**
+   * Returns the <code>ClassVisitor</code> which should be made to visit the
+   * version of the class being tested.
+   *
+   * @return a visitor the the experimental version of the class
+   */
+  public ClassVisitor newVisitor() {
+    return newVisitor;
+  }
+
+  /**
+   * Verifies that the visitors returned by {@link #originalVisitor()} and
+   * {@link #newVisitor()} have visited the same class.  This method can only
+   * be called if both visitors have already visited a class.
+   *
+   * @throws AnnotationMismatchException if the two visitors have not visited
+   * two versions of the same class that contain idential annotations.
+   */
+  public void verify() {
+    if(!newVisitor.name.equals(originalVisitor.name)) {
+      throw new AnnotationMismatchException(
+          "Cannot verify two different classes " +
+          newVisitor.name + " cannot be verified against " +
+          originalVisitor.name);
+    }
+    newVisitor.verifyAgainst(originalVisitor);
+  }
+
+  /**
+   * A ClassRecorder records all the annotations that it visits, and serves
+   * as a ClassVisitor, FieldVisitor and MethodVisitor.
+   */
+  private class ClassRecorder extends EmptyVisitor {
+
+    private String description;
+
+    public String name;
+    private String signature;
+
+    private Map<String, ClassRecorder> fieldRecorders;
+    // key is unparameterized name
+
+    private Map<String, ClassRecorder> methodRecorders;
+    // key is complete method signature
+
+    // general annotations
+    private Map<String, AnnotationRecorder> anns;
+    private Map<String, AnnotationRecorder> xanns;
+
+    //method specific annotations
+    private Set<AnnotationRecorder> danns;
+    private Map<ParameterDescription, AnnotationRecorder> panns;
+
+    public ClassRecorder() {
+      this("[class: ","","");
+    }
+
+    public ClassRecorder(String internalDescription, String name, String signature) {
+      this.description = internalDescription;
+      this.name = name;
+      this.signature = signature;
+
+      fieldRecorders = new HashMap<String, ClassRecorder>();
+      methodRecorders = new HashMap<String, ClassRecorder>();
+
+      anns = new HashMap<String, AnnotationRecorder>();
+      xanns = new HashMap<String, AnnotationRecorder>();
+
+      danns = new HashSet<AnnotationRecorder>();
+      panns = new HashMap<ParameterDescription, AnnotationRecorder>();
+    }
+
+    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+      this.name = name;
+      this.signature = signature;
+      description = description + name;
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      AnnotationRecorder av = new AnnotationRecorder(
+          description + " annotation: " + desc);
+      anns.put(desc,av);
+      return av;
+    }
+
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible) {
+      AnnotationRecorder av = new AnnotationRecorder(
+          description + " annotation: " + desc);
+      xanns.put(desc,av);
+      return av;
+    }
+
+    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+      ClassRecorder fr =
+        new ClassRecorder(
+            description + " field: " + name,
+            name, signature);
+      fieldRecorders.put(name, fr);
+      return fr;
+    }
+
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+      ClassRecorder mr =
+        new ClassRecorder(
+            description + " method: " + name + desc, name+desc, signature);
+      methodRecorders.put(name+desc, mr);
+      return mr;
+    }
+
+    // MethodVisitor methods:
+    public AnnotationVisitor visitAnnotationDefault() {
+      AnnotationRecorder dr = new AnnotationRecorder(
+          description + " default annotation");
+      danns.add(dr);
+      return dr;
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+      ParameterDescription pd =
+        new ParameterDescription(parameter, desc, visible);
+      AnnotationRecorder pr =
+        new AnnotationRecorder(description +
+            " parameter annotation: " + pd);
+      panns.put(pd, pr);
+      return pr;
+    }
+
+    public void verifyAgainst(ClassRecorder correct) {
+      // first, ensure all annotations are correct
+      verifyAnns(this.anns, correct.anns);
+      verifyAnns(this.xanns, correct.xanns);
+
+      // then recurse into any annotations on fields/methods
+      verifyMemberAnns(this.fieldRecorders, correct.fieldRecorders);
+      verifyMemberAnns(this.methodRecorders, correct.methodRecorders);
+    }
+
+    private void verifyAnns(
+        Map<String, AnnotationRecorder> questionableAnns,
+        Map<String, AnnotationRecorder> correctAnns) {
+      Set<AnnotationRecorder> unresolvedQuestionableAnns =
+        new HashSet<AnnotationRecorder>(questionableAnns.values());
+
+      for(Map.Entry<String, AnnotationRecorder> entry :
+        correctAnns.entrySet()) {
+        String name = entry.getKey();
+        AnnotationRecorder correctAnn = entry.getValue();
+        AnnotationRecorder questionableAnn = questionableAnns.get(name);
+        if(questionableAnn == null) {
+          throw new AnnotationMismatchException(description +
+              " does not contain expected annotation: " + correctAnn);
+        }
+
+        questionableAnn.verifyAgainst(correctAnn);
+
+        unresolvedQuestionableAnns.remove(questionableAnn);
+      }
+
+      for(AnnotationRecorder unexpectedAnnOnThis : unresolvedQuestionableAnns) {
+        throw new AnnotationMismatchException(description +
+            " contains unexpected annotation : " + unexpectedAnnOnThis);
+      }
+    }
+
+    private void verifyMemberAnns(
+        Map<String, ClassRecorder> questionableMembers,
+        Map<String, ClassRecorder> correctMembers) {
+      Set<ClassRecorder> unresolvedQuestionableMembers =
+        new HashSet<ClassRecorder>(questionableMembers.values());
+
+      for(Map.Entry<String, ClassRecorder> entry :
+        correctMembers.entrySet()) {
+        String name = entry.getKey();
+        ClassRecorder correctMember = entry.getValue();
+        ClassRecorder questionableMember = questionableMembers.get(name);
+        if(questionableMember == null) {
+          throw new AnnotationMismatchException(description +
+              " does not contain expected member: " + correctMember);
+        }
+
+        questionableMember.verifyAgainst(correctMember);
+
+        unresolvedQuestionableMembers.remove(questionableMember);
+      }
+
+      for(ClassRecorder unexpectedMemberOnThis : unresolvedQuestionableMembers) {
+        System.out.println("Going to throw exception: ");
+        System.out.println("questionable: " + questionableMembers);
+        System.out.println("correct: " + correctMembers);
+
+        throw new AnnotationMismatchException(description +
+            " contains unexpected member: " + unexpectedMemberOnThis);
+      }
+    }
+
+    public String toString() {
+      return description;
+    }
+  }
+
+  /**
+   * An AnnotationRecorder is an TypeAnnotationVisitor that records all the
+   * information it visits.
+   */
+  private class AnnotationRecorder implements TypeAnnotationVisitor {
+    private String description;
+
+    private List<String> fieldArgs1;
+    private List<Object> fieldArgs2;
+
+    private List<String> enumArgs1;
+    private List<String> enumArgs2;
+    private List<String> enumArgs3;
+
+    private List<String> innerAnnotationArgs1;
+    private List<String> innerAnnotationArgs2;
+    private Map<String, AnnotationRecorder> innerAnnotationMap;
+
+    private List<String> arrayArgs;
+    private Map<String, AnnotationRecorder> arrayMap;
+
+    private List<Integer> xIndexArgs;
+    private List<Integer> xLengthArgs;
+    private List<Integer> xLocationArgs;
+    private List<Integer> xLocationLengthArgs;
+    private List<Integer> xOffsetArgs;
+    private List<Integer> xStartPcArgs;
+    private List<Integer> xTargetTypeArgs;
+    private List<Integer> xParamIndexArgs;
+    private List<Integer> xBoundIndexArgs;
+    private List<Integer> xTypeIndexArgs;
+
+    public AnnotationRecorder(String description) {
+      this.description = description;
+      fieldArgs1 = new ArrayList<String>();
+      fieldArgs2 = new ArrayList<Object>();
+
+      enumArgs1 = new ArrayList<String>();
+      enumArgs2 = new ArrayList<String>();
+      enumArgs3 = new ArrayList<String>();
+
+      innerAnnotationArgs1 = new ArrayList<String>();
+      innerAnnotationArgs2 = new ArrayList<String>();
+      innerAnnotationMap = new HashMap<String, AnnotationRecorder>();
+
+      arrayArgs = new ArrayList<String>();
+      arrayMap = new HashMap<String, AnnotationRecorder>();
+
+      xIndexArgs = new ArrayList<Integer>();
+      xLengthArgs = new ArrayList<Integer>();
+      xLocationArgs = new ArrayList<Integer>();
+      xLocationLengthArgs = new ArrayList<Integer>();
+      xOffsetArgs = new ArrayList<Integer>();
+      xStartPcArgs = new ArrayList<Integer>();
+      xTargetTypeArgs = new ArrayList<Integer>();
+      xParamIndexArgs = new ArrayList<Integer>();
+      xBoundIndexArgs = new ArrayList<Integer>();
+      xTypeIndexArgs = new ArrayList<Integer>();
+    }
+
+    public void visitXIndex(int index) {
+      xIndexArgs.add(index);
+    }
+
+    public void visitXLength(int length) {
+      xLengthArgs.add(length);
+    }
+
+    public void visitXLocation(int location) {
+      xLocationArgs.add(location);
+    }
+
+    public void visitXLocationLength(int location_length) {
+     xLocationLengthArgs.add(location_length);
+    }
+
+    public void visitXOffset(int offset) {
+      xOffsetArgs.add(offset);
+    }
+
+    @Override
+    public void visitXNumEntries(int num_entries) { }
+
+    public void visitXStartPc(int start_pc) {
+      xStartPcArgs.add(start_pc);
+    }
+
+    public void visitXTargetType(int target_type) {
+      xTargetTypeArgs.add(target_type);
+    }
+
+    public void visitXParamIndex(int param_index) {
+      xParamIndexArgs.add(param_index);
+    }
+
+    public void visitXBoundIndex(int bound_index) {
+      xBoundIndexArgs.add(bound_index);
+    }
+
+    public void visitXTypeIndex(int type_index) {
+      xTypeIndexArgs.add(type_index);
+    }
+
+    public void visit(String name, Object value) {
+      fieldArgs1.add(name);
+      fieldArgs2.add(value);
+    }
+
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+      innerAnnotationArgs1.add(name);
+      innerAnnotationArgs2.add(name);
+
+      AnnotationRecorder av= new AnnotationRecorder(description + name);
+      innerAnnotationMap.put(name, av);
+      return av;
+    }
+
+    public AnnotationVisitor visitArray(String name) {
+      arrayArgs.add(name);
+      AnnotationRecorder av = new AnnotationRecorder(description + name);
+      arrayMap.put(name, av);
+      return av;
+    }
+
+    public void visitEnd() {
+    }
+
+    public void visitEnum(String name, String desc, String value) {
+      enumArgs1.add(name);
+      enumArgs2.add(desc);
+      enumArgs3.add(value);
+    }
+
+    public String toString() {
+      return description;
+    }
+
+    /**
+     * Checks that the information passed into this matches the information
+     * passed into another AnnotationRecorder.  For right now, the order in
+     * which information is passed in does matter.  If there is a conflict in
+     * information, an exception will be thrown.
+     *
+     * @param ar an annotation recorder that has visited the correct information
+     *  this should visit
+     * @throws AnnotationMismatchException if the information visited by this
+     *  does not match the information in ar
+     */
+    public void verifyAgainst(AnnotationRecorder ar) {
+      StringBuilder sb = new StringBuilder();
+      verifyList(sb, "visit()", 1, this.fieldArgs1, ar.fieldArgs1);
+      verifyList(sb, "visit()", 2, this.fieldArgs2, ar.fieldArgs2);
+
+      verifyList(sb, "visitEnum()", 1, this.enumArgs1, ar.enumArgs1);
+      verifyList(sb, "visitEnum()", 2, this.enumArgs2, ar.enumArgs2);
+      verifyList(sb, "visitEnum()", 3, this.enumArgs3, ar.enumArgs3);
+
+      verifyList(sb, "visitAnnotation()", 1, this.innerAnnotationArgs1, ar.innerAnnotationArgs1);
+      verifyList(sb, "visitAnnotation()", 2, this.innerAnnotationArgs2, ar.innerAnnotationArgs2);
+
+      verifyList(sb, "visitArray()", 1, this.arrayArgs, ar.arrayArgs);
+
+      verifyList(sb, "visitXIndexArgs()", 1, this.xIndexArgs, ar.xIndexArgs);
+      verifyList(sb, "visitXLength()", 1, this.xLengthArgs, ar.xIndexArgs);
+      verifyList(sb, "visitXLocation()", 1, this.xLocationArgs, ar.xLocationArgs);
+      verifyList(sb, "visitXLocationLength()", 1, this.xLocationLengthArgs, ar.xLocationLengthArgs);
+      verifyList(sb, "visitXOffset()", 1, this.xOffsetArgs, ar.xOffsetArgs);
+      verifyList(sb, "visitXStartPc()", 1, this.xStartPcArgs, ar.xStartPcArgs);
+      verifyList(sb, "visitXTargetType()", 1, this.xTargetTypeArgs, ar.xTargetTypeArgs);
+      verifyList(sb, "visitXParamIndex()", 1, this.xParamIndexArgs, ar.xParamIndexArgs);
+      verifyList(sb, "visitXBoundIndex()", 1, this.xBoundIndexArgs, ar.xBoundIndexArgs);
+      verifyList(sb, "visitXTypeIndex()", 1, this.xTypeIndexArgs, ar.xTypeIndexArgs);
+
+      verifyInnerAnnotationRecorder(sb, this.innerAnnotationMap, ar.innerAnnotationMap);
+      verifyInnerAnnotationRecorder(sb, this.arrayMap, ar.arrayMap);
+
+      if(sb.length() > 0) {
+        throw new AnnotationMismatchException(sb.toString());
+      }
+    }
+
+    private void verifyList(
+        StringBuilder sb,
+        String methodName,
+        int parameter,
+        List questionable,
+        List correct) {
+      if(!questionable.equals(correct)) {
+        String s = "\n" + description +
+        " was called with unexpected information in parameter: " + parameter +
+        "\nReceived: " + questionable  +
+        "\nExpected: " + correct;
+      }
+    }
+
+    private void verifyInnerAnnotationRecorder(
+        StringBuilder sb,
+        Map<String, AnnotationRecorder> questionableAR,
+        Map<String, AnnotationRecorder> correctAR) {
+      // checks on arguments passed in to the methods that created these
+      // AnnotationRecorders (i.e. the checks on the String keys on these maps)
+      // ensure that these have identical keys
+      for(Map.Entry<String, AnnotationRecorder> questionableEntry :
+        questionableAR.entrySet()) {
+        questionableEntry.getValue().verifyAgainst(
+            correctAR.get(questionableEntry.getClass()));
+      }
+    }
+
+  }
+
+  /**
+   * A ParameterDescription is a convenient class used to keep information about
+   * method parameters.  Parameters are equal if they have the same index,
+   * regardless of their description.
+   */
+  private class ParameterDescription {
+    public final int parameter;
+    public final String desc;
+    public final boolean visible;
+
+    public ParameterDescription(int parameter, String desc, boolean visible) {
+      this.parameter = parameter;
+      this.desc = desc;
+      this.visible = visible;
+    }
+
+    public boolean equals(Object o) {
+      if(o instanceof ParameterDescription) {
+        ParameterDescription p = (ParameterDescription) o;
+        return this.parameter == p.parameter;
+      }
+      return false;
+    }
+
+    public int hashCode() {
+      return parameter * 17;
+    }
+
+    public String toString() {
+      return
+        "parameter index: " + parameter +
+        " desc: " + desc +
+        " visible: " + visible;
+    }
+  }
+
+  /**
+   * An AnnotationMismatchException is an Exception that indicates that
+   * two versions of the same class do not have the same annotations on
+   * either the class, its field, or its methods.
+   */
+  public class AnnotationMismatchException extends RuntimeException {
+    private static final long serialVersionUID = 20060714L; // today's date
+
+    /**
+     * Constructs a new AnnotationMismatchException with the given error message.
+     *
+     * @param msg the error as to why the annotations do not match.
+     */
+    public AnnotationMismatchException(String msg) {
+      super(msg);
+    }
+  }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassAdapterTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest$TestClassLoader.class
new file mode 100644
index 0000000..23d2089
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest$TestClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.class b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.class
new file mode 100644
index 0000000..1b10f6a
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.java b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.java
new file mode 100644
index 0000000..74c7743
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassAdapterTest.java
@@ -0,0 +1,68 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import junit.framework.TestSuite;
+
+/**
+ * ClassAdapter tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassAdapterTest extends AbstractTest {
+
+    private final static TestClassLoader LOADER = new TestClassLoader();
+
+    public static TestSuite suite() throws Exception {
+        return new ClassAdapterTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(true, true);
+        cr.accept(new ClassAdapter(cw), false);
+        byte[] b = cw.toByteArray();
+        try {
+            LOADER.defineClass(n, b);
+        } catch (ClassFormatError cfe) {
+            fail(cfe.getMessage());
+        } catch (Throwable ignored) {
+        }
+    }
+
+    // ------------------------------------------------------------------------
+
+    static class TestClassLoader extends ClassLoader {
+
+        public Class defineClass(final String name, final byte[] b) {
+            return defineClass(name, b, 0, b.length);
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassReaderTest.class b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.class
new file mode 100644
index 0000000..ad5bb5a
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassReaderTest.java b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.java
new file mode 100644
index 0000000..aad1800
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassReaderTest.java
@@ -0,0 +1,50 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.commons.EmptyVisitor;
+
+/**
+ * ClassReader tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassReaderTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new ClassReaderTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        new ClassReader(is).accept(new EmptyVisitor(), false);
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest.class b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.class
new file mode 100644
index 0000000..3c21eef
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest.java b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.java
new file mode 100644
index 0000000..be0a27a
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest.java
@@ -0,0 +1,51 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import junit.framework.TestSuite;
+
+/**
+ * ClassWriter tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassWriterTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new ClassWriterTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(false, true);
+        cr.accept(cw, false);
+        assertEquals(cr, new ClassReader(cw.toByteArray()));
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.class b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.class
new file mode 100644
index 0000000..6154eb0
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.java b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.java
new file mode 100644
index 0000000..d83a2f3
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ClassWriterTest2.java
@@ -0,0 +1,51 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import junit.framework.TestSuite;
+
+/**
+ * ClassWriter tests for copyPool() optimization.
+ * 
+ * @author Eugene Kuleshov
+ */
+public class ClassWriterTest2 extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new ClassWriterTest2().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(cr, false);
+        cr.accept(cw, false);
+        assertEquals(cr, new ClassReader(cw.toByteArray()));
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest$TestClassLoader.class
new file mode 100644
index 0000000..3202177
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest$TestClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.class b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.class
new file mode 100644
index 0000000..73d5be1
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.java b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.java
new file mode 100644
index 0000000..df90694
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/ExtendedAnnotationTest.java
@@ -0,0 +1,399 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.lang.annotation.Annotation;
+import java.net.URL;
+import java.security.CodeSource;
+import java.security.ProtectionDomain;
+
+import junit.framework.TestCase;
+
+import org.objectweb.asm.tree.ClassNode;
+
+import annotations.ExtendedValuesDump;
+import annotations.ValueAttrAnnotation;
+import annotations.Values;
+import annotations.ValuesAnnotation;
+import annotations.ValuesEnum;
+
+public class TypeAnnotationTest extends TestCase {
+
+    private ValuesAnnotation a;
+
+    public TypeAnnotationTest(String name) {
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+        TestClassLoader cl = new TestClassLoader("annotations.Values",
+                getClass().getClassLoader());
+        Class c = cl.loadClass("annotations.Values");
+        Annotation[] annotations = c.getAnnotations();
+        for (int i = 0; i < annotations.length; ++i) {
+            if (annotations[i] instanceof ValuesAnnotation) {
+                a = (ValuesAnnotation) annotations[i];
+            }
+        }
+        if (a == null) {
+            fail();
+        }
+    }
+
+    public void testByteValue() {
+        assertEquals(1, a.byteValue());
+    }
+
+    public void testCharValue() {
+        assertEquals('A', a.charValue());
+    }
+
+    public void testBooleanValue() {
+        assertEquals(true, a.booleanValue());
+    }
+
+    public void testIntValue() {
+        assertEquals(1, a.intValue());
+    }
+
+    public void testShortValue() {
+        assertEquals(1, a.shortValue());
+    }
+
+    public void testLongValue() {
+        assertEquals(1L, a.longValue());
+    }
+
+    public void testFloatValue() {
+        assertEquals(1.0f, a.floatValue(), 0.1f);
+    }
+
+    public void testDoubleValue() {
+        assertEquals(1.0d, a.doubleValue(), 0.1d);
+    }
+
+    public void testStringValue() {
+        assertEquals("A", a.stringValue());
+    }
+
+    public void testAnnotationValue() {
+        ValueAttrAnnotation ann = a.annotationValue();
+        assertEquals("annotation", ann.value());
+    }
+
+    public void testEnumValue() {
+        ValuesEnum en = a.enumValue();
+        assertEquals(ValuesEnum.ONE, en);
+    }
+
+    public void testClassValue() {
+        Class c = a.classValue();
+        assertEquals(Values.class.getName(), c.getName());
+    }
+
+    public void testByteArrayValue() {
+        byte[] bs = a.byteArrayValue();
+        assertEquals(1, bs[0]);
+        assertEquals(-1, bs[1]);
+    }
+
+    public void testCharArrayValue() {
+        char[] bs = a.charArrayValue();
+        assertEquals('c', bs[0]);
+        assertEquals('b', bs[1]);
+        assertEquals((char) -1, bs[2]);
+    }
+
+    public void testBooleanArrayValue() {
+        boolean[] bs = a.booleanArrayValue();
+        assertEquals(true, bs[0]);
+        assertEquals(false, bs[1]);
+    }
+
+    public void testIntArrayValue() {
+        int[] bs = a.intArrayValue();
+        assertEquals(1, bs[0]);
+        assertEquals(-1, bs[1]);
+    }
+
+    public void testShortArrayValue() {
+        short[] bs = a.shortArrayValue();
+        assertEquals(1, bs[0]);
+        assertEquals(-1, bs[1]);
+    }
+
+    public void testLongArrayValue() {
+        long[] bs = a.longArrayValue();
+        assertEquals(1L, bs[0]);
+        assertEquals(-1L, bs[1]);
+    }
+
+    public void testFloatArrayValue() {
+        float[] bs = a.floatArrayValue();
+        assertEquals(1.0f, bs[0], 0.1f);
+        assertEquals(-1.0f, bs[1], 0.1f);
+    }
+
+    public void testDoubleArrayValue() {
+        double[] bs = a.doubleArrayValue();
+        assertEquals(1.0d, bs[0], 0.1d);
+        assertEquals(-1.0d, bs[1], 0.1d);
+    }
+
+    public void testStringArrayValue() {
+        String[] s = a.stringArrayValue();
+        assertEquals("aa", s[0]);
+        assertEquals("bb", s[1]);
+    }
+
+    public void testAnnotationArrayValue() {
+        ValueAttrAnnotation[] ann = a.annotationArrayValue();
+        assertEquals("annotation1", ann[0].value());
+        assertEquals("annotation2", ann[1].value());
+    }
+
+    public void testEnumArrayValue() {
+        ValuesEnum[] en = a.enumArrayValue();
+        assertEquals(ValuesEnum.ONE, en[0]);
+        assertEquals(ValuesEnum.TWO, en[1]);
+    }
+
+    public void testClassArrayValue() {
+        Class[] c = a.classArrayValue();
+        assertEquals(Values.class.getName(), c[0].getName());
+        assertEquals(Values.class.getName(), c[1].getName());
+    }
+
+    // issue 303711
+    public void testMethodNode() throws Exception {
+        InputStream is = getClass().getResourceAsStream("/"
+                + annotations.Values.class.getName().replace('.', '/')
+                + ".class");
+        ClassReader cr = new ClassReader(is);
+        ClassNode cn = new ClassNode();
+        cr.accept(cn, false);
+    }
+
+    // TODO: figure out a more general way of finding a file given the
+    // different ways of executing something in Eclipse, ant, and sub ant
+    public String nameExpected(String className) {
+      Class cls = this.getClass();
+      ProtectionDomain pr = cls.getProtectionDomain();
+      CodeSource cs = pr.getCodeSource();
+      URL loc = cs.getLocation();
+      String s = loc.toString();
+      if(s.startsWith("file:")) {
+        s = s.substring(5);
+      }
+      // in the default asmx ant script, class will be run from something like
+      // asmx/output/... so just cut things off at asmx
+      s = s.substring(0, s.lastIndexOf("asmx")+4);
+      return s+"/test/conform/cases/"+className+".expected";
+    }
+
+    /**
+     * Runs a test on the given class by loading up its expected file and 
+     * ensuring that they match the given bytecodes.
+     * 
+     * @param className the name of the class to test
+     * @param bytecodes the bytecodes that are to be tested to be sure they
+     * match the arrays in the given class.
+     */
+    public void testRunner(String className, byte[] bytecodes) throws Exception {
+      InputStream isExpected = new FileInputStream(nameExpected(className));
+
+      ClassReader crCorrect = new ClassReader(isExpected);
+      ClassReader crGenerated = new ClassReader(bytecodes);
+
+      AnnotationVerifier av = new AnnotationVerifier();
+
+      crCorrect.accept(av.originalVisitor(), false);
+      crGenerated.accept(av.newVisitor(), false);
+      try {
+         av.verify();
+      } catch(Exception e) {
+        fail("AnnotationMismatch: " + e.toString());
+      }
+    }
+
+    /**
+     * Runs an extended annotation test on TestClassEmpty.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testClassEmpty() throws Exception {
+      testRunner("TestClassEmpty", ExtendedValuesDump.dumpClassEmpty());
+    }
+
+    /**
+     * Runs an extended annotation test on TestClassNonEmpty.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testClassNonEmpty() throws Exception {
+      testRunner("TestClassNonEmpty", ExtendedValuesDump.dumpClassNonEmpty());
+    }
+
+    /**
+     * Runs an extended annotation test on TestFieldSimple.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testFieldSimple() throws Exception {
+      testRunner("TestFieldSimple", ExtendedValuesDump.dumpFieldSimple());
+    }
+
+    /**
+     * Runs an extended annotation test on TestFieldGeneric.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testFieldGeneric() throws Exception {
+      testRunner("TestFieldGeneric", ExtendedValuesDump.dumpFieldGeneric());
+    }
+
+    /**
+     * Runs an extended annotation test on TestLocalVariable.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testLocalVariable() throws Exception {
+      testRunner("TestLocalVariable", ExtendedValuesDump.dumpLocalVariable());
+    }
+
+    /**
+     * Runs an extended annotation test on TestLocalVariableGenericArray.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testLocalVariableGenericArray() throws Exception {
+      testRunner("TestLocalVariableGenericArray", 
+          ExtendedValuesDump.dumpLocalVariableGenericArray());
+    }
+
+    /**
+     * Runs an extended annotation test on TestMethodReceiver.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testMethodReceiver() throws Exception {
+      testRunner("TestMethodReceiver", ExtendedValuesDump.dumpMethodReceiver());
+    }
+
+    /**
+     * Runs an extended annotation test on TestMethodReturnTypeGenericArray.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testMethodReturnTypeGenericArray() throws Exception {
+      testRunner("TestMethodReturnTypeGenericArray", 
+          ExtendedValuesDump.dumpMethodReturnTypeGenericArray());
+    }
+
+    /**
+     * Runs an extended annotation test on TestObjectCreation.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testObjectCreation() throws Exception {
+      testRunner("TestObjectCreation", ExtendedValuesDump.dumpObjectCreation());
+    }
+
+    /**
+     * Runs an extended annotation test on TestObjectCreationGenericArray.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testObjectCreationGenericArray() throws Exception {
+      testRunner("TestObjectCreationGenericArray", 
+          ExtendedValuesDump.dumpObjectCreationGenericArray());
+    }
+
+    /**
+     * Runs an extended annotation test on TestTypecast.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testTypecast() throws Exception {
+      testRunner("TestTypecast", ExtendedValuesDump.dumpTypecast());
+    }
+
+    /**
+     * Runs an extended annotation test on TestTypecastGenericArray.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testTypecastGenericArray() throws Exception {
+      testRunner("TestTypecastGenericArray", 
+          ExtendedValuesDump.dumpTypecastGenericArray());
+    }
+
+    /**
+     * Runs an extended annotation test on TestTypeTest.
+     * 
+     * @throws Exception if unexpected error occurs
+     */
+    public void testTypeTest() throws Exception {
+      testRunner("TestTypeTest", ExtendedValuesDump.dumpTypeTest());
+    }
+
+    private static final class TestClassLoader extends ClassLoader {
+
+        private final String className;
+
+        private final ClassLoader loader;
+
+        public TestClassLoader(String className, ClassLoader loader) {
+            super();
+            this.className = className;
+            this.loader = loader;
+        }
+
+        public Class loadClass(String name) throws ClassNotFoundException {
+            if (className.equals(name)) {
+                try {
+                    byte[] bytecode = ExtendedValuesDump.dump();
+                    return super.defineClass(className,
+                            bytecode,
+                            0,
+                            bytecode.length);
+                } catch (Exception ex) {
+                    ex.printStackTrace();
+                    throw new ClassNotFoundException("Load error: "
+                            + ex.toString(), ex);
+                }
+            }
+
+            return loader.loadClass(name);
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/UnitTest.class b/asmx/test/conform/org/objectweb/asm/UnitTest.class
new file mode 100644
index 0000000..b801d1d
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/UnitTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/UnitTest.java b/asmx/test/conform/org/objectweb/asm/UnitTest.java
new file mode 100644
index 0000000..fa621da
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/UnitTest.java
@@ -0,0 +1,71 @@
+package org.objectweb.asm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class UnitTest extends TestCase {
+
+    public void testReader() throws IOException {
+        try {
+            new ClassReader((InputStream) null);
+            fail();
+        } catch (IOException e) {
+        }
+
+        ClassReader cr = new ClassReader(getClass().getName());
+        int item = cr.getItem(1);
+        assertTrue(item >= 10);
+        assertTrue(item < cr.header);
+    }
+
+    public void testWriter() {
+        ClassWriter cw = new ClassWriter(false);
+        cw.newConst(new Byte((byte) 0));
+        cw.newConst(new Character('0'));
+        cw.newConst(new Short((short) 0));
+        cw.newConst(new Boolean(false));
+        try {
+            cw.newConst(new Object());
+            fail();
+        } catch (RuntimeException e) {
+        }
+        cw.newMethod("A", "m", "()V", false);
+    }
+    
+    public void testLabel() {
+        new Label().toString();
+        try {
+            new Label().getOffset();
+            fail();
+        } catch (RuntimeException e) {
+        }
+    }
+    
+    public void testType() {
+        assertEquals(Type.getType(Integer.TYPE), Type.INT_TYPE);
+        assertEquals(Type.getType(Void.TYPE), Type.VOID_TYPE);
+        assertEquals(Type.getType(Boolean.TYPE), Type.BOOLEAN_TYPE);
+        assertEquals(Type.getType(Byte.TYPE), Type.BYTE_TYPE);
+        assertEquals(Type.getType(Character.TYPE), Type.CHAR_TYPE);
+        assertEquals(Type.getType(Short.TYPE), Type.SHORT_TYPE);
+        assertEquals(Type.getType(Double.TYPE), Type.DOUBLE_TYPE);
+        assertEquals(Type.getType(Float.TYPE), Type.FLOAT_TYPE);
+        assertEquals(Type.getType(Long.TYPE), Type.LONG_TYPE);
+        String s1 = Type.getType(UnitTest.class).getInternalName();
+        String s2 = Type.getInternalName(UnitTest.class);
+        assertEquals(s1, s2);
+        for (int i = 0; i < Arrays.class.getMethods().length; ++i) {
+            Method m = Arrays.class.getMethods()[i];
+            Type[] args = Type.getArgumentTypes(m);
+            Type r = Type.getReturnType(m);
+            String d1 = Type.getMethodDescriptor(r, args);
+            String d2 = Type.getMethodDescriptor(m);
+            assertEquals(d1, d2);
+        }
+        Type.INT_TYPE.hashCode();
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.class b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.class
new file mode 100644
index 0000000..28172cc
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.java b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.java
new file mode 100644
index 0000000..6bc03c8
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableAttributeTest.java
@@ -0,0 +1,79 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.attrs;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.util.TraceClassVisitor;
+import org.objectweb.asm.util.attrs.ASMStackMapTableAttribute;
+
+/**
+ * StackMapTableAttributeTest
+ * 
+ * @author Eugene Kuleshov
+ */
+public class StackMapTableAttributeTest extends AbstractTest {
+
+    public static final String TEST_CLASS = "StackMapTableSample.data";
+
+    public StackMapTableAttributeTest() {
+        super();
+        is = getClass().getResourceAsStream(TEST_CLASS);
+    }
+
+    public void test() throws Exception {
+        Attribute[] attributes = new Attribute[] { new ASMStackMapTableAttribute() };
+
+        ClassWriter cw = new ClassWriter(false);
+
+        ClassReader cr1 = new ClassReader(is);
+        cr1.accept(cw, attributes, true);
+
+        ClassReader cr2 = new ClassReader(cw.toByteArray());
+
+        if (!Arrays.equals(cr1.b, cr2.b)) {
+            StringWriter sw1 = new StringWriter();
+            StringWriter sw2 = new StringWriter();
+            ClassVisitor cv1 = new TraceClassVisitor(new PrintWriter(sw1));
+            ClassVisitor cv2 = new TraceClassVisitor(new PrintWriter(sw2));
+            cr1.accept(cv1, attributes, true);
+            cr2.accept(cv2, attributes, true);
+            assertEquals("different data", sw1.toString(), sw2.toString());
+        }
+
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.class b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.class
new file mode 100644
index 0000000..14a4481
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.data b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.data
new file mode 100644
index 0000000..d3b9067
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.data
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.java b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.java
new file mode 100644
index 0000000..68534fd
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/attrs/StackMapTableSample.java
@@ -0,0 +1,141 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.attrs;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is used to cover StackMapTable variations.
+ * 
+ * It is compiled using "javac -target 1.6" into StackMapTableSample.data
+ * 
+ * @author Eugene Kuleshov
+ */
+public class StackMapTableSample implements java.io.Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private static Map MAP;
+    static {
+        MAP = new HashMap();
+        for (int k = 0; k < 10; k++) {
+            MAP.put(k < 5 ? "A" + k : "B" + k, new Integer(k));
+        }
+    }
+
+    private Map map;
+
+    public StackMapTableSample(String s) {
+        map = new HashMap();
+        for (int k = 0; k < 10; k++) {
+            map.put(k < 5 ? "A" + k : "B" + k, new Integer(k));
+        }
+    }
+
+    public void appendFrame(boolean b) {
+        int n = 5;
+        if (b)
+            return;
+    }
+
+    public void appendAndChopFrame(int i) {
+        for (int k = 0; k < i; k++) {
+        }
+    }
+
+    public int sameLocals1stackItemFrame() {
+        try {
+        } finally {
+            return 0;
+        }
+    }
+
+    public void sameLocals1stackItemFrame2() {
+        Object a;
+        try {
+            a = new Object();
+        } catch (Exception e) {
+        } finally {
+        }
+    }
+
+    public int sameLocals1stackItemFrameExtended() {
+        try {
+            long l10 = 11L;
+            long l11 = 11L;
+            long l12 = 11L;
+            long l13 = 11L;
+            long l14 = 11L;
+            long l15 = 11L;
+            long l16 = 11L;
+            long l17 = 11L;
+            long l18 = 11L;
+            long l19 = 11L;
+            long l20 = 11L;
+            long l21 = 11L;
+            long l22 = 11L; // offset > 64
+
+        } finally {
+            return 0;
+        }
+    }
+
+    public void sameFrameExtended(boolean b) {
+        while (true) {
+            long l10 = 11L;
+            long l11 = 11L;
+            long l12 = 11L;
+            long l13 = 11L;
+            long l14 = 11L;
+            long l15 = 11L;
+            long l16 = 11L;
+            long l17 = 11L;
+            long l18 = 11L;
+            long l19 = 11L;
+            long l20 = 11L;
+            long l21 = 11L;
+            long l22 = 11L; // offset > 64
+
+            if (b)
+                return;
+        }
+    }
+
+    public void fullFrame(String s) {
+        long l10 = 11L;
+        long l11 = 11L;
+        long l12 = 11L;
+        long l13 = 11L;
+
+        if (s == null)
+            return;
+    }
+
+}
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$1.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$1.class
new file mode 100644
index 0000000..62e8fce
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$1.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$AdviceClassAdapter.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$AdviceClassAdapter.class
new file mode 100644
index 0000000..8fcd121
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$AdviceClassAdapter.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$ReferenceClassAdapter.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$ReferenceClassAdapter.class
new file mode 100644
index 0000000..c4a90c2
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest$ReferenceClassAdapter.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest.class
new file mode 100644
index 0000000..af14f2a
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest.java b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest.java
new file mode 100644
index 0000000..1ca1c17
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest.java
@@ -0,0 +1,133 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.commons;
+
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class AdviceAdapterTest extends AbstractTest {
+
+    public static void main(String[] args) throws Exception {
+        TestRunner.run(AdviceAdapterTest.suite());
+    }
+
+    public static TestSuite suite() throws Exception {
+        return new AdviceAdapterTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw1 = new ClassWriter(false, true);
+        ClassWriter cw2 = new ClassWriter(false, true);
+        cr.accept(new ReferenceClassAdapter(cw1), false);
+        cr.accept(new AdviceClassAdapter(cw2), false);
+        assertEquals(new ClassReader(cw1.toByteArray()),
+                new ClassReader(cw2.toByteArray()));
+    }
+
+    static class ReferenceClassAdapter extends ClassAdapter {
+
+        public ReferenceClassAdapter(final ClassVisitor cv) {
+            super(cv);
+        }
+
+        public MethodVisitor visitMethod(
+            final int access,
+            final String name,
+            final String desc,
+            final String signature,
+            final String[] exceptions)
+        {
+            MethodVisitor mv = cv.visitMethod(access,
+                    name,
+                    desc,
+                    signature,
+                    exceptions);
+
+            if (mv == null
+                    || (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) > 0)
+            {
+                return mv;
+            }
+
+            return new LocalVariablesSorter(access, desc, mv);
+        }
+    }
+
+    static class AdviceClassAdapter extends ClassAdapter {
+
+        public AdviceClassAdapter(final ClassVisitor cv) {
+            super(cv);
+        }
+
+        public MethodVisitor visitMethod(
+            final int access,
+            final String name,
+            final String desc,
+            final String signature,
+            final String[] exceptions)
+        {
+            MethodVisitor mv = cv.visitMethod(access,
+                    name,
+                    desc,
+                    signature,
+                    exceptions);
+
+            if (mv == null
+                    || (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) > 0)
+            {
+                return mv;
+            }
+
+            return new AdviceAdapter(mv, access, name, desc) {
+                protected void onMethodEnter() {
+                    // mv.visitInsn(NOP);
+                    // mv.visitInsn(NOP);
+                    // mv.visitInsn(NOP);
+                }
+
+                protected void onMethodExit(int opcode) {
+                    // mv.visitInsn(NOP);
+                    // mv.visitInsn(NOP);
+                    // mv.visitInsn(NOP);
+                    // mv.visitInsn(NOP);
+                }
+            };
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$1.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$1.class
new file mode 100644
index 0000000..a6e7e98
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$1.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$A.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$A.class
new file mode 100644
index 0000000..f633796
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$A.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$AdviceClassAdapter.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$AdviceClassAdapter.class
new file mode 100644
index 0000000..d463aac
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$AdviceClassAdapter.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$AdvisingClassLoader.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$AdvisingClassLoader.class
new file mode 100644
index 0000000..3a84527
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$AdvisingClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$B.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$B.class
new file mode 100644
index 0000000..8afa710
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2$B.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2.class b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2.class
new file mode 100644
index 0000000..c9b24e8
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2.java b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2.java
new file mode 100644
index 0000000..762377d
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/AdviceAdapterTest2.java
@@ -0,0 +1,230 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.commons;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+
+/**
+ * Simple example of using AdviceAdapter to implement tracing callback 
+ *
+ * @author Eugene Kuleshov
+ */
+public class AdviceAdapterTest2 extends AbstractTest {
+
+    public void test() throws Exception {
+        Class c = getClass();
+        String name = c.getName();
+        AdvisingClassLoader cl = new AdvisingClassLoader(name+"$");
+        Class cc = cl.loadClass(name+"$B");
+        Method m = cc.getMethod("run", new Class[] {Integer.TYPE});
+        try {
+            m.invoke(null, new Object[] { new Integer(0)});
+        } catch (InvocationTargetException e) {
+            throw (Exception) e.getTargetException();
+        }
+    }
+
+    
+    private static class AdvisingClassLoader extends ClassLoader {
+        private String prefix;
+
+        public AdvisingClassLoader(String prefix) throws IOException {
+            this.prefix = prefix;
+        }
+        
+        public Class loadClass(String name) throws ClassNotFoundException {
+            if(name.startsWith(prefix)) {
+                try {
+                    ClassWriter cw = new ClassWriter(true, true);
+                    ClassReader cr = new ClassReader(getClass().getResourceAsStream( "/"+name.replace('.', '/')+".class"));
+                    cr.accept(new AdviceClassAdapter(cw), false);
+                    byte[] bytecode = cw.toByteArray();
+                    return super.defineClass(name, bytecode, 0, bytecode.length);            
+                } catch(IOException ex) {
+                    throw new ClassNotFoundException( "Load error: "+ex.toString(), ex);
+                }
+            }
+            return super.loadClass(name);
+        }
+
+    }
+
+    
+    // test callback
+    private static int n = 0;
+    public static void enter(String msg) {
+        System.err.println(off().append("enter ").append(msg).toString());
+        n++;
+    }
+    public static void exit(String msg) {
+        n--;
+        System.err.println(off().append("<").toString());
+    }
+    private static StringBuffer off() {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < n; i++) {
+            sb.append("  ");
+        }
+        return sb;
+    }
+    
+    
+    static class AdviceClassAdapter extends ClassAdapter implements Opcodes {
+        private String cname;
+
+        public AdviceClassAdapter(ClassVisitor cv) {
+            super(cv);
+        }
+        
+        public void visit(
+            int version,
+            int access,
+            String name,
+            String signature,
+            String superName,
+            String[] interfaces)
+        {
+            this.cname = name;
+            super.visit(version, access, name, signature, superName, interfaces);
+        }
+
+        public MethodVisitor visitMethod(
+            int access,
+            final String name,
+            final String desc,
+            String signature,
+            String[] exceptions)
+        {
+            MethodVisitor mv = cv.visitMethod(access,
+                    name,
+                    desc,
+                    signature,
+                    exceptions);
+
+            if (mv == null
+                    || (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_NATIVE)) > 0)
+            {
+                return mv;
+            }
+
+            return new AdviceAdapter(mv, access, name, desc) {
+                    protected void onMethodEnter() {
+                        mv.visitLdcInsn(cname+"."+name+desc);
+                        mv.visitMethodInsn(INVOKESTATIC, 
+                                "org/objectweb/asm/commons/AdviceAdapterTest2", 
+                                "enter", "(Ljava/lang/String;)V");
+                    }
+    
+                    protected void onMethodExit(int opcode) {
+                        mv.visitLdcInsn(cname+"."+name+desc);
+                        mv.visitMethodInsn(INVOKESTATIC, 
+                                "org/objectweb/asm/commons/AdviceAdapterTest2", 
+                                "exit", "(Ljava/lang/String;)V");
+                    }
+                    
+                };
+        }
+    }
+
+
+    // TEST CLASSES
+    
+    public static class A {
+      final String s;
+
+      public A(String s) {
+        this.s = s;
+      }
+      public A(A a) {
+        this.s = a.s;
+      }
+    }
+    
+    
+    public static class B extends A {
+      
+      public B() {
+        super(new B(""));
+        test(this);
+      }
+
+      public B( A a) {
+        super(a);
+        test(this);
+      }
+      
+      public B(String s) {
+        super(s==null ? new A( "") : new A(s));
+        test(this);
+      }
+
+      private static A aa;
+      public B(String s, A a) {
+        this(s==null ? aa = new A( s) : a);
+        A aa = new A("");
+        test(aa);
+      }
+
+      public B(String s, String s1) {
+        super(s!=null ? new A( getA(s1).s) : new A( s) );
+        test(this);
+      }
+
+      
+      private void test(Object b) {
+      }
+      private static A getA(String s) {
+        return new A(s);
+      }
+      
+      // execute all
+      public static void run(int n) {
+          new B();
+          new B( new A(""));
+          new B( new B());
+          new B( "", new A(""));
+          new B( "", "");
+      }
+      
+    }
+    
+}
+
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierClassVisitor.class b/asmx/test/conform/org/objectweb/asm/commons/GASMifierClassVisitor.class
new file mode 100644
index 0000000..b6b7a6f
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierClassVisitor.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierClassVisitor.java b/asmx/test/conform/org/objectweb/asm/commons/GASMifierClassVisitor.java
new file mode 100644
index 0000000..47dba89
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierClassVisitor.java
@@ -0,0 +1,144 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import java.io.FileInputStream;
+import java.io.PrintWriter;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.util.ASMifierClassVisitor;
+
+public class GASMifierClassVisitor extends ASMifierClassVisitor {
+
+    /**
+     * Prints the ASM source code to generate the given class to the standard
+     * output. <p> Usage: ASMifierClassVisitor [-debug] &lt;fully qualified
+     * class name or class file name&gt;
+     * 
+     * @param args the command line arguments.
+     * 
+     * @throws Exception if the class cannot be found, or if an IO exception
+     *         occurs.
+     */
+    public static void main(final String[] args) throws Exception {
+        int i = 0;
+        boolean skipDebug = true;
+
+        boolean ok = true;
+        if (args.length < 1 || args.length > 2) {
+            ok = false;
+        }
+        if (ok && args[0].equals("-debug")) {
+            i = 1;
+            skipDebug = false;
+            if (args.length != 2) {
+                ok = false;
+            }
+        }
+        if (!ok) {
+            System.err.println("Prints the ASM code to generate the given class.");
+            System.err.println("Usage: GASMifierClassVisitor [-debug] "
+                    + "<fully qualified class name or class file name>");
+            System.exit(-1);
+        }
+        ClassReader cr;
+        if (args[i].endsWith(".class")) {
+            cr = new ClassReader(new FileInputStream(args[i]));
+        } else {
+            cr = new ClassReader(args[i]);
+        }
+        cr.accept(new GASMifierClassVisitor(new PrintWriter(System.out)),
+                getDefaultAttributes(),
+                skipDebug);
+    }
+
+    public GASMifierClassVisitor(PrintWriter pw) {
+        super(pw);
+    }
+
+    public void visit(
+        final int version,
+        final int access,
+        final String name,
+        final String signature,
+        final String superName,
+        final String[] interfaces)
+    {
+        super.visit(version, access, name, signature, superName, interfaces);
+        int n;
+        if (name.lastIndexOf('/') != -1) {
+            n = 1;
+        } else {
+            n = 0;
+        }
+        text.set(n + 5, "ClassWriter cw = new ClassWriter(true);\n");
+        text.set(n + 7, "GeneratorAdapter mg;\n");
+        text.add(n + 1, "import org.objectweb.asm.commons.*;\n");
+    }
+
+    public MethodVisitor visitMethod(
+        final int access,
+        final String name,
+        final String desc,
+        final String signature,
+        final String[] exceptions)
+    {
+        buf.setLength(0);
+        buf.append("{\n");
+        buf.append("mg = new GeneratorAdapter(");
+        buf.append(access);
+        buf.append(", ");
+        buf.append(GASMifierMethodVisitor.getMethod(name, desc));
+        buf.append(", ");
+        if (signature == null) {
+            buf.append("null");
+        } else {
+            buf.append('"').append(signature).append('"');
+        }
+        buf.append(", ");
+        if (exceptions != null && exceptions.length > 0) {
+            buf.append("new Type[] {");
+            for (int i = 0; i < exceptions.length; ++i) {
+                buf.append(i == 0 ? " " : ", ");
+                buf.append(GASMifierMethodVisitor.getType(exceptions[i]));
+            }
+            buf.append(" }");
+        } else {
+            buf.append("null");
+        }
+        buf.append(", cw);\n");
+        text.add(buf.toString());
+        GASMifierMethodVisitor acv = new GASMifierMethodVisitor(access, desc);
+        text.add(acv.getText());
+        text.add("}\n");
+        return new LocalVariablesSorter(access, desc, acv);
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierMethodVisitor.class b/asmx/test/conform/org/objectweb/asm/commons/GASMifierMethodVisitor.class
new file mode 100644
index 0000000..79922f3
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierMethodVisitor.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierMethodVisitor.java b/asmx/test/conform/org/objectweb/asm/commons/GASMifierMethodVisitor.java
new file mode 100644
index 0000000..8f05f21
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierMethodVisitor.java
@@ -0,0 +1,1016 @@
+/***
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000-2005 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 org.objectweb.asm.commons;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.util.ASMifierAbstractVisitor;
+import org.objectweb.asm.util.ASMifierAnnotationVisitor;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A {@link MethodVisitor} that prints the ASM code that generates the methods
+ * it visits.
+ * 
+ * @author Eric Bruneton
+ * @author Eugene Kuleshov
+ */
+public class GASMifierMethodVisitor extends ASMifierAbstractVisitor implements
+        MethodVisitor,
+        Opcodes
+{
+
+    int access;
+
+    Type[] argumentTypes;
+
+    int firstLocal;
+
+    Map locals;
+
+    List localTypes;
+
+    int lastOpcode = -1;
+
+    HashMap labelNames;
+
+    public GASMifierMethodVisitor(int access, String desc) {
+        super("mg");
+        this.access = access;
+        this.labelNames = new HashMap();
+        this.argumentTypes = Type.getArgumentTypes(desc);
+        int nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1;
+        for (int i = 0; i < argumentTypes.length; i++) {
+            nextLocal += argumentTypes[i].getSize();
+        }
+        this.firstLocal = nextLocal;
+        this.locals = new HashMap();
+        this.localTypes = new ArrayList();
+    }
+
+    public AnnotationVisitor visitAnnotationDefault() {
+        buf.setLength(0);
+        buf.append("{\n").append("av0 = mg.visitAnnotationDefault();\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(
+        final int parameter,
+        final String desc,
+        final boolean visible)
+    {
+        buf.setLength(0);
+        buf.append("{\n")
+                .append("av0 = mg.visitParameterAnnotation(")
+                .append(parameter)
+                .append(", \"");
+        buf.append(desc);
+        buf.append("\", ").append(visible).append(");\n");
+        text.add(buf.toString());
+        ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0);
+        text.add(av.getText());
+        text.add("}\n");
+        return av;
+    }
+
+    public void visitCode() {
+        /* text.add("mg.visitCode();\n"); */
+    }
+
+    public void visitInsn(final int opcode) {
+        buf.setLength(0);
+        switch (opcode) {
+            case IRETURN:
+            case LRETURN:
+            case FRETURN:
+            case DRETURN:
+            case ARETURN:
+            case RETURN:
+                buf.append("mg.returnValue();\n");
+                break;
+            case NOP:
+                break;
+            case ACONST_NULL:
+                buf.append("mg.push((String)null);\n");
+                break;
+            case ICONST_M1:
+            case ICONST_0:
+            case ICONST_1:
+            case ICONST_2:
+            case ICONST_3:
+            case ICONST_4:
+            case ICONST_5:
+                buf.append("mg.push(").append(opcode - ICONST_0).append(");\n");
+                break;
+            case LCONST_0:
+            case LCONST_1:
+                buf.append("mg.push(")
+                        .append(opcode - LCONST_0)
+                        .append("L);\n");
+                break;
+            case FCONST_0:
+            case FCONST_1:
+            case FCONST_2:
+                buf.append("mg.push(")
+                        .append(opcode - FCONST_0)
+                        .append("f);\n");
+                break;
+            case DCONST_0:
+            case DCONST_1:
+                buf.append("mg.push(")
+                        .append(opcode - DCONST_0)
+                        .append("d);\n");
+                break;
+            case POP:
+                buf.append("mg.pop();\n");
+                break;
+            case POP2:
+                buf.append("mg.pop2();\n");
+                break;
+            case DUP:
+                buf.append("mg.dup();\n");
+                break;
+            case DUP_X1:
+                buf.append("mg.dupX1();\n");
+                break;
+            case DUP_X2:
+                buf.append("mg.dupX2();\n");
+                break;
+            case DUP2:
+                buf.append("mg.dup2();\n");
+                break;
+            case DUP2_X1:
+                buf.append("mg.dup2X1();\n");
+                break;
+            case DUP2_X2:
+                buf.append("mg.dup2X2();\n");
+                break;
+            case SWAP:
+                buf.append("mg.swap();\n");
+                break;
+            case MONITORENTER:
+                buf.append("mg.monitorEnter();\n");
+                break;
+            case MONITOREXIT:
+                buf.append("mg.monitorExit();\n");
+                break;
+            case ARRAYLENGTH:
+                buf.append("mg.arrayLength();\n");
+                break;
+            case IALOAD:
+                buf.append("mg.arrayLoad(Type.INT_TYPE);\n");
+                break;
+            case LALOAD:
+                buf.append("mg.arrayLoad(Type.LONG_TYPE);\n");
+                break;
+            case FALOAD:
+                buf.append("mg.arrayLoad(Type.FLOAT_TYPE);\n");
+                break;
+            case DALOAD:
+                buf.append("mg.arrayLoad(Type.DOUBLE_TYPE);\n");
+                break;
+            case AALOAD:
+                buf.append("mg.arrayLoad(" + getType("java/lang/Object")
+                        + ");\n");
+                break;
+            case BALOAD:
+                buf.append("mg.arrayLoad(Type.BYTE_TYPE);\n");
+                break;
+            case CALOAD:
+                buf.append("mg.arrayLoad(Type.CHAR_TYPE);\n");
+                break;
+            case SALOAD:
+                buf.append("mg.arrayLoad(Type.SHORT_TYPE);\n");
+                break;
+            case IASTORE:
+                buf.append("mg.arrayStore(Type.INT_TYPE);\n");
+                break;
+            case LASTORE:
+                buf.append("mg.arrayStore(Type.LONG_TYPE);\n");
+                break;
+            case FASTORE:
+                buf.append("mg.arrayStore(Type.FLOAT_TYPE);\n");
+                break;
+            case DASTORE:
+                buf.append("mg.arrayStore(Type.DOUBLE_TYPE);\n");
+                break;
+            case AASTORE:
+                buf.append("mg.arrayStore(" + getType("java/lang/Object")
+                        + ");\n");
+                break;
+            case BASTORE:
+                buf.append("mg.arrayStore(Type.BYTE_TYPE);\n");
+                break;
+            case CASTORE:
+                buf.append("mg.arrayStore(Type.CHAR_TYPE);\n");
+                break;
+            case SASTORE:
+                buf.append("mg.arrayStore(Type.SHORT_TYPE);\n");
+                break;
+            case IADD:
+                buf.append("mg.math(GeneratorAdapter.ADD, Type.INT_TYPE);\n");
+                break;
+            case LADD:
+                buf.append("mg.math(GeneratorAdapter.ADD, Type.LONG_TYPE);\n");
+                break;
+            case FADD:
+                buf.append("mg.math(GeneratorAdapter.ADD, Type.FLOAT_TYPE);\n");
+                break;
+            case DADD:
+                buf.append("mg.math(GeneratorAdapter.ADD, Type.DOUBLE_TYPE);\n");
+                break;
+            case ISUB:
+                buf.append("mg.math(GeneratorAdapter.SUB, Type.INT_TYPE);\n");
+                break;
+            case LSUB:
+                buf.append("mg.math(GeneratorAdapter.SUB, Type.LONG_TYPE);\n");
+                break;
+            case FSUB:
+                buf.append("mg.math(GeneratorAdapter.SUB, Type.FLOAT_TYPE);\n");
+                break;
+            case DSUB:
+                buf.append("mg.math(GeneratorAdapter.SUB, Type.DOUBLE_TYPE);\n");
+                break;
+            case IMUL:
+                buf.append("mg.math(GeneratorAdapter.MUL, Type.INT_TYPE);\n");
+                break;
+            case LMUL:
+                buf.append("mg.math(GeneratorAdapter.MUL, Type.LONG_TYPE);\n");
+                break;
+            case FMUL:
+                buf.append("mg.math(GeneratorAdapter.MUL, Type.FLOAT_TYPE);\n");
+                break;
+            case DMUL:
+                buf.append("mg.math(GeneratorAdapter.MUL, Type.DOUBLE_TYPE);\n");
+                break;
+            case IDIV:
+                buf.append("mg.math(GeneratorAdapter.DIV, Type.INT_TYPE);\n");
+                break;
+            case LDIV:
+                buf.append("mg.math(GeneratorAdapter.DIV, Type.LONG_TYPE);\n");
+                break;
+            case FDIV:
+                buf.append("mg.math(GeneratorAdapter.DIV, Type.FLOAT_TYPE);\n");
+                break;
+            case DDIV:
+                buf.append("mg.math(GeneratorAdapter.DIV, Type.DOUBLE_TYPE);\n");
+                break;
+            case IREM:
+                buf.append("mg.math(GeneratorAdapter.REM, Type.INT_TYPE);\n");
+                break;
+            case LREM:
+                buf.append("mg.math(GeneratorAdapter.REM, Type.LONG_TYPE);\n");
+                break;
+            case FREM:
+                buf.append("mg.math(GeneratorAdapter.REM, Type.FLOAT_TYPE);\n");
+                break;
+            case DREM:
+                buf.append("mg.math(GeneratorAdapter.REM, Type.DOUBLE_TYPE);\n");
+                break;
+            case INEG:
+                buf.append("mg.math(GeneratorAdapter.NEG, Type.INT_TYPE);\n");
+                break;
+            case LNEG:
+                buf.append("mg.math(GeneratorAdapter.NEG, Type.LONG_TYPE);\n");
+                break;
+            case FNEG:
+                buf.append("mg.math(GeneratorAdapter.NEG, Type.FLOAT_TYPE);\n");
+                break;
+            case DNEG:
+                buf.append("mg.math(GeneratorAdapter.NEG, Type.DOUBLE_TYPE);\n");
+                break;
+            case ISHL:
+                buf.append("mg.math(GeneratorAdapter.SHL, Type.INT_TYPE);\n");
+                break;
+            case LSHL:
+                buf.append("mg.math(GeneratorAdapter.SHL, Type.LONG_TYPE);\n");
+                break;
+            case ISHR:
+                buf.append("mg.math(GeneratorAdapter.SHR, Type.INT_TYPE);\n");
+                break;
+            case LSHR:
+                buf.append("mg.math(GeneratorAdapter.SHR, Type.LONG_TYPE);\n");
+                break;
+            case IUSHR:
+                buf.append("mg.math(GeneratorAdapter.USHR, Type.INT_TYPE);\n");
+                break;
+            case LUSHR:
+                buf.append("mg.math(GeneratorAdapter.USHR, Type.LONG_TYPE);\n");
+                break;
+            case IAND:
+                buf.append("mg.math(GeneratorAdapter.AND, Type.INT_TYPE);\n");
+                break;
+            case LAND:
+                buf.append("mg.math(GeneratorAdapter.AND, Type.LONG_TYPE);\n");
+                break;
+            case IOR:
+                buf.append("mg.math(GeneratorAdapter.OR, Type.INT_TYPE);\n");
+                break;
+            case LOR:
+                buf.append("mg.math(GeneratorAdapter.OR, Type.LONG_TYPE);\n");
+                break;
+            case IXOR:
+                buf.append("mg.math(GeneratorAdapter.XOR, Type.INT_TYPE);\n");
+                break;
+            case LXOR:
+                buf.append("mg.math(GeneratorAdapter.XOR, Type.LONG_TYPE);\n");
+                break;
+            case ATHROW:
+                buf.append("mg.throwException();\n");
+                break;
+            case I2L:
+                buf.append("mg.cast(Type.INT_TYPE, Type.LONG_TYPE);\n");
+                break;
+            case I2F:
+                buf.append("mg.cast(Type.INT_TYPE, Type.FLOAT_TYPE);\n");
+                break;
+            case I2D:
+                buf.append("mg.cast(Type.INT_TYPE, Type.DOUBLE_TYPE);\n");
+                break;
+            case L2I:
+                buf.append("mg.cast(Type.LONG_TYPE, Type.INT_TYPE);\n");
+                break;
+            case L2F:
+                buf.append("mg.cast(Type.LONG_TYPE, Type.FLOAT_TYPE);\n");
+                break;
+            case L2D:
+                buf.append("mg.cast(Type.LONG_TYPE, Type.DOUBLE_TYPE);\n");
+                break;
+            case F2I:
+                buf.append("mg.cast(Type.FLOAT_TYPE, Type.INT_TYPE);\n");
+                break;
+            case F2L:
+                buf.append("mg.cast(Type.FLOAT_TYPE, Type.LONG_TYPE);\n");
+                break;
+            case F2D:
+                buf.append("mg.cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE);\n");
+                break;
+            case D2I:
+                buf.append("mg.cast(Type.DOUBLE_TYPE, Type.INT_TYPE);\n");
+                break;
+            case D2L:
+                buf.append("mg.cast(Type.DOUBLE_TYPE, Type.LONG_TYPE);\n");
+                break;
+            case D2F:
+                buf.append("mg.cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE);\n");
+                break;
+            case I2B:
+                // TODO detect if previous element in 'text' is a cast,
+                // for possible optimisations (e.g. cast(F,I) cast(I,B) =
+                // cast(F,B))
+                buf.append("mg.cast(Type.INT_TYPE, Type.BYTE_TYPE);\n");
+                break;
+            case I2C: // idem
+                buf.append("mg.cast(Type.INT_TYPE, Type.CHAR_TYPE);\n");
+                break;
+            case I2S: // idem
+                buf.append("mg.cast(Type.INT_TYPE, Type.SHORT_TYPE);\n");
+                break;
+            case LCMP:
+            case FCMPL:
+            case FCMPG:
+            case DCMPL:
+            case DCMPG:
+                // TODO detect xCMPy IF_ICMP -> ifCmp(..., ..., label)
+                buf.append("mg.visitInsn(")
+                        .append(OPCODES[opcode])
+                        .append(");\n");
+                break;
+            default:
+                throw new RuntimeException("unexpected case");
+        }
+        text.add(buf.toString());
+        lastOpcode = opcode;
+    }
+
+    public void visitIntInsn(final int opcode, final int operand) {
+        buf.setLength(0);
+        if (opcode == NEWARRAY) {
+            String type;
+            switch (operand) {
+                case T_BOOLEAN:
+                    type = "Type.BOOLEAN_TYPE";
+                    break;
+                case T_CHAR:
+                    type = "Type.CHAR_TYPE";
+                    break;
+                case T_FLOAT:
+                    type = "Type.FLOAT_TYPE";
+                    break;
+                case T_DOUBLE:
+                    type = "Type.DOUBLE_TYPE";
+                    break;
+                case T_BYTE:
+                    type = "Type.BYTE_TYPE";
+                    break;
+                case T_SHORT:
+                    type = "Type.SHORT_TYPE";
+                    break;
+                case T_INT:
+                    type = "Type.INT_TYPE";
+                    break;
+                case T_LONG:
+                    type = "Type.LONG_TYPE";
+                    break;
+                default:
+                    throw new RuntimeException("unexpected case");
+            }
+            buf.append("mg.newArray(").append(type).append(");\n");
+        } else {
+            buf.append("mg.push(").append(operand).append(");\n");
+        }
+        text.add(buf.toString());
+        lastOpcode = opcode;
+    }
+
+    public void visitVarInsn(final int opcode, int var) {
+        buf.setLength(0);
+        switch (opcode) {
+            case RET:
+                buf.append("mg.ret(");
+                if (var < firstLocal) {
+                    buf.append(var);
+                } else {
+                    int v = generateNewLocal(var, "Type.INT_TYPE");
+                    buf.append("local").append(v);
+                }
+                buf.append(");\n");
+                break;
+
+            case ILOAD:
+                generateLoadLocal(var, "Type.INT_TYPE");
+                break;
+            case LLOAD:
+                generateLoadLocal(var, "Type.LONG_TYPE");
+                break;
+            case FLOAD:
+                generateLoadLocal(var, "Type.FLOAT_TYPE");
+                break;
+            case DLOAD:
+                generateLoadLocal(var, "Type.DOUBLE_TYPE");
+                break;
+            case ALOAD:
+                generateLoadLocal(var, getType("java/lang/Object"));
+                break;
+
+            case ISTORE:
+                generateStoreLocal(var, "Type.INT_TYPE");
+                break;
+            case LSTORE:
+                generateStoreLocal(var, "Type.LONG_TYPE");
+                break;
+            case FSTORE:
+                generateStoreLocal(var, "Type.FLOAT_TYPE");
+                break;
+            case DSTORE:
+                generateStoreLocal(var, "Type.DOUBLE_TYPE");
+                break;
+            case ASTORE:
+                generateStoreLocal(var, getType("java/lang/Object"));
+                break;
+
+            default:
+                throw new RuntimeException("unexpected case");
+        }
+
+        text.add(buf.toString());
+        lastOpcode = opcode;
+    }
+
+    private void generateLoadLocal(int var, String type) {
+        if (var < firstLocal) {
+            if (var == 0 && (access & ACC_STATIC) == 0) {
+                buf.append("mg.loadThis();\n");
+            } else {
+                buf.append("mg.loadArg(")
+                        .append(getArgIndex(var))
+                        .append(");\n");
+            }
+        } else {
+            int local = generateNewLocal(var, type);
+            buf.append("mg.loadLocal(local").append(local);
+            if (!type.equals(localTypes.get(local))) {
+                localTypes.set(local, type);
+                buf.append(", ").append(type);
+            }
+            buf.append(");\n");
+        }
+    }
+
+    private void generateStoreLocal(int var, String type) {
+        if (var < firstLocal) {
+            buf.append("mg.storeArg(").append(getArgIndex(var)).append(");\n");
+        } else {
+            int local = generateNewLocal(var, type);
+            buf.append("mg.storeLocal(local").append(local);
+            if (!type.equals(localTypes.get(local))) {
+                localTypes.set(local, type);
+                buf.append(", ").append(type);
+            }
+            buf.append(");\n");
+        }
+    }
+
+    private int generateNewLocal(int var, String type) {
+        Integer i = (Integer) locals.get(new Integer(var));
+        if (i == null) {
+            int local = locals.size();
+            locals.put(new Integer(var), new Integer(local));
+            localTypes.add(type);
+            buf.append("int local" + local + " = mg.newLocal(" + type + ");\n");
+            return local;
+        }
+        return i.intValue();
+    }
+
+    private int getArgIndex(int var) {
+        int nextLocal = ((Opcodes.ACC_STATIC & access) != 0) ? 0 : 1;
+        int i = 0;
+        while (nextLocal != var) {
+            nextLocal += argumentTypes[i++].getSize();
+        }
+        return i;
+    }
+
+    public void visitTypeInsn(final int opcode, final String desc) {
+        String type;
+        if (desc.charAt(0) == '[') {
+            type = getDescType(desc);
+        } else {
+            type = getType(desc);
+        }
+        buf.setLength(0);
+        if (opcode == NEW) {
+            buf.append("mg.newInstance(").append(type).append(");\n");
+        } else if (opcode == ANEWARRAY) {
+            buf.append("mg.newArray(").append(type).append(");\n");
+        } else if (opcode == CHECKCAST) {
+            buf.append("mg.checkCast(").append(type).append(");\n");
+        } else if (opcode == INSTANCEOF) {
+            buf.append("mg.instanceOf(").append(type).append(");\n");
+        }
+        text.add(buf.toString());
+        lastOpcode = opcode;
+    }
+
+    public void visitFieldInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        switch (opcode) {
+            case GETFIELD:
+                buf.append("mg.getField(");
+                break;
+            case PUTFIELD:
+                buf.append("mg.putField(");
+                break;
+            case GETSTATIC:
+                buf.append("mg.getStatic(");
+                break;
+            case PUTSTATIC:
+                buf.append("mg.putStatic(");
+                break;
+            default:
+                throw new RuntimeException("unexpected case");
+        }
+        buf.append(getType(owner));
+        buf.append(", \"");
+        buf.append(name);
+        buf.append("\", ");
+        buf.append(getDescType(desc));
+        buf.append(");\n");
+        text.add(buf.toString());
+        lastOpcode = opcode;
+    }
+
+    public void visitMethodInsn(
+        final int opcode,
+        final String owner,
+        final String name,
+        final String desc)
+    {
+        buf.setLength(0);
+        switch (opcode) {
+            case INVOKEVIRTUAL:
+                buf.append("mg.invokeVirtual(");
+                break;
+            case INVOKESPECIAL:
+                buf.append("mg.invokeConstructor(");
+                break;
+            case INVOKESTATIC:
+                buf.append("mg.invokeStatic(");
+                break;
+            case INVOKEINTERFACE:
+                buf.append("mg.invokeInterface(");
+                break;
+            default:
+                throw new RuntimeException("unexpected case");
+        }
+        if (owner.charAt(0) == '[') {
+            buf.append(getDescType(owner));
+        } else {
+            buf.append(getType(owner));
+        }
+        buf.append(", ");
+        buf.append(getMethod(name, desc));
+        buf.append(");\n");
+        text.add(buf.toString());
+        lastOpcode = opcode;
+    }
+
+    public void visitJumpInsn(final int opcode, final Label label) {
+        buf.setLength(0);
+        declareLabel(label);
+        if (opcode == GOTO || opcode == IFNULL || opcode == IFNONNULL) {
+            if (opcode == GOTO)
+                buf.append("mg.goTo(");
+            if (opcode == IFNULL)
+                buf.append("mg.ifNull(");
+            if (opcode == IFNONNULL)
+                buf.append("mg.ifNonNull(");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ICMPEQ) {
+            buf.append("mg.ifICmp(GeneratorAdapter.EQ, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ICMPNE) {
+            buf.append("mg.ifICmp(GeneratorAdapter.NE, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ICMPLT) {
+            buf.append("mg.ifICmp(GeneratorAdapter.LT, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ICMPGE) {
+            buf.append("mg.ifICmp(GeneratorAdapter.GE, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ICMPGT) {
+            buf.append("mg.ifICmp(GeneratorAdapter.GT, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ICMPLE) {
+            buf.append("mg.ifICmp(GeneratorAdapter.LE, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ACMPEQ) {
+            buf.append("mg.ifCmp(");
+            buf.append(getType("java/lang/Object"))
+                    .append(", ")
+                    .append("GeneratorAdapter.EQ, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IF_ACMPNE) {
+            buf.append("mg.ifCmp(");
+            buf.append(getType("java/lang/Object"))
+                    .append(", ")
+                    .append("GeneratorAdapter.NE, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IFEQ) {
+            buf.append("mg.ifZCmp(GeneratorAdapter.EQ, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IFNE) {
+            buf.append("mg.ifZCmp(GeneratorAdapter.NE, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IFLT) {
+            buf.append("mg.ifZCmp(GeneratorAdapter.LT, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IFGE) {
+            buf.append("mg.ifZCmp(GeneratorAdapter.GE, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IFGT) {
+            buf.append("mg.ifZCmp(GeneratorAdapter.GT, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else if (opcode == IFLE) {
+            buf.append("mg.ifZCmp(GeneratorAdapter.LE, ");
+            appendLabel(label);
+            buf.append(");\n");
+        } else {
+            buf.append("mg.visitJumpInsn(")
+                    .append(OPCODES[opcode])
+                    .append(", ");
+            appendLabel(label);
+            buf.append(");\n");
+        }
+        text.add(buf.toString());
+        lastOpcode = opcode;
+    }
+
+    public void visitLabel(final Label label) {
+        buf.setLength(0);
+        declareLabel(label);
+        buf.append("mg.mark(");
+        appendLabel(label);
+        buf.append(");\n");
+        text.add(buf.toString());
+        lastOpcode = -1;
+    }
+
+    public void visitLdcInsn(final Object cst) {
+        buf.setLength(0);
+        buf.append("mg.push(");
+        if (cst == null) {
+            buf.append("(String)null");
+        } else if (cst instanceof Long) {
+            buf.append(cst + "L");
+        } else if (cst instanceof Float) {
+            float f = ((Float) cst).floatValue();
+            if (Float.isNaN(f)) {
+                buf.append("Float.NaN");
+            } else if (Float.isInfinite(f)) {
+                buf.append(f > 0
+                        ? "Float.POSITIVE_INFINITY"
+                        : "Float.NEGATIVE_INFINITY");
+            } else {
+                buf.append(cst + "f");
+            }
+        } else if (cst instanceof Double) {
+            double d = ((Double) cst).doubleValue();
+            if (Double.isNaN(d)) {
+                buf.append("Double.NaN");
+            } else if (Double.isInfinite(d)) {
+                buf.append(d > 0
+                        ? "Double.POSITIVE_INFINITY"
+                        : "Double.NEGATIVE_INFINITY");
+            } else {
+                buf.append(cst + "d");
+            }
+        } else if (cst instanceof String) {
+            appendString(buf, (String) cst);
+        } else if (cst instanceof Type) {
+            buf.append("Type.getType(\"").append(cst).append("\")");
+        } else {
+            buf.append(cst);
+        }
+        buf.append(");\n");
+        text.add(buf.toString());
+        lastOpcode = LDC;
+    }
+
+    public void visitIincInsn(final int var, final int increment) {
+        buf.setLength(0);
+        if (var < firstLocal) {
+            buf.append("mg.iinc(").append(var);
+        } else {
+            int v = generateNewLocal(var, "Type.INT_TYPE");
+            buf.append("mg.iinc(local").append(v);
+        }
+        buf.append(", ").append(increment).append(");\n");
+        text.add(buf.toString());
+        lastOpcode = IINC;
+    }
+
+    public void visitTableSwitchInsn(
+        final int min,
+        final int max,
+        final Label dflt,
+        final Label labels[])
+    {
+        buf.setLength(0);
+        for (int i = 0; i < labels.length; ++i) {
+            declareLabel(labels[i]);
+        }
+        declareLabel(dflt);
+
+        buf.append("mg.visitTableSwitchInsn(")
+                .append(min)
+                .append(", ")
+                .append(max)
+                .append(", ");
+        appendLabel(dflt);
+        buf.append(", new Label[] {");
+        for (int i = 0; i < labels.length; ++i) {
+            buf.append(i == 0 ? " " : ", ");
+            appendLabel(labels[i]);
+        }
+        buf.append(" }); // TODO\n");
+        text.add(buf.toString());
+        lastOpcode = TABLESWITCH;
+    }
+
+    public void visitLookupSwitchInsn(
+        final Label dflt,
+        final int keys[],
+        final Label labels[])
+    {
+        buf.setLength(0);
+        for (int i = 0; i < labels.length; ++i) {
+            declareLabel(labels[i]);
+        }
+        declareLabel(dflt);
+
+        buf.append("mg.visitLookupSwitchInsn(");
+        appendLabel(dflt);
+        buf.append(", new int[] {");
+        for (int i = 0; i < keys.length; ++i) {
+            buf.append(i == 0 ? " " : ", ").append(keys[i]);
+        }
+        buf.append(" }, new Label[] {");
+        for (int i = 0; i < labels.length; ++i) {
+            buf.append(i == 0 ? " " : ", ");
+            appendLabel(labels[i]);
+        }
+        buf.append(" }); // TODO\n");
+        text.add(buf.toString());
+        lastOpcode = LOOKUPSWITCH;
+    }
+
+    public void visitMultiANewArrayInsn(final String desc, final int dims) {
+        buf.setLength(0);
+        buf.append("mg.visitMultiANewArrayInsn(\"");
+        buf.append(desc);
+        buf.append("\", ").append(dims).append(");\n");
+        text.add(buf.toString());
+        lastOpcode = MULTIANEWARRAY;
+    }
+
+    public void visitTryCatchBlock(
+        final Label start,
+        final Label end,
+        final Label handler,
+        final String type)
+    {
+        buf.setLength(0);
+        declareLabel(start);
+        declareLabel(end);
+        declareLabel(handler);
+        buf.append("mg.visitTryCatchBlock(");
+        appendLabel(start);
+        buf.append(", ");
+        appendLabel(end);
+        buf.append(", ");
+        appendLabel(handler);
+        buf.append(", ");
+        if (type == null) {
+            buf.append("null");
+        } else {
+            buf.append('"').append(type).append('"');
+        }
+        buf.append("); // TODO\n");
+        text.add(buf.toString());
+        lastOpcode = -1;
+    }
+
+    public void visitLocalVariable(
+        final String name,
+        final String desc,
+        final String signature,
+        final Label start,
+        final Label end,
+        final int index)
+    {
+        buf.setLength(0);
+        buf.append("mg.visitLocalVariable(\"");
+        buf.append(name);
+        buf.append("\", \"");
+        buf.append(desc);
+        buf.append("\", ");
+        if (signature == null) {
+            buf.append("null");
+        } else {
+            buf.append('"').append(signature).append('"');
+        }
+        buf.append(", ");
+        appendLabel(start);
+        buf.append(", ");
+        appendLabel(end);
+        buf.append(", ").append(index).append(");\n");
+        text.add(buf.toString());
+        lastOpcode = -1;
+    }
+
+    public void visitLineNumber(final int line, final Label start) {
+        buf.setLength(0);
+        buf.append("mg.visitLineNumber(").append(line).append(", ");
+        appendLabel(start);
+        buf.append(");\n");
+        text.add(buf.toString());
+        lastOpcode = -1;
+    }
+
+    public void visitMaxs(final int maxStack, final int maxLocals) {
+        text.add("mg.endMethod();\n");
+        lastOpcode = -1;
+    }
+
+    public void visitEnd() {
+        // does nothing
+    }
+
+    static String getType(String internalName) {
+        return "Type.getType(\"L" + internalName + ";\")";
+    }
+
+    static String getDescType(String desc) {
+        if (desc.equals("Z"))
+            return "Type.BOOLEAN_TYPE";
+        if (desc.equals("B"))
+            return "Type.BYTE_TYPE";
+        if (desc.equals("C"))
+            return "Type.CHAR_TYPE";
+        if (desc.equals("D"))
+            return "Type.DOUBLE_TYPE";
+        if (desc.equals("F"))
+            return "Type.FLOAT_TYPE";
+        if (desc.equals("I"))
+            return "Type.INT_TYPE";
+        if (desc.equals("J"))
+            return "Type.LONG_TYPE";
+        if (desc.equals("S"))
+            return "Type.SHORT_TYPE";
+        if (desc.equals("V"))
+            return "Type.VOID_TYPE";
+        return "Type.getType(\"" + desc + "\")";
+    }
+
+    static String getMethod(String name, String desc) {
+        Type rt = Type.getReturnType(desc);
+        Type[] argt = Type.getArgumentTypes(desc);
+        StringBuffer buf = new StringBuffer();
+        buf.append("Method.getMethod(\"");
+        buf.append(rt.getClassName()).append(" ");
+        buf.append(name).append("(");
+        for (int i = 0; i < argt.length; ++i) {
+            if (i > 0)
+                buf.append(',');
+            buf.append(argt[i].getClassName());
+        }
+        buf.append(")\")");
+        return buf.toString();
+    }
+
+    /**
+     * Appends a declaration of the given label to {@link #buf buf}. This
+     * declaration is of the form "Label lXXX = new Label();". Does nothing if
+     * the given label has already been declared.
+     * 
+     * @param l a label.
+     */
+    private void declareLabel(final Label l) {
+        String name = (String) labelNames.get(l);
+        if (name == null) {
+            name = "label" + labelNames.size();
+            labelNames.put(l, name);
+            buf.append("Label ").append(name).append(" = mg.newLabel();\n");
+        }
+    }
+
+    /**
+     * Appends the name of the given label to {@link #buf buf}. The given label
+     * <i>must</i> already have a name. One way to ensure this is to always
+     * call {@link #declareLabel declared} before calling this method.
+     * 
+     * @param l a label.
+     */
+    private void appendLabel(final Label l) {
+        buf.append((String) labelNames.get(l));
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$1.class b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$1.class
new file mode 100644
index 0000000..43a4228
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$1.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$Compiler.class b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$Compiler.class
new file mode 100644
index 0000000..dd34d17
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$Compiler.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$TestClassLoader.class
new file mode 100644
index 0000000..c640024
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest$TestClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest.class b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest.class
new file mode 100644
index 0000000..7b8a955
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest.java b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest.java
new file mode 100644
index 0000000..0ea08d4
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/GASMifierTest.java
@@ -0,0 +1,153 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.commons;
+
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.ClassLoaderIClassLoader;
+import org.codehaus.janino.DebuggingInformation;
+import org.codehaus.janino.IClassLoader;
+import org.codehaus.janino.Parser;
+import org.codehaus.janino.Scanner;
+import org.codehaus.janino.UnitCompiler;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * GASMifier tests.
+ * 
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public class GASMifierTest extends AbstractTest {
+
+    public static final Compiler COMPILER = new Compiler();
+
+    private final static TestClassLoader LOADER = new TestClassLoader();
+
+    public static TestSuite suite() throws Exception {
+        return new GASMifierTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+
+        if (cr.b.length > 20000) {
+            return;
+        }
+
+        StringWriter sw = new StringWriter();
+        GASMifierClassVisitor cv = new GASMifierClassVisitor(new PrintWriter(sw));
+        cr.accept(cv, false);
+
+        String generated = sw.toString();
+
+        byte[] generatorClassData;
+        try {
+            generatorClassData = COMPILER.compile(n, generated);
+        } catch (Exception ex) {
+            trace(generated);
+            throw ex;
+        }
+
+        ClassWriter cw = new ClassWriter(true);
+        cr.accept(new ClassAdapter(cw) {
+            public MethodVisitor visitMethod(
+                int access,
+                String name,
+                String desc,
+                String signature,
+                String[] exceptions)
+            {
+                return new LocalVariablesSorter(access,
+                        desc,
+                        super.visitMethod(access,
+                                name,
+                                desc,
+                                signature,
+                                exceptions));
+            }
+        }, false);
+        cr = new ClassReader(cw.toByteArray());
+
+        Class c = LOADER.defineClass("asm." + n + "Dump", generatorClassData);
+        Method m = c.getMethod("dump", new Class[0]);
+        byte[] b;
+        try {
+            b = (byte[]) m.invoke(null, new Object[0]);
+        } catch (InvocationTargetException ex) {
+            trace(generated);
+            throw (Exception) ex.getTargetException();
+        }
+
+        try {
+            assertEquals(cr, new ClassReader(b));
+        } catch (Throwable e) {
+            trace(generated);
+            assertEquals(cr, new ClassReader(b));
+        }
+    }
+
+    private void trace(String generated) {
+        if (System.getProperty("asm.test.class") != null) {
+            System.err.println(generated);
+        }
+    }
+
+    private static class TestClassLoader extends ClassLoader {
+
+        public Class defineClass(final String name, final byte[] b) {
+            return defineClass(name, b, 0, b.length);
+        }
+    }
+
+    private static class Compiler {
+
+        final static IClassLoader CL = new ClassLoaderIClassLoader(new URLClassLoader(new URL[0]));
+
+        public byte[] compile(String name, String source) throws Exception {
+            Parser p = new Parser(new Scanner(name, new StringReader(source)));
+            UnitCompiler uc = new UnitCompiler(p.parseCompilationUnit(), CL);
+            return uc.compileUnit(DebuggingInformation.ALL)[0].toByteArray();
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest$1.class b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest$1.class
new file mode 100644
index 0000000..86e2593
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest$1.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest$TestClassLoader.class
new file mode 100644
index 0000000..c7881fb
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest$TestClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest.class b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest.class
new file mode 100644
index 0000000..c276c44
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest.java b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest.java
new file mode 100644
index 0000000..dbdbd7f
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/LocalVariablesSorterTest.java
@@ -0,0 +1,88 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.commons;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+
+/**
+ * @author Eric Bruneton
+ */
+public class LocalVariablesSorterTest extends AbstractTest {
+
+    private final static TestClassLoader LOADER = new TestClassLoader();
+
+    public static TestSuite suite() throws Exception {
+        return new LocalVariablesSorterTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(true, true);
+        cr.accept(new ClassAdapter(cw) {
+            public MethodVisitor visitMethod(
+                int access,
+                String name,
+                String desc,
+                String signature,
+                String[] exceptions)
+            {
+                return new LocalVariablesSorter(access,
+                        desc,
+                        super.visitMethod(access,
+                                name,
+                                desc,
+                                signature,
+                                exceptions));
+            }
+        }, false);
+        byte[] b = cw.toByteArray();
+        try {
+            LOADER.defineClass(n, b);
+        } catch (ClassFormatError cfe) {
+            fail(cfe.getMessage());
+        } catch (Throwable ignored) {
+        }
+    }
+
+    // ------------------------------------------------------------------------
+
+    static class TestClassLoader extends ClassLoader {
+
+        public Class defineClass(final String name, final byte[] b) {
+            return defineClass(name, b, 0, b.length);
+        }
+    }
+}
\ No newline at end of file
diff --git a/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest$1.class b/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest$1.class
new file mode 100644
index 0000000..02b707c
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest$1.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest.class b/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest.class
new file mode 100644
index 0000000..e10b324
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest.java b/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest.java
new file mode 100644
index 0000000..a4541d1
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/SerialVersionUIDAdderTest.java
@@ -0,0 +1,75 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.commons;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for the SerialVerionUid computation.
+ * 
+ * @author Alexandre Vasseur
+ * @author Eric Bruneton
+ */
+public class SerialVersionUIDAdderTest extends TestCase implements Serializable
+{
+
+    protected final static int aField = 32;
+
+    static {
+        System.gc();
+    }
+
+    public Object[] aMethod() {
+        return null;
+    }
+
+    private long computeSerialVersionUID(String className) throws IOException {
+        final long[] svuid = new long[1];
+        ClassVisitor cv = new SerialVersionUIDAdder(new EmptyVisitor()) {
+            protected long computeSVUID() throws IOException {
+                svuid[0] = super.computeSVUID();
+                return svuid[0];
+            }
+        };
+        new ClassReader(className).accept(cv, false);
+        return svuid[0];
+    }
+
+    public void test() throws Throwable {
+        long UID = computeSerialVersionUID(getClass().getName());
+        assertEquals(3047446532799341501L, UID);
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest$TestClassLoader.class
new file mode 100644
index 0000000..9b4ceb2
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest$TestClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest.class b/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest.class
new file mode 100644
index 0000000..c92f60c
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest.java b/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest.java
new file mode 100644
index 0000000..3b9dd73
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/commons/StaticInitMergerTest.java
@@ -0,0 +1,87 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.commons;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Eric Bruneton
+ */
+public class StaticInitMergerTest extends TestCase implements Opcodes {
+
+    private final static TestClassLoader LOADER = new TestClassLoader();
+
+    public void test() throws Exception {
+        ClassWriter cw = new ClassWriter(true);
+        ClassVisitor cv = new StaticInitMerger("$clinit$", cw);
+        cv.visit(V1_1, ACC_PUBLIC, "A", null, "java/lang/Object", null);
+        cv.visitField(ACC_PUBLIC + ACC_STATIC, "counter", "I", null, null);
+        for (int i = 0; i < 5; ++i) {
+            MethodVisitor mv = cv.visitMethod(ACC_PUBLIC,
+                    "<clinit>",
+                    "()V",
+                    null,
+                    null);
+            mv.visitFieldInsn(GETSTATIC, "A", "counter", "I");
+            mv.visitInsn(ICONST_1);
+            mv.visitInsn(IADD);
+            mv.visitFieldInsn(PUTSTATIC, "A", "counter", "I");
+            mv.visitInsn(RETURN);
+            mv.visitMaxs(0, 0);
+        }
+        MethodVisitor mv = cv.visitMethod(ACC_PUBLIC,
+                "<init>",
+                "()V",
+                null,
+                null);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V");
+        mv.visitInsn(RETURN);
+        mv.visitMaxs(0, 0);
+        cv.visitEnd();
+
+        Class c = LOADER.defineClass("A", cw.toByteArray());
+        assertEquals(c.getField("counter").getInt(c.newInstance()), 5);
+    }
+
+    // ------------------------------------------------------------------------
+
+    static class TestClassLoader extends ClassLoader {
+
+        public Class defineClass(final String name, final byte[] b) {
+            return defineClass(name, b, 0, b.length);
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/signature/SignatureTest.class b/asmx/test/conform/org/objectweb/asm/signature/SignatureTest.class
new file mode 100644
index 0000000..b3c5381
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/signature/SignatureTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/signature/SignatureTest.java b/asmx/test/conform/org/objectweb/asm/signature/SignatureTest.java
new file mode 100644
index 0000000..a9a9c11
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/signature/SignatureTest.java
@@ -0,0 +1,92 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.signature;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+
+import org.objectweb.asm.signature.SignatureReader;
+import org.objectweb.asm.signature.SignatureWriter;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Signature tests.
+ * 
+ * @author Thomas Hallgren
+ * @author Eric Bruneton
+ */
+public class SignatureTest extends TestCase {
+
+    private String line;
+
+    public SignatureTest(String line) {
+        super("test");
+        this.line = line;
+    }
+
+    public static TestSuite suite() throws Exception {
+        TestSuite suite = new TestSuite();
+        InputStream is = SignatureTest.class.getResourceAsStream("signatures.txt");
+        LineNumberReader lnr = new LineNumberReader(new InputStreamReader(is));
+
+        String line;
+        while ((line = lnr.readLine()) != null) {
+            if (line.length() < 2) {
+                continue;
+            }
+            suite.addTest(new SignatureTest(line));
+        }
+        lnr.close();
+        return suite;
+    }
+
+    public void test() throws Exception {
+        if (line.length() > 2) {
+            String signature = line.substring(2);
+            SignatureWriter wrt = new SignatureWriter();
+            SignatureReader rdr = new SignatureReader(signature);
+            switch (line.charAt(0)) {
+                case 'C':
+                case 'M':
+                    rdr.accept(wrt);
+                    break;
+                case 'T':
+                    rdr.acceptType(wrt);
+                    break;
+                default:
+                    return;
+            }
+            assertEquals(signature, wrt.toString());
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/signature/signatures.txt b/asmx/test/conform/org/objectweb/asm/signature/signatures.txt
new file mode 100644
index 0000000..0301cd5
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/signature/signatures.txt
@@ -0,0 +1,1902 @@
+C <D::Ljava/lang/reflect/GenericDeclaration;>Ljava/lang/Object;Ljava/lang/reflect/Type;
+C <D::Ljava/lang/reflect/GenericDeclaration;>Ljava/lang/Object;Lsun/reflect/generics/scope/Scope;
+C <D::Ljava/lang/reflect/GenericDeclaration;>Lsun/reflect/generics/reflectiveObjects/LazyReflectiveObjectGenerator;Ljava/lang/reflect/TypeVariable<TD;>;
+C <E::Ljava/util/concurrent/Delayed;>Ljava/util/AbstractQueue<TE;>;Ljava/util/concurrent/BlockingQueue<TE;>;
+C <E:Ljava/lang/Enum<TE;>;>Ljava/lang/Object;Ljava/io/Serializable;
+C <E:Ljava/lang/Enum<TE;>;>Ljava/lang/Object;Ljava/lang/Comparable<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Enum<TE;>;>Ljava/lang/Object;Ljava/util/Iterator<TE;>;
+C <E:Ljava/lang/Enum<TE;>;>Ljava/util/AbstractSet<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <E:Ljava/lang/Enum<TE;>;>Ljava/util/EnumSet<TE;>;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Iterable<TE;>;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Collection<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Enumeration<TE;>;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Iterator<TE;>;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/List<TE;>;Ljava/util/RandomAccess;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/ListIterator<TE;>;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Queue<TE;>;
+C <E:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Set<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/List<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/Queue<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractCollection<TE;>;Ljava/util/Set<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;Ljava/util/List<TE;>;Ljava/util/RandomAccess;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractList<TE;>;Ljava/util/RandomAccess;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractQueue<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractQueue<TE;>;Ljava/util/Queue<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractQueue<TE;>;Ljava/util/concurrent/BlockingQueue<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractSequentialList<TE;>;Ljava/util/List<TE;>;Ljava/util/Queue<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/util/Set<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/AbstractSet<TE;>;Ljava/util/SortedSet<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$CheckedCollection<TE;>;Ljava/util/List<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$CheckedCollection<TE;>;Ljava/util/Set<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$CheckedList<TE;>;Ljava/util/RandomAccess;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$CheckedSet<TE;>;Ljava/util/SortedSet<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$SynchronizedCollection<TE;>;Ljava/util/List<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$SynchronizedCollection<TE;>;Ljava/util/Set<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$SynchronizedList<TE;>;Ljava/util/RandomAccess;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$SynchronizedSet<TE;>;Ljava/util/SortedSet<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableCollection<TE;>;Ljava/util/List<TE;>;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableCollection<TE;>;Ljava/util/Set<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableList<TE;>;Ljava/util/RandomAccess;
+C <E:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableSet<TE;>;Ljava/util/SortedSet<TE;>;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/HashSet<TE;>;Ljava/util/Set<TE;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <E:Ljava/lang/Object;>Ljava/util/SubList<TE;>;Ljava/util/RandomAccess;
+C <E:Ljava/lang/Object;>Ljava/util/Vector<TE;>;
+C <K:Ljava/lang/Enum<TK;>;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/io/Serializable;Ljava/lang/Cloneable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map$Entry<TK;TV;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map<TK;TV;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Map<TK;TV;>;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/ref/WeakReference<TK;>;Ljava/util/Map$Entry<TK;TV;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/io/Serializable;Ljava/lang/Cloneable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/SortedMap<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/concurrent/ConcurrentMap<TK;TV;>;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/Collections$CheckedMap<TK;TV;>;Ljava/util/SortedMap<TK;TV;>;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/Collections$SynchronizedMap<TK;TV;>;Ljava/util/SortedMap<TK;TV;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableMap<TK;TV;>;Ljava/util/SortedMap<TK;TV;>;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/Collections$UnmodifiableSet<Ljava/util/Map$Entry<TK;TV;>;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/Dictionary<TK;TV;>;Ljava/util/Map<TK;TV;>;Ljava/lang/Cloneable;Ljava/io/Serializable;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/HashMap$Entry<TK;TV;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/HashMap<TK;TV;>;Ljava/util/Map<TK;TV;>;
+C <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/locks/ReentrantLock;Ljava/io/Serializable;
+C <N:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;
+C <S::Lsun/reflect/generics/tree/Signature;>Lsun/reflect/generics/repository/AbstractRepository<TS;>;
+C <T::Lsun/reflect/generics/tree/Tree;>Ljava/lang/Object;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/reflect/GenericDeclaration;Ljava/lang/reflect/Type;Ljava/lang/reflect/AnnotatedElement;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/Comparable<Ljava/lang/Object;>;>;Ljava/io/Serializable;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Comparator<TT;>;Ljava/io/Serializable;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Enumeration<TT;>;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Enumeration<TT;>;Ljava/util/Iterator<TT;>;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/Iterator<TT;>;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/concurrent/Callable<TT;>;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;Lsun/reflect/generics/visitor/TypeTreeVisitor<TT;>;
+C <T:Ljava/lang/Object;>Ljava/lang/ThreadLocal<TT;>;
+C <T:Ljava/lang/Object;>Ljava/lang/ref/Reference<TT;>;
+C <T:Ljava/lang/Object;>Ljava/lang/reflect/AccessibleObject;Ljava/lang/reflect/GenericDeclaration;Ljava/lang/reflect/Member;
+C <T:Ljava/lang/Object;>Ljava/util/AbstractSet<TT;>;
+C <T:Ljava/lang/Object;>Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater<TT;>;
+C <T:Ljava/lang/Object;>Ljava/util/concurrent/atomic/AtomicLongFieldUpdater<TT;>;
+C <T:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;
+C <T:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<TT;TV;>;
+C <V:Ljava/lang/Object;>Ljava/lang/Object;
+C <V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/io/Serializable;
+C <V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/concurrent/CompletionService<TV;>;
+C <V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/concurrent/Delayed;Ljava/util/concurrent/Future<TV;>;
+C <V:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/concurrent/Future<TV;>;Ljava/lang/Runnable;
+C <V:Ljava/lang/Object;>Ljava/util/AbstractMap<Ljava/lang/String;TV;>;
+C <V:Ljava/lang/Object;>Ljava/util/concurrent/FutureTask<TV;>;Ljava/util/concurrent/ScheduledFuture<TV;>;
+C Ljava/lang/Enum<Ljava/lang/Thread$State;>;
+C Ljava/lang/Enum<Ljava/lang/annotation/ElementType;>;
+C Ljava/lang/Enum<Ljava/lang/annotation/RetentionPolicy;>;
+C Ljava/lang/Enum<Ljava/lang/management/MemoryType;>;
+C Ljava/lang/Enum<Ljava/math/RoundingMode;>;
+C Ljava/lang/Enum<Ljava/net/Authenticator$RequestorType;>;
+C Ljava/lang/Enum<Ljava/net/Proxy$Type;>;
+C Ljava/lang/Enum<Ljava/security/KeyRep$Type;>;
+C Ljava/lang/Enum<Ljava/util/Formatter$BigDecimalLayoutForm;>;
+C Ljava/lang/Enum<Ljava/util/concurrent/TimeUnit;>;
+C Ljava/lang/Enum<Ljavax/swing/JTable$PrintMode;>;
+C Ljava/lang/Enum<Ljavax/swing/text/html/FormSubmitEvent$MethodType;>;
+C Ljava/lang/Enum<Lsun/awt/shell/Win32ShellFolder2$SystemIcon;>;
+C Ljava/lang/Enum<Lsun/misc/FormattedFloatingDecimal$Form;>;
+C Ljava/lang/Enum<Lsun/net/ProgressSource$State;>;
+C Ljava/lang/Enum<Lsun/net/www/protocol/http/AuthCacheValue$Type;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Byte;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Double;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Float;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Integer;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Long;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/lang/Short;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/math/BigDecimal;>;
+C Ljava/lang/Number;Ljava/lang/Comparable<Ljava/math/BigInteger;>;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;Ljava/lang/Comparable<Ljava/util/Calendar;>;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Cloneable;Ljava/lang/Comparable<Ljava/util/Date;>;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/io/File;>;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Boolean;>;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Character;>;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Object;>;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/String;>;Ljava/lang/CharSequence;
+C Ljava/lang/Object;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/util/UUID;>;
+C Ljava/lang/Object;Ljava/lang/Cloneable;Ljava/io/Serializable;Ljava/lang/Comparable<Ljava/lang/Object;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ljava/lang/Object;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ljava/net/URI;>;Ljava/io/Serializable;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ljava/nio/charset/Charset;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ljava/text/CollationKey;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ljava/util/Collections$SelfComparable;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ljava/util/concurrent/Delayed;>;
+C Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/lang/String;>;
+C Ljava/lang/Object;Ljava/security/PrivilegedAction<Ljava/util/prefs/PreferencesFactory;>;
+C Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/Object;>;Ljava/lang/Cloneable;
+C Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;
+C Ljava/lang/Object;Ljava/util/Comparator<Ljava/lang/String;>;Ljava/io/Serializable;
+C Ljava/lang/Object;Ljava/util/Comparator<Ljava/security/cert/X509Certificate;>;
+C Ljava/lang/Object;Ljava/util/Comparator<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;
+C Ljava/lang/Object;Ljava/util/Comparator<Lsun/security/x509/AVA;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/lang/Object;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/lang/String;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/net/InetAddress;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/net/NetworkInterface;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/net/URL;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/security/Permission;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljava/util/zip/ZipEntry;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljavax/security/sasl/SaslClientFactory;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljavax/security/sasl/SaslServerFactory;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljavax/swing/tree/TreeNode;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<Ljavax/swing/tree/TreePath;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<TE;>;
+C Ljava/lang/Object;Ljava/util/Enumeration<TT;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/lang/Object;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/lang/Runnable;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/lang/String;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/nio/charset/Charset;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/security/Provider$Service;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/util/Map$Entry<TK;TV;>;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljavax/imageio/ImageReader;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljavax/imageio/ImageTranscoder;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljavax/imageio/ImageWriter;>;
+C Ljava/lang/Object;Ljava/util/Iterator<TE;>;
+C Ljava/lang/Object;Ljava/util/Iterator<TK;>;
+C Ljava/lang/Object;Ljava/util/Iterator<TV;>;
+C Ljava/lang/Object;Ljava/util/ListIterator<TE;>;
+C Ljava/lang/Object;Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;
+C Ljava/lang/Object;Ljava/util/Map$Entry<Ljava/lang/String;TV;>;
+C Ljava/lang/Object;Ljava/util/Map<Ljava/lang/Object;Ljava/lang/Object;>;Ljava/lang/Cloneable;
+C Ljava/lang/Object;Ljava/util/Set<TE;>;
+C Ljava/lang/Object;Ljava/util/concurrent/Callable<Ljava/lang/Object;>;
+C Ljava/lang/Object;Ljavax/naming/NamingEnumeration<Ljava/lang/Object;>;
+C Ljava/lang/Object;Ljavax/naming/NamingEnumeration<Ljava/lang/String;>;
+C Ljava/lang/Object;Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/Attribute;>;
+C Ljava/lang/Object;Lsun/reflect/generics/visitor/TypeTreeVisitor<Ljava/lang/reflect/Type;>;
+C Ljava/lang/Object;Lsun/security/x509/CertAttrSet<Lsun/security/x509/Extension;>;
+C Ljava/lang/ThreadLocal<Ljava/lang/Long;>;
+C Ljava/lang/ThreadLocal<Lsun/java2d/opengl/OGLContext;>;
+C Ljava/lang/ref/WeakReference<Ljava/lang/ThreadLocal;>;
+C Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/ByteBuffer;>;
+C Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/CharBuffer;>;Ljava/lang/Appendable;Ljava/lang/CharSequence;Ljava/lang/Readable;
+C Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/DoubleBuffer;>;
+C Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/FloatBuffer;>;
+C Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/IntBuffer;>;
+C Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/LongBuffer;>;
+C Ljava/nio/Buffer;Ljava/lang/Comparable<Ljava/nio/ShortBuffer;>;
+C Ljava/util/AbstractCollection<Ljava/lang/Runnable;>;Ljava/util/concurrent/BlockingQueue<Ljava/lang/Runnable;>;
+C Ljava/util/AbstractCollection<Ljava/lang/String;>;
+C Ljava/util/AbstractCollection<TV;>;
+C Ljava/util/AbstractList<Ljava/lang/Object;>;Ljava/util/RandomAccess;Ljava/io/Serializable;
+C Ljava/util/AbstractList<Ljava/security/Provider$Service;>;
+C Ljava/util/AbstractList<Ljava/security/Provider;>;
+C Ljava/util/AbstractList<TE;>.Itr;Ljava/util/ListIterator<TE;>;
+C Ljava/util/AbstractMap<Ljava/lang/Object;Ljava/lang/Object;>;Ljava/io/Serializable;
+C Ljava/util/AbstractMap<Ljava/text/AttributedCharacterIterator$Attribute;Ljava/lang/Object;>;
+C Ljava/util/AbstractMap<TK;TV;>;Ljava/util/SortedMap<TK;TV;>;Ljava/io/Serializable;
+C Ljava/util/AbstractSet<Ljava/lang/Object;>;Ljava/io/Serializable;
+C Ljava/util/AbstractSet<Ljava/lang/String;>;
+C Ljava/util/AbstractSet<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;
+C Ljava/util/AbstractSet<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;
+C Ljava/util/AbstractSet<Ljava/util/Map$Entry<TK;TV;>;>;
+C Ljava/util/AbstractSet<Ljavax/print/attribute/standard/PrinterStateReason;>;
+C Ljava/util/AbstractSet<TK;>;
+C Ljava/util/EnumMap<TK;TV;>.EnumMapIterator<Ljava/util/Map$Entry<TK;TV;>;>;Ljava/util/Map$Entry<TK;TV;>;
+C Ljava/util/EnumMap<TK;TV;>.EnumMapIterator<TK;>;
+C Ljava/util/EnumMap<TK;TV;>.EnumMapIterator<TV;>;
+C Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;
+C Ljava/util/HashMap<Ljavax/print/attribute/standard/PrinterStateReason;Ljavax/print/attribute/standard/Severity;>;Ljavax/print/attribute/PrintServiceAttribute;
+C Ljava/util/HashMap<TK;TV;>.HashIterator<Ljava/util/Map$Entry<TK;TV;>;>;
+C Ljava/util/HashMap<TK;TV;>.HashIterator<TK;>;
+C Ljava/util/HashMap<TK;TV;>.HashIterator<TV;>;
+C Ljava/util/HashSet<Ljavax/print/attribute/standard/JobStateReason;>;Ljavax/print/attribute/PrintJobAttribute;
+C Ljava/util/Hashtable<Ljava/lang/Object;Ljava/lang/Object;>;
+C Ljava/util/IdentityHashMap<TK;TV;>.IdentityHashMapIterator<Ljava/util/Map$Entry<TK;TV;>;>;Ljava/util/Map$Entry<TK;TV;>;
+C Ljava/util/IdentityHashMap<TK;TV;>.IdentityHashMapIterator<TK;>;
+C Ljava/util/IdentityHashMap<TK;TV;>.IdentityHashMapIterator<TV;>;
+C Ljava/util/LinkedHashMap<TK;TV;>.LinkedHashIterator<Ljava/util/Map$Entry<TK;TV;>;>;
+C Ljava/util/LinkedHashMap<TK;TV;>.LinkedHashIterator<TK;>;
+C Ljava/util/LinkedHashMap<TK;TV;>.LinkedHashIterator<TV;>;
+C Ljava/util/TreeMap<TK;TV;>.PrivateEntryIterator<Ljava/util/Map$Entry<TK;TV;>;>;
+C Ljava/util/TreeMap<TK;TV;>.PrivateEntryIterator<TK;>;
+C Ljava/util/TreeMap<TK;TV;>.PrivateEntryIterator<TV;>;
+C Ljava/util/Vector<Ljava/lang/String;>;
+C Ljava/util/WeakHashMap<TK;TV;>.HashIterator<Ljava/util/Map$Entry<TK;TV;>;>;
+C Ljava/util/WeakHashMap<TK;TV;>.HashIterator<TK;>;
+C Ljava/util/WeakHashMap<TK;TV;>.HashIterator<TV;>;
+C Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>.HashIterator;Ljava/util/Iterator<TK;>;Ljava/util/Enumeration<TK;>;
+C Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>.HashIterator;Ljava/util/Iterator<TV;>;Ljava/util/Enumeration<TV;>;
+C Ljava/util/concurrent/ConcurrentHashMap<TK;TV;>.HashIterator;Ljava/util/Map$Entry<TK;TV;>;Ljava/util/Iterator<Ljava/util/Map$Entry<TK;TV;>;>;
+C Ljava/util/concurrent/FutureTask<TV;>;
+C Lsun/misc/LRUCache<Ljava/lang/String;Ljava/util/regex/Pattern;>;
+C Lsun/reflect/generics/repository/AbstractRepository<Lsun/reflect/generics/tree/TypeSignature;>;
+C Lsun/reflect/generics/repository/GenericDeclRepository<Lsun/reflect/generics/tree/ClassSignature;>;
+C Lsun/reflect/generics/repository/GenericDeclRepository<Lsun/reflect/generics/tree/MethodTypeSignature;>;
+C Lsun/reflect/generics/scope/AbstractScope<Ljava/lang/Class<*>;>;Lsun/reflect/generics/scope/Scope;
+C Lsun/reflect/generics/scope/AbstractScope<Ljava/lang/reflect/Constructor;>;
+C Lsun/reflect/generics/scope/AbstractScope<Ljava/lang/reflect/Method;>;
+C Lsun/util/PreHashedMap<Ljava/lang/String;>;
+C Lsun/util/PreHashedMap<Ljava/nio/charset/Charset;>;
+M ()V^TE;
+M ()V^TE;^TF;
+M ()Ljava/lang/Class<*>;
+M ()Ljava/lang/Class<+Ljava/lang/Enum;>;
+M ()Ljava/lang/Class<+Ljava/lang/Object;>;
+M ()Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;
+M ()Ljava/lang/Class<+Ljavax/print/attribute/Attribute;>;
+M ()Ljava/lang/Class<-TT;>;
+M ()Ljava/lang/Class<TE;>;
+M ()Ljava/lang/Class<TT;>;
+M ()Ljava/lang/ref/Reference<+TT;>;
+M ()Ljava/lang/reflect/Constructor<*>;
+M ()Ljava/lang/reflect/Constructor<TT;>;
+M ()Ljava/util/ArrayList<Lsun/net/ProgressSource;>;
+M ()Ljava/util/Collection<*>;
+M ()Ljava/util/Collection<Ljava/lang/Object;>;
+M ()Ljava/util/Collection<Ljava/lang/String;>;
+M ()Ljava/util/Collection<Ljava/lang/Thread;>;
+M ()Ljava/util/Collection<Ljava/util/List<*>;>;
+M ()Ljava/util/Collection<Ljavax/security/auth/x500/X500Principal;>;
+M ()Ljava/util/Collection<Lsun/security/x509/Extension;>;
+M ()Ljava/util/Collection<TV;>;
+M ()Ljava/util/Comparator<-Ljava/awt/Component;>;
+M ()Ljava/util/Comparator<-TE;>;
+M ()Ljava/util/Comparator<-TK;>;
+M ()Ljava/util/Comparator<Lsun/security/x509/AVA;>;
+M ()Ljava/util/Dictionary<Ljava/lang/Object;Ljava/lang/Object;>;
+M ()Ljava/util/EnumMap<TK;TV;>;
+M ()Ljava/util/EnumSet<TE;>;
+M ()Ljava/util/Enumeration<*>;
+M ()Ljava/util/Enumeration<+Ljava/security/Principal;>;
+M ()Ljava/util/Enumeration<+Ljava/util/zip/ZipEntry;>;
+M ()Ljava/util/Enumeration<Ljava/applet/Applet;>;
+M ()Ljava/util/Enumeration<Ljava/awt/MenuShortcut;>;
+M ()Ljava/util/Enumeration<Ljava/lang/String;>;
+M ()Ljava/util/Enumeration<Ljava/net/InetAddress;>;
+M ()Ljava/util/Enumeration<Ljava/net/NetworkInterface;>;
+M ()Ljava/util/Enumeration<Ljava/security/Identity;>;
+M ()Ljava/util/Enumeration<Ljava/security/Permission;>;
+M ()Ljava/util/Enumeration<Ljava/security/acl/AclEntry;>;
+M ()Ljava/util/Enumeration<Ljava/security/acl/Permission;>;
+M ()Ljava/util/Enumeration<Ljava/sql/Driver;>;
+M ()Ljava/util/Enumeration<Ljava/util/jar/JarEntry;>;
+M ()Ljava/util/Enumeration<Ljavax/naming/RefAddr;>;
+M ()Ljava/util/Enumeration<Ljavax/security/sasl/SaslClientFactory;>;
+M ()Ljava/util/Enumeration<Ljavax/security/sasl/SaslServerFactory;>;
+M ()Ljava/util/Enumeration<Ljavax/sql/rowset/spi/SyncProvider;>;
+M ()Ljava/util/Enumeration<Ljavax/swing/AbstractButton;>;
+M ()Ljava/util/Enumeration<Ljavax/swing/table/TableColumn;>;
+M ()Ljava/util/Enumeration<Lsun/security/x509/Extension;>;
+M ()Ljava/util/Enumeration<TE;>;
+M ()Ljava/util/Enumeration<TK;>;
+M ()Ljava/util/Enumeration<TT;>;
+M ()Ljava/util/Enumeration<TV;>;
+M ()Ljava/util/HashMap$Entry<TK;TV;>;
+M ()Ljava/util/HashMap<Ljava/lang/String;Ljavax/swing/text/Keymap;>;
+M ()Ljava/util/HashSet<Ljava/lang/String;>;
+M ()Ljava/util/Hashtable<**>;
+M ()Ljava/util/Hashtable<Ljava/awt/Component;Ljava/lang/Integer;>;
+M ()Ljava/util/Iterator<+Ljava/security/cert/PolicyNode;>;
+M ()Ljava/util/Iterator<Ljava/awt/event/InputEvent;>;
+M ()Ljava/util/Iterator<Ljava/lang/Class<*>;>;
+M ()Ljava/util/Iterator<Ljava/lang/Object;>;
+M ()Ljava/util/Iterator<Ljava/lang/Runnable;>;
+M ()Ljava/util/Iterator<Ljava/lang/String;>;
+M ()Ljava/util/Iterator<Ljava/nio/charset/Charset;>;
+M ()Ljava/util/Iterator<Ljava/security/Provider$Service;>;
+M ()Ljava/util/Iterator<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;
+M ()Ljava/util/Iterator<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;
+M ()Ljava/util/Iterator<Ljava/util/Map$Entry<TK;TV;>;>;
+M ()Ljava/util/Iterator<Lsun/security/provider/certpath/PolicyNodeImpl;>;
+M ()Ljava/util/Iterator<Lsun/security/x509/GeneralName;>;
+M ()Ljava/util/Iterator<Lsun/security/x509/GeneralSubtree;>;
+M ()Ljava/util/Iterator<TE;>;
+M ()Ljava/util/Iterator<TK;>;
+M ()Ljava/util/Iterator<TT;>;
+M ()Ljava/util/Iterator<TV;>;
+M ()Ljava/util/LinkedHashMap$Entry<TK;TV;>;
+M ()Ljava/util/List<*>;
+M ()Ljava/util/List<+Ljava/awt/image/BufferedImage;>;
+M ()Ljava/util/List<+Ljava/security/cert/Certificate;>;
+M ()Ljava/util/List<Ljava/awt/KeyEventDispatcher;>;
+M ()Ljava/util/List<Ljava/awt/KeyEventPostProcessor;>;
+M ()Ljava/util/List<Ljava/awt/datatransfer/DataFlavor;>;
+M ()Ljava/util/List<Ljava/lang/Runnable;>;
+M ()Ljava/util/List<Ljava/lang/String;>;
+M ()Ljava/util/List<Ljava/lang/management/GarbageCollectorMXBean;>;
+M ()Ljava/util/List<Ljava/lang/management/MemoryManagerMXBean;>;
+M ()Ljava/util/List<Ljava/lang/management/MemoryPoolMXBean;>;
+M ()Ljava/util/List<Ljava/security/Provider;>;
+M ()Ljava/util/List<Ljava/security/cert/CertStore;>;
+M ()Ljava/util/List<Ljava/security/cert/Certificate;>;
+M ()Ljava/util/List<Ljava/security/cert/PKIXCertPathChecker;>;
+M ()Ljava/util/List<Ljava/security/cert/X509Certificate;>;
+M ()Ljava/util/List<Ljavax/naming/ldap/Rdn;>;
+M ()Ljava/util/List<Ljavax/sound/midi/Receiver;>;
+M ()Ljava/util/List<Ljavax/sound/midi/Transmitter;>;
+M ()Ljava/util/List<Lsun/management/CompilerThreadStat;>;
+M ()Ljava/util/List<Lsun/management/Flag;>;
+M ()Ljava/util/List<Lsun/management/counter/Counter;>;
+M ()Ljava/util/List<Lsun/reflect/generics/tree/SimpleClassTypeSignature;>;
+M ()Ljava/util/List<Lsun/security/x509/AVA;>;
+M ()Ljava/util/List<Lsun/security/x509/CertificatePolicyId;>;
+M ()Ljava/util/List<Lsun/security/x509/GeneralName;>;
+M ()Ljava/util/List<Lsun/security/x509/GeneralSubtree;>;
+M ()Ljava/util/List<Lsun/security/x509/RDN;>;
+M ()Ljava/util/ListIterator<TE;>;
+M ()Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;
+M ()Ljava/util/Map$Entry<Ljava/lang/String;TV;>;
+M ()Ljava/util/Map$Entry<TK;TV;>;
+M ()Ljava/util/Map<Ljava/awt/font/TextAttribute;*>;
+M ()Ljava/util/Map<Ljava/lang/Class;Ljava/lang/annotation/Annotation;>;
+M ()Ljava/util/Map<Ljava/lang/String;*>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Long;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/management/MemoryUsage;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/lang/reflect/Method;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ljava/util/jar/Attributes;>;
+M ()Ljava/util/Map<Ljava/lang/String;TT;>;
+M ()Ljava/util/Map<Ljava/lang/Thread;[Ljava/lang/StackTraceElement;>;
+M ()Ljava/util/Map<Ljava/text/AttributedCharacterIterator$Attribute;Ljava/lang/Object;>;
+M ()Ljava/util/Map<Ljava/util/Set<Ljava/lang/String;>;Ljava/lang/ClassLoader;>;
+M ()Ljava/util/Set<+Ljava/security/cert/PolicyQualifierInfo;>;
+M ()Ljava/util/Set<+Ljava/security/cert/X509CRLEntry;>;
+M ()Ljava/util/Set<Ljava/lang/Object;>;
+M ()Ljava/util/Set<Ljava/lang/String;>;
+M ()Ljava/util/Set<Ljava/nio/channels/SelectionKey;>;
+M ()Ljava/util/Set<Ljava/security/Principal;>;
+M ()Ljava/util/Set<Ljava/security/Provider$Service;>;
+M ()Ljava/util/Set<Ljava/security/cert/PolicyQualifierInfo;>;
+M ()Ljava/util/Set<Ljava/security/cert/TrustAnchor;>;
+M ()Ljava/util/Set<Ljava/security/cert/X509CRLEntry;>;
+M ()Ljava/util/Set<Ljava/text/AttributedCharacterIterator$Attribute;>;
+M ()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/Object;Ljava/lang/Object;>;>;
+M ()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;
+M ()Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;TV;>;>;
+M ()Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;
+M ()Ljava/util/Set<Ljavax/swing/KeyStroke;>;
+M ()Ljava/util/Set<Lsun/security/x509/AccessDescription;>;
+M ()Ljava/util/Set<TK;>;
+M ()Ljava/util/SortedMap<Ljava/lang/String;Ljava/lang/String;>;
+M ()Ljava/util/SortedMap<Ljava/lang/String;Ljava/nio/charset/Charset;>;
+M ()Ljava/util/TreeMap$Entry<TK;TV;>;
+M ()Ljava/util/Vector<Ljava/awt/image/RenderedImage;>;
+M ()Ljava/util/Vector<Ljava/awt/image/renderable/RenderableImage;>;
+M ()Ljava/util/Vector<Ljava/io/File;>;
+M ()Ljava/util/Vector<Ljava/lang/Object;>;
+M ()Ljava/util/WeakHashMap$Entry<TK;TV;>;
+M ()Ljava/util/concurrent/BlockingQueue<Ljava/lang/Runnable;>;
+M ()Ljava/util/concurrent/ConcurrentHashMap$HashEntry<TK;TV;>;
+M ()Ljava/util/concurrent/ConcurrentLinkedQueue$Node<TE;>;
+M ()Ljava/util/concurrent/Future<TV;>;
+M ()Ljavax/naming/NamingEnumeration<*>;
+M ()Ljavax/naming/NamingEnumeration<+Ljavax/naming/directory/Attribute;>;
+M ()Ljavax/naming/NamingEnumeration<Ljava/lang/String;>;
+M ()Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/Attribute;>;
+M ()TD;
+M ()TE;
+M ()TK;
+M ()TT;
+M ()TV;
+M ()V
+M ()[Ljava/lang/Class<*>;
+M ()[Ljava/lang/reflect/TypeVariable<*>;
+M ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/Class<TT;>;>;
+M ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/reflect/Constructor<TT;>;>;
+M ()[Ljava/lang/reflect/TypeVariable<Ljava/lang/reflect/Method;>;
+M ()[TE;
+M ()[TK;
+M ()[TT;
+M ()[TV;
+M (CLjava/lang/Class<*>;)V
+M (I)Ljava/lang/Class<*>;
+M (I)Ljava/util/Collection<*>;
+M (I)Ljava/util/Iterator<Ljavax/imageio/ImageTypeSpecifier;>;
+M (I)Ljava/util/LinkedList$Entry<TE;>;
+M (I)Ljava/util/ListIterator<TE;>;
+M (I)Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;
+M (I)Ljava/util/Set<Ljava/awt/AWTKeyStroke;>;
+M (I)Ljava/util/concurrent/ConcurrentHashMap$HashEntry<TK;TV;>;
+M (I)Ljava/util/concurrent/ConcurrentHashMap$Segment<TK;TV;>;
+M (I)TE;
+M (I)V
+M (IFIIJLjava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)V
+M (II)Ljava/util/List<TE;>;
+M (IIIILjava/util/Iterator;Ljava/io/ObjectInputStream;TV;)Ljava/util/TreeMap$Entry<TK;TV;>;
+M (IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue<Ljava/lang/Runnable;>;)V
+M (IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue<Ljava/lang/Runnable;>;Ljava/util/concurrent/RejectedExecutionHandler;)V
+M (IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue<Ljava/lang/Runnable;>;Ljava/util/concurrent/ThreadFactory;)V
+M (IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue<Ljava/lang/Runnable;>;Ljava/util/concurrent/ThreadFactory;Ljava/util/concurrent/RejectedExecutionHandler;)V
+M (IILjava/awt/image/ColorModel;[BIILjava/util/Hashtable<**>;)V
+M (IILjava/awt/image/ColorModel;[IIILjava/util/Hashtable<**>;)V
+M (II[IIILjava/util/Hashtable<**>;)V
+M (ILjava/io/ObjectInputStream;TV;)V
+M (ILjava/lang/String;Ljava/util/Set<Ljava/lang/String;>;)Ljavax/imageio/metadata/IIOMetadata;
+M (ILjava/rmi/server/RMIClientSocketFactory;Ljava/rmi/server/RMIServerSocketFactory;Ljava/util/Map<Ljava/lang/String;*>;)V
+M (ILjava/util/Collection<+TE;>;)Z
+M (ILjava/util/Comparator<-TE;>;)V
+M (ILjava/util/Iterator;Ljava/io/ObjectInputStream;TV;)V
+M (ILjava/util/List<Ljavax/naming/ldap/Rdn;>;)Ljavax/naming/Name;
+M (ILjava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)Ljava/lang/Object;
+M (ILjava/util/Set<+Ljava/awt/AWTKeyStroke;>;)V
+M (ILjavax/imageio/ImageTypeSpecifier;IILjavax/imageio/metadata/IIOMetadata;Ljava/util/List<+Ljava/awt/image/BufferedImage;>;Ljavax/imageio/ImageWriteParam;)V
+M (ITE;)TE;
+M (ITE;)V
+M (ITE;TE;)Z
+M (ITK;TV;I)V
+M (ITK;TV;Ljava/util/HashMap$Entry<TK;TV;>;)V
+M (ITK;TV;Ljava/util/Hashtable$Entry<TK;TV;>;)V
+M (IZLjava/util/Collection<+TE;>;)V
+M (IZZZLjava/util/List<Ljava/security/cert/PKIXCertPathChecker;>;)V
+M (J)Ljava/lang/ref/Reference<+TT;>;
+M (J)TV;
+M (JILjava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)Ljava/lang/Object;
+M (JILjava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)Ljava/sql/ResultSet;
+M (JLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/Future<TV;>;
+M (JLjava/util/concurrent/TimeUnit;)TE;
+M (JLjava/util/concurrent/TimeUnit;)TV;
+M (Ljava/awt/datatransfer/DataFlavor;)Ljava/util/List<Ljava/lang/String;>;
+M (Ljava/awt/dnd/DragGestureRecognizer;ILjava/awt/Point;Ljava/util/List<+Ljava/awt/event/InputEvent;>;)V
+M (Ljava/awt/im/InputMethodHighlight;)Ljava/util/Map<Ljava/awt/font/TextAttribute;*>;
+M (Ljava/awt/image/ColorModel;Ljava/awt/image/WritableRaster;ZLjava/util/Hashtable<**>;)V
+M (Ljava/awt/image/Raster;Ljava/util/List<+Ljava/awt/image/BufferedImage;>;Ljavax/imageio/metadata/IIOMetadata;)V
+M (Ljava/awt/image/RenderedImage;Ljava/util/List<+Ljava/awt/image/BufferedImage;>;Ljavax/imageio/metadata/IIOMetadata;)V
+M (Ljava/beans/Encoder;Ljava/lang/Class<*>;)V
+M (Ljava/io/InputStream;)Ljava/util/Collection<+Ljava/security/cert/CRL;>;
+M (Ljava/io/InputStream;)Ljava/util/Collection<+Ljava/security/cert/Certificate;>;
+M (Ljava/io/InputStream;Ljava/lang/Class<*>;)V
+M (Ljava/io/InputStream;Ljava/util/Map<Ljava/util/Set<Ljava/lang/String;>;Ljava/lang/ClassLoader;>;)V
+M (Ljava/io/ObjectStreamClass;)Ljava/lang/Class<*>;
+M (Ljava/lang/Boolean;Ljava/util/List<Lsun/security/x509/PolicyInformation;>;)V
+M (Ljava/lang/Boolean;Ljava/util/Vector<Lsun/security/util/ObjectIdentifier;>;)V
+M (Ljava/lang/Class;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)Ljava/lang/annotation/Annotation;
+M (Ljava/lang/Class;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)V
+M (Ljava/lang/Class<*>;)I
+M (Ljava/lang/Class<*>;)Ljava/beans/BeanInfo;
+M (Ljava/lang/Class<*>;)Ljava/beans/PersistenceDelegate;
+M (Ljava/lang/Class<*>;)Ljava/beans/PropertyEditor;
+M (Ljava/lang/Class<*>;)Ljava/io/ObjectStreamClass;
+M (Ljava/lang/Class<*>;)Ljava/lang/String;
+M (Ljava/lang/Class<*>;)Ljava/util/prefs/Preferences;
+M (Ljava/lang/Class<*>;)Ljavax/print/attribute/Attribute;
+M (Ljava/lang/Class<*>;)Ljavax/swing/table/TableCellEditor;
+M (Ljava/lang/Class<*>;)Ljavax/swing/table/TableCellRenderer;
+M (Ljava/lang/Class<*>;)Lsun/reflect/generics/scope/ClassScope;
+M (Ljava/lang/Class<*>;)V
+M (Ljava/lang/Class<*>;)Z
+M (Ljava/lang/Class<*>;I)Ljava/beans/BeanInfo;
+M (Ljava/lang/Class<*>;I)Ljava/lang/Object;
+M (Ljava/lang/Class<*>;I)V
+M (Ljava/lang/Class<*>;Ljava/awt/Component;)Ljava/awt/Container;
+M (Ljava/lang/Class<*>;Ljava/beans/PersistenceDelegate;)V
+M (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)Ljava/beans/BeanInfo;
+M (Ljava/lang/Class<*>;Ljava/lang/Class<*>;)V
+M (Ljava/lang/Class<*>;Ljava/lang/Object;Ljava/lang/Object;Ljava/beans/Encoder;)V
+M (Ljava/lang/Class<*>;Ljava/lang/String;)Ljava/lang/Object;
+M (Ljava/lang/Class<*>;Ljava/lang/String;)V
+M (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;Ljava/lang/String;)V
+M (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+M (Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/Class<*>;[Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+M (Ljava/lang/Class<*>;Ljava/lang/String;Z)V
+M (Ljava/lang/Class<*>;Ljavax/print/attribute/Attribute;)V
+M (Ljava/lang/Class<*>;Ljavax/sound/sampled/AudioFormat;)V
+M (Ljava/lang/Class<*>;Ljavax/sound/sampled/AudioFormat;I)V
+M (Ljava/lang/Class<*>;Ljavax/swing/table/TableCellEditor;)V
+M (Ljava/lang/Class<*>;Ljavax/swing/table/TableCellRenderer;)V
+M (Ljava/lang/Class<*>;[B)V
+M (Ljava/lang/Class<*>;[I)Ljava/lang/Object;
+M (Ljava/lang/Class<*>;[Ljava/lang/Object;)V
+M (Ljava/lang/Class<*>;[Ljava/lang/reflect/Type;Ljava/lang/reflect/Type;)Lsun/reflect/generics/reflectiveObjects/ParameterizedTypeImpl;
+M (Ljava/lang/Class<*>;[Ljava/lang/reflect/Type;Ljava/lang/reflect/Type;)V
+M (Ljava/lang/Class<*>;[Ljavax/sound/sampled/AudioFormat;II)V
+M (Ljava/lang/Class<+Ljava/lang/Enum;>;Ljava/lang/String;)V
+M (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;)Z
+M (Ljava/lang/Class<+Ljava/lang/annotation/Annotation;>;Ljava/lang/String;)V
+M (Ljava/lang/Class<+Ljavax/print/attribute/Attribute;>;)Ljava/lang/Object;
+M (Ljava/lang/Class<+Ljavax/print/attribute/Attribute;>;)Z
+M (Ljava/lang/Class<+Ljavax/print/attribute/Attribute;>;Ljavax/print/DocFlavor;Ljavax/print/attribute/AttributeSet;)Ljava/lang/Object;
+M (Ljava/lang/Class<TE;>;[Ljava/lang/Enum;)V
+M (Ljava/lang/Class<TK;>;)V
+M (Ljava/lang/Class<TT;>;Ljava/lang/Class<TV;>;Ljava/lang/String;)V
+M (Ljava/lang/Class<TT;>;Ljava/lang/String;)V
+M (Ljava/lang/Class<TT;>;[Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B)V
+M (Ljava/lang/ClassLoader;Ljava/lang/String;Ljava/lang/Class<*>;Ljava/security/ProtectionDomain;[B)[B
+M (Ljava/lang/ClassLoader;[Ljava/lang/Class<*>;)Ljava/lang/Class<*>;
+M (Ljava/lang/ClassLoader;[Ljava/lang/Class<*>;Ljava/lang/reflect/InvocationHandler;)Ljava/lang/Object;
+M (Ljava/lang/Comparable<Ljava/lang/Object;>;Ljava/lang/Comparable<Ljava/lang/Object;>;)I
+M (Ljava/lang/Object;)Ljava/util/HashMap$Entry<TK;TV;>;
+M (Ljava/lang/Object;)Ljava/util/Iterator<Ljavax/imageio/ImageReader;>;
+M (Ljava/lang/Object;)Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;
+M (Ljava/lang/Object;)Ljava/util/TreeMap$Entry<TK;TV;>;
+M (Ljava/lang/Object;)Ljava/util/WeakHashMap$Entry<TK;TV;>;
+M (Ljava/lang/Object;)TT;
+M (Ljava/lang/Object;)TV;
+M (Ljava/lang/Object;I)TV;
+M (Ljava/lang/Object;ILjava/lang/Object;)TV;
+M (Ljava/lang/Object;Ljava/lang/Class<*>;)Ljava/lang/Class<*>;
+M (Ljava/lang/Object;Ljava/lang/Class<*>;)Ljava/lang/Object;
+M (Ljava/lang/Object;Ljava/lang/Class<*>;)Ljavax/print/attribute/Attribute;
+M (Ljava/lang/Object;Ljava/lang/Class<*>;)Z
+M (Ljava/lang/Object;Ljava/util/Hashtable<**>;)Ljavax/naming/spi/ObjectFactory;
+M (Ljava/lang/Object;Ljavax/naming/Name;Ljavax/naming/Context;Ljava/util/Hashtable<**>;)Ljava/lang/Object;
+M (Ljava/lang/Object;Ljavax/naming/Name;Ljavax/naming/Context;Ljava/util/Hashtable<**>;Ljavax/naming/directory/Attributes;)Ljava/lang/Object;
+M (Ljava/lang/Object;Ljavax/naming/Name;Ljavax/naming/Context;Ljava/util/Hashtable<**>;Ljavax/naming/directory/Attributes;)Ljavax/naming/spi/DirStateFactory$Result;
+M (Ljava/lang/Runnable;)Ljava/util/concurrent/Callable<Ljava/lang/Object;>;
+M (Ljava/lang/Runnable;)Ljava/util/concurrent/Future<*>;
+M (Ljava/lang/Runnable;JJLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture<*>;
+M (Ljava/lang/Runnable;JLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture<*>;
+M (Ljava/lang/Runnable;TT;)V
+M (Ljava/lang/Runnable;TV;)Ljava/util/concurrent/Future<TV;>;
+M (Ljava/lang/Runnable;TV;)V
+M (Ljava/lang/Runnable;TV;J)V
+M (Ljava/lang/Runnable;TV;JJ)V
+M (Ljava/lang/String;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;)Ljava/lang/Class<+Ljavax/swing/plaf/ComponentUI;>;
+M (Ljava/lang/String;)Ljava/lang/Comparable<*>;
+M (Ljava/lang/String;)Ljava/lang/reflect/TypeVariable<*>;
+M (Ljava/lang/String;)Ljava/util/Collection<*>;
+M (Ljava/lang/String;)Ljava/util/Enumeration<Ljava/net/URL;>;
+M (Ljava/lang/String;)Ljava/util/Iterator<Ljavax/imageio/ImageReader;>;
+M (Ljava/lang/String;)Ljava/util/Iterator<Ljavax/imageio/ImageWriter;>;
+M (Ljava/lang/String;)Ljava/util/List<Ljava/awt/datatransfer/DataFlavor;>;
+M (Ljava/lang/String;)Ljava/util/Set<Ljava/lang/String;>;
+M (Ljava/lang/String;)Ljavax/naming/NamingEnumeration<Ljavax/naming/Binding;>;
+M (Ljava/lang/String;)Ljavax/naming/NamingEnumeration<Ljavax/naming/NameClassPair;>;
+M (Ljava/lang/String;)TT;
+M (Ljava/lang/String;)V
+M (Ljava/lang/String;IILjava/lang/String;Ljava/util/Vector<*>;Ljavax/swing/text/html/parser/AttributeList;)V
+M (Ljava/lang/String;Ljava/lang/Class;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;Ljava/lang/Class<*>;)V
+M (Ljava/lang/String;Ljava/lang/Class<*>;II)V
+M (Ljava/lang/String;Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/String;)V
+M (Ljava/lang/String;Ljava/lang/Class<*>;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
+M (Ljava/lang/String;Ljava/lang/Class<*>;Z)V
+M (Ljava/lang/String;Ljava/lang/Class<*>;[Ljava/beans/MethodDescriptor;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V
+M (Ljava/lang/String;Ljava/lang/Class<*>;[Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V
+M (Ljava/lang/String;Ljava/lang/Class<*>;[Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V
+M (Ljava/lang/String;Ljava/lang/Class<+Ljava/security/KeyStore$Entry;>;)Z
+M (Ljava/lang/String;Ljava/lang/Class<+Ljavax/naming/Context;>;)Ljavax/naming/spi/ResolveResult;
+M (Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/Class<+Ljavax/swing/plaf/ComponentUI;>;
+M (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;Ljava/lang/String;)Ljava/util/List<Ljava/security/Provider$Service;>;
+M (Ljava/lang/String;Ljava/lang/String;IZLjava/lang/String;Ljava/util/List<Ljava/lang/String;>;)V
+M (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class<*>;)V
+M (Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;*>;Ljavax/security/auth/callback/CallbackHandler;)Ljavax/security/sasl/SaslServer;
+M (Ljava/lang/String;Ljava/lang/String;Ljavax/naming/directory/SearchControls;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;Ljavax/naming/directory/SearchControls;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/CodeSource;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;Ljava/nio/ByteBuffer;Ljava/security/ProtectionDomain;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;Ljava/util/Hashtable<**>;)Ljavax/naming/Context;
+M (Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;)Ljava/util/List<Ljava/security/Provider$Service;>;
+M (Ljava/lang/String;Ljava/util/Map<+Ljava/text/AttributedCharacterIterator$Attribute;*>;)V
+M (Ljava/lang/String;Ljava/util/Map<+Ljava/text/AttributedCharacterIterator$Attribute;*>;Ljava/awt/font/FontRenderContext;)V
+M (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)Ljava/lang/Object;
+M (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>;)V
+M (Ljava/lang/String;Ljava/util/Set<Ljava/lang/String;>;)Ljavax/imageio/metadata/IIOMetadata;
+M (Ljava/lang/String;Ljavax/naming/directory/Attributes;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljava/lang/String;Ljavax/naming/directory/Attributes;[Ljava/lang/String;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljava/lang/String;Ljavax/security/auth/login/AppConfigurationEntry$LoginModuleControlFlag;Ljava/util/Map<Ljava/lang/String;*>;)V
+M (Ljava/lang/String;TV;)TV;
+M (Ljava/lang/String;Z)Ljava/lang/Class<*>;
+M (Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;[BII)Ljava/lang/Class<*>;
+M (Ljava/lang/String;[BIILjava/security/CodeSource;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;[BIILjava/security/ProtectionDomain;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;[Ljava/lang/String;Ljava/lang/ClassLoader;)Ljava/lang/Class<*>;
+M (Ljava/lang/String;[Ljava/lang/String;Ljava/util/Hashtable<**>;)Lcom/sun/jndi/dns/DnsContext;
+M (Ljava/lang/String;[Lsun/reflect/generics/tree/FieldTypeSignature;)Ljava/lang/reflect/TypeVariable<*>;
+M (Ljava/lang/Thread;TT;)V
+M (Ljava/lang/ref/Reference<+TT;>;)Z
+M (Ljava/net/URI;)Ljava/util/List<Ljava/net/Proxy;>;
+M (Ljava/net/URI;Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;)Ljava/net/CacheResponse;
+M (Ljava/net/URI;Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;)Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;
+M (Ljava/net/URI;Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;>;)V
+M (Ljava/net/URL;Ljava/lang/String;)Ljava/lang/Class<*>;
+M (Ljava/security/Principal;)Ljava/util/Enumeration<Ljava/security/acl/Permission;>;
+M (Ljava/security/PrivilegedAction;)Ljava/util/concurrent/Callable<Ljava/lang/Object;>;
+M (Ljava/security/PrivilegedExceptionAction;)Ljava/util/concurrent/Callable<Ljava/lang/Object;>;
+M (Ljava/security/Provider;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;)V
+M (Ljava/security/PublicKey;Ljavax/security/auth/x500/X500Principal;Ljava/security/cert/CertPath;Ljava/util/List<Ljava/security/cert/X509Certificate;>;Ljava/security/cert/PKIXParameters;Lsun/security/provider/certpath/PolicyNodeImpl;)Ljava/security/cert/PolicyNode;
+M (Ljava/security/cert/CRLSelector;)Ljava/util/Collection<+Ljava/security/cert/CRL;>;
+M (Ljava/security/cert/CRLSelector;)Ljava/util/Collection<Ljava/security/cert/CRL;>;
+M (Ljava/security/cert/CRLSelector;)Ljava/util/Collection<Ljava/security/cert/X509CRL;>;
+M (Ljava/security/cert/CertSelector;)Ljava/util/Collection<+Ljava/security/cert/Certificate;>;
+M (Ljava/security/cert/CertSelector;)Ljava/util/Collection<Ljava/security/cert/Certificate;>;
+M (Ljava/security/cert/CertSelector;)Ljava/util/Collection<Ljava/security/cert/X509Certificate;>;
+M (Ljava/security/cert/Certificate;Ljava/util/Collection<Ljava/lang/String;>;)V
+M (Ljava/security/cert/X509CRLSelector;)Ljava/util/Collection<Ljavax/security/auth/x500/X500Principal;>;
+M (Ljava/security/cert/X509CertSelector;Ljava/util/Collection<Ljava/security/cert/CertStore;>;Ljava/util/Collection<Ljava/security/cert/X509Certificate;>;)V
+M (Ljava/security/cert/X509CertSelector;Ljava/util/Set<Lsun/security/x509/GeneralNameInterface;>;)V
+M (Ljava/security/cert/X509Certificate;)Ljava/util/Collection<Ljava/util/List<*>;>;
+M (Ljava/security/cert/X509Certificate;)Ljava/util/List<Ljava/lang/String;>;
+M (Ljava/security/cert/X509Certificate;)Ljava/util/Set<Lsun/security/x509/AccessDescription;>;
+M (Ljava/security/cert/X509Certificate;Ljava/security/PublicKey;ZLjava/util/Set<Ljava/security/cert/X509Certificate;>;)Z
+M (Ljava/security/cert/X509Certificate;Ljava/security/PublicKey;ZZLjava/util/Set<Ljava/security/cert/X509Certificate;>;)V
+M (Ljava/security/cert/X509Certificate;Ljava/util/LinkedList<Ljava/security/cert/X509Certificate;>;)V
+M (Ljava/security/cert/X509Certificate;Ljava/util/Set<Ljava/security/PublicKey;>;Ljava/util/Set<Ljava/security/cert/X509Certificate;>;)Ljava/security/PublicKey;
+M (Ljava/security/cert/X509Certificate;Lsun/security/provider/certpath/State;Ljava/util/List<Ljava/security/cert/X509Certificate;>;)V
+M (Ljava/sql/Array;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)V
+M (Ljava/sql/SQLData;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)V
+M (Ljava/sql/Struct;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)V
+M (Ljava/util/AbstractList<TE;>;II)V
+M (Ljava/util/Collection<*>;)Ljava/util/HashSet<Ljava/lang/Object;>;
+M (Ljava/util/Collection<*>;)V
+M (Ljava/util/Collection<*>;)Z
+M (Ljava/util/Collection<*>;Ljava/lang/Object;)I
+M (Ljava/util/Collection<*>;Ljava/util/Collection<*>;)Z
+M (Ljava/util/Collection<+Ljava/util/Map$Entry<TK;TV;>;>;)Z
+M (Ljava/util/Collection<+TE;>;)I
+M (Ljava/util/Collection<+TE;>;)V
+M (Ljava/util/Collection<+TE;>;)Z
+M (Ljava/util/Collection<-Ljava/lang/Runnable;>;)I
+M (Ljava/util/Collection<-Ljava/lang/Runnable;>;I)I
+M (Ljava/util/Collection<-TE;>;)I
+M (Ljava/util/Collection<-TE;>;I)I
+M (Ljava/util/Collection<Ljava/lang/Object;>;)Ljava/util/HashSet<Ljava/lang/Object;>;
+M (Ljava/util/Collection<Ljava/lang/Object;>;)Ljava/util/HashSet<Ljavax/security/auth/x500/X500Principal;>;
+M (Ljava/util/Collection<Ljava/lang/String;>;)V
+M (Ljava/util/Collection<Ljava/security/PublicKey;>;)V
+M (Ljava/util/Collection<Ljava/security/cert/X509Certificate;>;Ljava/util/List;)Ljava/util/LinkedList;
+M (Ljava/util/Collection<Ljava/util/List<*>;>;)Ljava/util/Collection<Ljava/util/List<*>;>;
+M (Ljava/util/Collection<Ljava/util/List<*>;>;)Ljava/util/Set<Ljava/util/List<*>;>;
+M (Ljava/util/Collection<Ljava/util/List<*>;>;)Ljava/util/Set<Lsun/security/x509/GeneralNameInterface;>;
+M (Ljava/util/Collection<Ljava/util/List<*>;>;)V
+M (Ljava/util/Collection<Ljavax/print/attribute/standard/JobStateReason;>;)V
+M (Ljava/util/Collection<Ljavax/security/auth/x500/X500Principal;>;)V
+M (Ljava/util/Collection<TE;>;)V
+M (Ljava/util/Collection<TE;>;Ljava/lang/Class<TE;>;)V
+M (Ljava/util/Collection<TE;>;Ljava/lang/Object;)V
+M (Ljava/util/Comparator<-Ljava/awt/Component;>;)V
+M (Ljava/util/Comparator<-TE;>;)V
+M (Ljava/util/Comparator<-TK;>;)V
+M (Ljava/util/Comparator<TT;>;)V
+M (Ljava/util/Dictionary<Ljava/lang/Object;Ljava/lang/Object;>;)V
+M (Ljava/util/EnumMap<TK;+TV;>;)V
+M (Ljava/util/EnumSet<TE;>;)V
+M (Ljava/util/Enumeration<*>;)V
+M (Ljava/util/Enumeration<+Ljava/io/InputStream;>;)V
+M (Ljava/util/Enumeration<Ljava/lang/String;>;)V
+M (Ljava/util/Enumeration<Ljava/lang/String;>;Ljava/util/Properties;)V
+M (Ljava/util/Enumeration<Ljavax/swing/tree/TreePath;>;)V
+M (Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Short;>;Ljava/lang/String;)Ljava/lang/Short;
+M (Ljava/util/HashMap<TK;TV;>;)V
+M (Ljava/util/Hashtable<**>;)Ljavax/naming/Context;
+M (Ljava/util/Hashtable<**>;)Ljavax/naming/spi/InitialContextFactory;
+M (Ljava/util/Hashtable<**>;)V
+M (Ljava/util/Hashtable<**>;[Ljavax/naming/ldap/Control;)Ljavax/naming/Context;
+M (Ljava/util/Hashtable<**>;[Ljavax/naming/ldap/Control;)V
+M (Ljava/util/Hashtable<Ljava/lang/Object;Ljava/lang/Object;>;)V
+M (Ljava/util/Iterator<*>;)V
+M (Ljava/util/Iterator<+Ljavax/imageio/ImageReadParam;>;)Ljava/util/Iterator<Ljavax/imageio/IIOImage;>;
+M (Ljava/util/Iterator<Ljava/lang/Class<*>;>;)V
+M (Ljava/util/Iterator<Ljava/security/PermissionCollection;>;)V
+M (Ljava/util/Iterator<TE;>;)V
+M (Ljava/util/Iterator<TT;>;Ljavax/imageio/spi/ServiceRegistry$Filter;)V
+M (Ljava/util/LinkedHashMap$Entry<TK;TV;>;)V
+M (Ljava/util/LinkedList$Entry<TE;>;)TE;
+M (Ljava/util/LinkedList<Ljava/security/cert/X509Certificate;>;)V
+M (Ljava/util/List;Ljava/util/LinkedList<Ljava/security/cert/X509Certificate;>;)V
+M (Ljava/util/List<*>;)V
+M (Ljava/util/List<*>;I)V
+M (Ljava/util/List<*>;II)V
+M (Ljava/util/List<*>;Ljava/util/List<*>;)I
+M (Ljava/util/List<*>;Ljava/util/Random;)V
+M (Ljava/util/List<+Ljava/awt/image/BufferedImage;>;)V
+M (Ljava/util/List<+Ljava/security/cert/Certificate;>;)Ljava/security/cert/CertPath;
+M (Ljava/util/List<+TE;>;)V
+M (Ljava/util/List<Ljava/lang/Byte;>;)Ljava/lang/String;
+M (Ljava/util/List<Ljava/lang/String;>;)Ljava/lang/ProcessBuilder;
+M (Ljava/util/List<Ljava/lang/String;>;)V
+M (Ljava/util/List<Ljava/lang/String;>;)[Ljava/lang/String;
+M (Ljava/util/List<Ljava/lang/String;>;Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;)Ljava/lang/String;
+M (Ljava/util/List<Ljava/security/cert/CertStore;>;)V
+M (Ljava/util/List<Ljava/security/cert/PKIXCertPathChecker;>;)V
+M (Ljava/util/List<Ljavax/naming/ldap/Rdn;>;)Ljavax/naming/Name;
+M (Ljava/util/List<Ljavax/naming/ldap/Rdn;>;)V
+M (Ljava/util/List<Ljavax/naming/ldap/Rdn;>;)Z
+M (Ljava/util/List<Lsun/misc/ProxyGenerator$ProxyMethod;>;)V
+M (Ljava/util/List<Lsun/reflect/generics/tree/SimpleClassTypeSignature;>;)Lsun/reflect/generics/tree/ClassTypeSignature;
+M (Ljava/util/List<Lsun/reflect/generics/tree/SimpleClassTypeSignature;>;)V
+M (Ljava/util/List<Lsun/security/jca/ServiceId;>;)Ljava/util/List<Ljava/security/Provider$Service;>;
+M (Ljava/util/List<Lsun/security/jca/ServiceId;>;)V
+M (Ljava/util/List<Lsun/security/x509/CertificatePolicyMap;>;)V
+M (Ljava/util/List<Lsun/security/x509/DistributionPoint;>;)V
+M (Ljava/util/List<Lsun/security/x509/PolicyInformation;>;)V
+M (Ljava/util/List<TE;>;)V
+M (Ljava/util/List<TE;>;III)V
+M (Ljava/util/List<TE;>;Ljava/lang/Class<TE;>;)V
+M (Ljava/util/List<TE;>;Ljava/lang/Object;)V
+M (Ljava/util/Map$Entry<+TK;+TV;>;)V
+M (Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;)V
+M (Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;)I
+M (Ljava/util/Map$Entry<TK;TV;>;)V
+M (Ljava/util/Map$Entry<TK;TV;>;)Z
+M (Ljava/util/Map$Entry<TK;TV;>;Ljava/lang/Class<TV;>;)V
+M (Ljava/util/Map<**>;)V
+M (Ljava/util/Map<+Ljava/text/AttributedCharacterIterator$Attribute;*>;)Ljava/awt/Font;
+M (Ljava/util/Map<+Ljava/text/AttributedCharacterIterator$Attribute;*>;)V
+M (Ljava/util/Map<+Ljava/text/AttributedCharacterIterator$Attribute;*>;II)V
+M (Ljava/util/Map<+TK;+TV;>;)V
+M (Ljava/util/Map<Ljava/awt/RenderingHints$Key;*>;)V
+M (Ljava/util/Map<Ljava/lang/String;*>;)Ljavax/management/remote/JMXConnector;
+M (Ljava/util/Map<Ljava/lang/String;*>;)V
+M (Ljava/util/Map<Ljava/lang/String;*>;)[Ljava/lang/String;
+M (Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)Ljava/lang/Object;
+M (Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)Ljava/sql/ResultSet;
+M (Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)V
+M (Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)[Ljava/lang/Object;
+M (Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;)Ljava/lang/String;
+M (Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;)[Ljava/security/Provider;
+M (Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/reflect/Method;>;>;I)[Ljavax/management/MBeanOperationInfo;
+M (Ljava/util/Map<Ljava/lang/String;Lsun/management/MXBeanSupport$AttributeMethod;>;)[Ljavax/management/MBeanAttributeInfo;
+M (Ljava/util/Map<Ljava/security/Provider$ServiceKey;Ljava/security/Provider$Service;>;)V
+M (Ljava/util/Map<Ljavax/print/attribute/standard/PrinterStateReason;Ljavax/print/attribute/standard/Severity;>;)V
+M (Ljava/util/Map<TK;+TV;>;)V
+M (Ljava/util/Map<TK;TV;>;)V
+M (Ljava/util/Map<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)V
+M (Ljava/util/Map<TK;TV;>;Ljava/lang/Object;)V
+M (Ljava/util/PriorityQueue<+TE;>;)V
+M (Ljava/util/Set<*>;)Ljava/util/Set<*>;
+M (Ljava/util/Set<+Ljava/text/AttributedCharacterIterator$Attribute;>;)I
+M (Ljava/util/Set<+Ljava/util/Map$Entry<+TK;+TV;>;>;)V
+M (Ljava/util/Set<+TE;>;)V
+M (Ljava/util/Set<Ljava/lang/String;>;)V
+M (Ljava/util/Set<Ljava/lang/String;>;Ljava/util/Enumeration<Ljava/lang/String;>;)V
+M (Ljava/util/Set<Ljava/security/cert/TrustAnchor;>;)V
+M (Ljava/util/Set<Ljava/security/cert/TrustAnchor;>;Ljava/security/cert/CertSelector;)V
+M (Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;)V
+M (Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;Ljava/lang/Class<TV;>;)V
+M (Ljava/util/Set<Ljavax/security/auth/x500/X500Principal;>;)V
+M (Ljava/util/Set<Lsun/security/x509/GeneralNameInterface;>;)V
+M (Ljava/util/Set<TE;>;)V
+M (Ljava/util/Set<TE;>;Ljava/lang/Class<TE;>;)V
+M (Ljava/util/Set<TE;>;Ljava/lang/Object;)V
+M (Ljava/util/SortedMap<TE;Ljava/lang/Object;>;)V
+M (Ljava/util/SortedMap<TK;+TV;>;)V
+M (Ljava/util/SortedMap<TK;TV;>;)V
+M (Ljava/util/SortedMap<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)V
+M (Ljava/util/SortedMap<TK;TV;>;Ljava/lang/Object;)V
+M (Ljava/util/SortedSet<+TE;>;)V
+M (Ljava/util/SortedSet<Ljava/util/Map$Entry<TK;TV;>;>;TV;)V
+M (Ljava/util/SortedSet<TE;>;)V
+M (Ljava/util/SortedSet<TE;>;Ljava/lang/Class<TE;>;)V
+M (Ljava/util/SortedSet<TE;>;Ljava/lang/Object;)V
+M (Ljava/util/TreeMap$Entry<TK;TV;>;)Ljava/util/TreeMap$Entry<TK;TV;>;
+M (Ljava/util/TreeMap$Entry<TK;TV;>;)V
+M (Ljava/util/TreeMap$Entry<TK;TV;>;Ljava/util/TreeMap$Entry<TK;TV;>;)V
+M (Ljava/util/Vector<*>;)V
+M (Ljava/util/Vector<*>;Ljava/util/Map<Ljava/lang/String;*>;)V
+M (Ljava/util/Vector<+Ljava/io/File;>;)V
+M (Ljava/util/Vector<Ljava/lang/Object;>;)V
+M (Ljava/util/Vector<Ljava/lang/Object;>;Ljava/util/Vector<Ljava/lang/Object;>;)V
+M (Ljava/util/Vector<Ljavax/swing/text/html/parser/Element;>;)V
+M (Ljava/util/Vector<Ljavax/swing/tree/PathPlaceHolder;>;Ljavax/swing/tree/TreePath;)V
+M (Ljava/util/Vector<Lsun/security/util/ObjectIdentifier;>;)V
+M (Ljava/util/Vector<Lsun/security/x509/CertificatePolicyId;>;)V
+M (Ljava/util/concurrent/Callable<TT;>;)V
+M (Ljava/util/concurrent/Callable<TV;>;)Ljava/util/concurrent/Future<TV;>;
+M (Ljava/util/concurrent/Callable<TV;>;)V
+M (Ljava/util/concurrent/Callable<TV;>;J)V
+M (Ljava/util/concurrent/ConcurrentHashMap$HashEntry<TK;TV;>;)TV;
+M (Ljava/util/concurrent/ConcurrentLinkedQueue$Node<TE;>;)V
+M (Ljava/util/concurrent/ConcurrentLinkedQueue$Node<TE;>;Ljava/util/concurrent/ConcurrentLinkedQueue$Node<TE;>;)Z
+M (Ljava/util/concurrent/CopyOnWriteArrayList<TE;>;II)V
+M (Ljava/util/concurrent/Executor;Ljava/util/concurrent/BlockingQueue<Ljava/util/concurrent/Future<TV;>;>;)V
+M (Ljava/util/concurrent/locks/AbstractQueuedSynchronizer$ConditionObject;)Ljava/util/Collection<Ljava/lang/Thread;>;
+M (Ljava/util/concurrent/locks/Condition;)Ljava/util/Collection<Ljava/lang/Thread;>;
+M (Ljavax/imageio/ImageReadParam;Ljava/util/Iterator<Ljavax/imageio/ImageTypeSpecifier;>;II)Ljava/awt/image/BufferedImage;
+M (Ljavax/imageio/ImageReader;Ljavax/imageio/ImageWriter;)Ljava/util/Iterator<Ljavax/imageio/ImageTranscoder;>;
+M (Ljavax/imageio/ImageTypeSpecifier;Ljava/lang/String;)Ljava/util/Iterator<Ljavax/imageio/ImageWriter;>;
+M (Ljavax/imageio/metadata/IIOMetadata;Ljavax/imageio/ImageTypeSpecifier;IILjavax/imageio/metadata/IIOMetadata;Ljava/util/List<+Ljava/awt/image/BufferedImage;>;Ljavax/imageio/ImageWriteParam;)V
+M (Ljavax/imageio/spi/ServiceRegistry;Ljava/lang/Class<*>;)V
+M (Ljavax/management/MBeanServerConnection;Ljavax/management/ObjectName;Ljava/lang/Class<*>;)V
+M (Ljavax/management/ObjectName;Ljava/rmi/MarshalledObject;Ljavax/security/auth/Subject;)Ljava/util/Set<Ljavax/management/ObjectInstance;>;
+M (Ljavax/management/ObjectName;Ljava/rmi/MarshalledObject;Ljavax/security/auth/Subject;)Ljava/util/Set<Ljavax/management/ObjectName;>;
+M (Ljavax/management/openmbean/CompositeData;)Ljava/util/Map<Ljava/lang/String;Ljava/lang/management/MemoryUsage;>;
+M (Ljavax/management/remote/JMXServiceURL;Ljava/util/Map<Ljava/lang/String;*>;)Ljavax/management/remote/JMXConnector;
+M (Ljavax/management/remote/JMXServiceURL;Ljava/util/Map<Ljava/lang/String;*>;)V
+M (Ljavax/management/remote/JMXServiceURL;Ljava/util/Map<Ljava/lang/String;*>;Ljavax/management/MBeanServer;)Ljavax/management/remote/JMXConnectorServer;
+M (Ljavax/management/remote/JMXServiceURL;Ljava/util/Map<Ljava/lang/String;*>;Ljavax/management/MBeanServer;)V
+M (Ljavax/management/remote/JMXServiceURL;Ljava/util/Map<Ljava/lang/String;*>;Ljavax/management/remote/rmi/RMIServerImpl;Ljavax/management/MBeanServer;)V
+M (Ljavax/management/remote/rmi/RMIServer;Ljava/util/Map<Ljava/lang/String;*>;)V
+M (Ljavax/management/remote/rmi/RMIServerImpl;Ljava/lang/String;Ljava/lang/ClassLoader;Ljavax/security/auth/Subject;Ljava/util/Map<Ljava/lang/String;*>;)V
+M (Ljavax/naming/Name;)Ljavax/naming/NamingEnumeration<Ljavax/naming/Binding;>;
+M (Ljavax/naming/Name;)Ljavax/naming/NamingEnumeration<Ljavax/naming/NameClassPair;>;
+M (Ljavax/naming/Name;Ljava/lang/Class<+Ljavax/naming/Context;>;)Ljavax/naming/spi/ResolveResult;
+M (Ljavax/naming/Name;Ljava/lang/String;Ljavax/naming/directory/SearchControls;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljavax/naming/Name;Ljava/lang/String;[Ljava/lang/Object;Ljavax/naming/directory/SearchControls;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljavax/naming/Name;Ljavax/naming/directory/Attributes;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljavax/naming/Name;Ljavax/naming/directory/Attributes;[Ljava/lang/String;)Ljavax/naming/NamingEnumeration<Ljavax/naming/directory/SearchResult;>;
+M (Ljavax/naming/ldap/Control;Ljavax/naming/Context;Ljava/util/Hashtable<**>;)Ljavax/naming/ldap/Control;
+M (Ljavax/print/attribute/Attribute;Ljava/lang/Class<*>;)V
+M (Ljavax/print/attribute/AttributeSet;Ljava/lang/Class<*>;)V
+M (Ljavax/print/attribute/standard/Severity;)Ljava/util/Set<Ljavax/print/attribute/standard/PrinterStateReason;>;
+M (Ljavax/security/auth/Subject;Ljavax/security/auth/callback/CallbackHandler;Ljava/util/Map<Ljava/lang/String;*>;Ljava/util/Map<Ljava/lang/String;*>;)V
+M (Ljavax/security/auth/x500/X500Principal;Lsun/security/provider/certpath/ForwardState;Lsun/security/provider/certpath/ForwardBuilder;Ljava/util/List;Ljava/util/LinkedList<Ljava/security/cert/X509Certificate;>;)V
+M (Ljavax/security/auth/x500/X500Principal;Lsun/security/provider/certpath/ReverseState;Lsun/security/provider/certpath/ReverseBuilder;Ljava/util/List;Ljava/util/LinkedList<Ljava/security/cert/X509Certificate;>;)V
+M (Ljavax/sound/midi/Soundbank;Ljava/lang/String;Ljava/lang/Class<*>;)V
+M (Ljavax/sound/midi/Soundbank;Ljavax/sound/midi/Patch;Ljava/lang/String;Ljava/lang/Class<*>;)V
+M (Ljavax/sound/sampled/AudioFileFormat$Type;Ljavax/sound/sampled/AudioFormat;ILjava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)V
+M (Ljavax/sound/sampled/AudioFormat$Encoding;FIIIFZLjava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;)V
+M (Ljavax/swing/text/AttributeSet;Ljava/util/Enumeration<*>;)Ljavax/swing/text/AttributeSet;
+M (Ljavax/swing/tree/TreePath;)Ljava/util/Enumeration<Ljavax/swing/tree/TreePath;>;
+M (Lsun/reflect/generics/visitor/TypeTreeVisitor<*>;)V
+M (Lsun/security/jca/GetInstance$Instance;Ljava/util/Iterator<Ljava/security/Provider$Service;>;Ljava/lang/String;)V
+M (Lsun/security/provider/certpath/ForwardState;)Ljava/util/Collection<Ljava/security/cert/X509Certificate;>;
+M (Lsun/security/provider/certpath/ReverseState;)Ljava/util/Collection<Ljava/security/cert/X509Certificate;>;
+M (Lsun/security/provider/certpath/State;)Ljava/util/Collection<Ljava/security/cert/X509Certificate;>;
+M (Lsun/security/x509/CertificatePolicyId;Ljava/util/Set<Ljava/security/cert/PolicyQualifierInfo;>;)V
+M (Lsun/security/x509/GeneralNames;)Ljava/util/Collection<Ljava/util/List<*>;>;
+M (TD;)V
+M (TD;Ljava/lang/String;[Lsun/reflect/generics/tree/FieldTypeSignature;Lsun/reflect/generics/factory/GenericsFactory;)V
+M (TE;)I
+M (TE;)Ljava/util/SortedSet<TE;>;
+M (TE;)TE;
+M (TE;)V
+M (TE;)Z
+M (TE;I)I
+M (TE;I)V
+M (TE;JLjava/util/concurrent/TimeUnit;)Z
+M (TE;Ljava/util/LinkedList$Entry<TE;>;)Ljava/util/LinkedList$Entry<TE;>;
+M (TE;Ljava/util/LinkedList$Entry<TE;>;Ljava/util/LinkedList$Entry<TE;>;)V
+M (TE;Ljava/util/concurrent/ConcurrentLinkedQueue$Node<TE;>;)V
+M (TE;TE;)Ljava/util/SortedSet<TE;>;
+M (TE;TE;)V
+M (TE;TE;)Z
+M (TK;)Ljava/util/SortedMap<TK;TV;>;
+M (TK;)Ljava/util/TreeMap$Entry<TK;TV;>;
+M (TK;)V
+M (TK;)Z
+M (TK;ILjava/util/concurrent/ConcurrentHashMap$HashEntry<TK;TV;>;TV;)V
+M (TK;ITV;)TV;
+M (TK;ITV;TV;)Z
+M (TK;ITV;Z)TV;
+M (TK;TK;)I
+M (TK;TK;)Ljava/util/SortedMap<TK;TV;>;
+M (TK;TK;)V
+M (TK;TV;)TV;
+M (TK;TV;)V
+M (TK;TV;Ljava/lang/ref/ReferenceQueue<TK;>;ILjava/util/WeakHashMap$Entry<TK;TV;>;)V
+M (TK;TV;Ljava/util/TreeMap$Entry<TK;TV;>;)V
+M (TK;TV;TV;)Z
+M (TK;Z)V
+M (TN;)TV;
+M (TT;)I
+M (TT;)J
+M (TT;)TT;
+M (TT;)TV;
+M (TT;)V
+M (TT;)Z
+M (TT;I)I
+M (TT;I)V
+M (TT;II)Z
+M (TT;J)J
+M (TT;J)V
+M (TT;JJ)Z
+M (TT;Ljava/lang/ref/ReferenceQueue<-TT;>;)V
+M (TT;TT;)I
+M (TT;TV;)TV;
+M (TT;TV;)V
+M (TT;TV;TV;)Z
+M (TT;Z)V
+M (TV;)TV;
+M (TV;)V
+M (TV;I)V
+M (TV;I)Z
+M (TV;JLjava/util/concurrent/TimeUnit;)TV;
+M (TV;TN;)Z
+M (TV;TV;)Z
+M (TV;TV;II)Z
+M (TV;TV;ZZ)Z
+M (TV;Z)V
+M (TV;Z)Z
+M (TV;ZJ)TV;
+M (ZIILjava/util/Map<Ljava/awt/font/TextAttribute;*>;)V
+M (ZLjava/util/Set<+Ljava/security/Principal;>;Ljava/util/Set<*>;Ljava/util/Set<*>;)V
+M (ZTK;ZTK;)V
+M ([BII)Ljava/lang/Class<*>;
+M ([BLsun/reflect/ConstantPool;Ljava/lang/Class;)Ljava/util/Map<Ljava/lang/Class;Ljava/lang/annotation/Annotation;>;
+M ([I)TV;
+M ([Ljava/awt/datatransfer/DataFlavor;)Ljava/util/Map<Ljava/awt/datatransfer/DataFlavor;Ljava/lang/String;>;
+M ([Ljava/lang/Class;)Ljava/lang/reflect/Constructor<TT;>;
+M ([Ljava/lang/Class;)Ljava/util/List<Ljava/lang/Class;>;
+M ([Ljava/lang/Class;I)Ljava/lang/reflect/Constructor<TT;>;
+M ([Ljava/lang/Class;[Ljava/lang/Class;Ljava/util/List<Ljava/lang/Class;>;)V
+M ([Ljava/lang/Object;)TT;
+M ([Ljava/lang/Object;Ljava/lang/StringBuilder;Ljava/util/Set<[Ljava/lang/Object;>;)V
+M ([Ljava/lang/Object;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class<*>;>;)V
+M ([Ljava/lang/String;)Ljava/lang/Class<*>;
+M ([Ljava/lang/String;)Ljava/util/Map<Ljava/lang/String;Ljava/awt/datatransfer/DataFlavor;>;
+M ([Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;*>;Ljavax/security/auth/callback/CallbackHandler;)Ljavax/security/sasl/SaslClient;
+M ([Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;Ljava/lang/String;Z)Ljava/lang/Process;
+M ([Ljavax/print/attribute/Attribute;Ljava/lang/Class<*>;)V
+M ([TE;)V
+M ([TE;I)V
+M ([TE;II)V
+M ([Z)TV;
+M <A::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TA;>;)TA;
+M <E:Ljava/lang/Enum<TE;>;>(Ljava/lang/Class<TE;>;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(Ljava/util/Collection<TE;>;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(Ljava/util/EnumSet<TE;>;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(TE;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(TE;TE;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(TE;TE;TE;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(TE;TE;TE;TE;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(TE;TE;TE;TE;TE;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Enum<TE;>;>(TE;[TE;)Ljava/util/EnumSet<TE;>;
+M <E:Ljava/lang/Object;>(Ljava/util/Collection<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Collection<TE;>;
+M <E:Ljava/lang/Object;>(Ljava/util/List<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/List<TE;>;
+M <E:Ljava/lang/Object;>(Ljava/util/Set<TE;>;)Ljava/util/Set<TE;>;
+M <E:Ljava/lang/Object;>(Ljava/util/Set<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/Set<TE;>;
+M <E:Ljava/lang/Object;>(Ljava/util/SortedSet<TE;>;Ljava/lang/Class<TE;>;)Ljava/util/SortedSet<TE;>;
+M <K:Ljava/lang/Object;>(Ljava/lang/Object;)TK;
+M <K:Ljava/lang/Object;>(Ljava/util/TreeMap$Entry<TK;*>;)TK;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>()Ljava/util/Map<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<+TK;+TV;>;)Ljava/util/Map<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;)Ljava/util/Map<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/Map<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/Map<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;+TV;>;)Ljava/util/SortedMap<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;TV;>;)Ljava/util/SortedMap<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/SortedMap<TK;TV;>;Ljava/lang/Class<TK;>;Ljava/lang/Class<TV;>;)Ljava/util/SortedMap<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/TreeMap$Entry<TK;TV;>;)Ljava/util/TreeMap$Entry<TK;TV;>;
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/TreeMap$Entry<TK;TV;>;)Z
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(Ljava/util/TreeMap$Entry<TK;TV;>;Z)V
+M <K:Ljava/lang/Object;V:Ljava/lang/Object;>(TK;TV;)Ljava/util/Map<TK;TV;>;
+M <T::Ljava/lang/Comparable<-TT;>;>(Ljava/util/List<TT;>;)V
+M <T::Ljava/lang/annotation/Annotation;>(Ljava/lang/Class<TT;>;)TT;
+M <T::Ljava/lang/reflect/GenericDeclaration;>(TT;Ljava/lang/String;[Lsun/reflect/generics/tree/FieldTypeSignature;Lsun/reflect/generics/factory/GenericsFactory;)Lsun/reflect/generics/reflectiveObjects/TypeVariableImpl<TT;>;
+M <T::Ljava/security/Principal;>(Ljava/lang/Class<TT;>;)Ljava/util/Set<TT;>;
+M <T::Ljava/security/spec/AlgorithmParameterSpec;>(Ljava/lang/Class<TT;>;)TT;
+M <T::Ljava/security/spec/KeySpec;>(Ljava/security/Key;Ljava/lang/Class<TT;>;)TT;
+M <T::Ljava/util/EventListener;>(Ljava/lang/Class<TT;>;)[TT;
+M <T::Ljava/util/EventListener;>(Ljava/lang/Class<TT;>;TT;)V
+M <T::Ljava/util/EventListener;>(Ljava/util/EventListener;Ljava/lang/Class<TT;>;)[TT;
+M <T::Ljavax/print/attribute/PrintServiceAttribute;>(Ljava/lang/Class<TT;>;)TT;
+M <T:Ljava/awt/dnd/DragGestureRecognizer;>(Ljava/lang/Class<TT;>;Ljava/awt/Component;ILjava/awt/dnd/DragGestureListener;)TT;
+M <T:Ljava/awt/dnd/DragGestureRecognizer;>(Ljava/lang/Class<TT;>;Ljava/awt/dnd/DragSource;Ljava/awt/Component;ILjava/awt/dnd/DragGestureListener;)TT;
+M <T:Ljava/lang/Enum<TT;>;>(Ljava/lang/Class<TT;>;Ljava/lang/String;)TT;
+M <T:Ljava/lang/Object;:Ljava/lang/Comparable<-TT;>;>(Ljava/lang/String;Ljava/lang/Class<TT;>;TT;Ljava/lang/Comparable<-TT;>;Ljava/lang/Comparable<-TT;>;ZZ)V
+M <T:Ljava/lang/Object;:Ljava/lang/Comparable<-TT;>;>(Ljava/util/Collection<+TT;>;)TT;
+M <T:Ljava/lang/Object;>()Ljava/util/Comparator<TT;>;
+M <T:Ljava/lang/Object;>()Ljava/util/List<TT;>;
+M <T:Ljava/lang/Object;>()Ljava/util/Set<TT;>;
+M <T:Ljava/lang/Object;>(I)Ljava/util/Enumeration<TT;>;
+M <T:Ljava/lang/Object;>(I)Ljava/util/Iterator<TT;>;
+M <T:Ljava/lang/Object;>(ITT;)Ljava/util/List<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;)Ljava/util/Iterator<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;)Ljava/util/Set<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;)TT;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;Ljava/lang/ClassLoader;)Ljava/util/Iterator<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;Ljava/lang/Object;Ljava/lang/String;)TT;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)TT;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)TT;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;Ljavax/imageio/spi/ServiceRegistry$Filter;Z)Ljava/util/Iterator<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;TT;TT;)Z
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;Z)Ljava/util/Iterator<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Class<TT;>;[Ljava/lang/Class;[Ljava/lang/Class;IILjava/lang/String;[B[B)Ljava/lang/reflect/Constructor<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Runnable;TT;)Ljava/util/concurrent/Callable<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/Runnable;TT;)Ljava/util/concurrent/Future<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/lang/String;Ljava/lang/Class<TT;>;ZTT;)V
+M <T:Ljava/lang/Object;>(Ljava/lang/String;Ljava/lang/Class<TT;>;ZTT;Ljava/util/List<+TT;>;)V
+M <T:Ljava/lang/Object;>(Ljava/lang/reflect/Constructor<TT;>;)Ljava/lang/reflect/Constructor<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;)TT;
+M <T:Ljava/lang/Object;>(Ljava/security/PrivilegedAction<TT;>;Ljava/security/AccessControlContext;)TT;
+M <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;)TT;
+M <T:Ljava/lang/Object;>(Ljava/security/PrivilegedExceptionAction<TT;>;Ljava/security/AccessControlContext;)TT;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<+TT;>;)Ljava/util/Collection<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<+TT;>;Ljava/util/Comparator<-TT;>;)TT;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<-TT;>;[TT;)Z
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<Ljava/util/concurrent/Callable<TT;>;>;)Ljava/util/List<Ljava/util/concurrent/Future<TT;>;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<Ljava/util/concurrent/Callable<TT;>;>;)TT;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<Ljava/util/concurrent/Callable<TT;>;>;JLjava/util/concurrent/TimeUnit;)Ljava/util/List<Ljava/util/concurrent/Future<TT;>;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<Ljava/util/concurrent/Callable<TT;>;>;JLjava/util/concurrent/TimeUnit;)TT;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<Ljava/util/concurrent/Callable<TT;>;>;ZJ)TT;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)Ljava/util/Collection<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)Ljava/util/Enumeration<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;Ljava/lang/Object;)Ljava/util/Collection<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Comparator<TT;>;)Ljava/util/Comparator<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Enumeration<TT;>;)Ljava/util/ArrayList<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/List<+Ljava/lang/Comparable<-TT;>;>;TT;)I
+M <T:Ljava/lang/Object;>(Ljava/util/List<+TT;>;)Ljava/util/List<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/List<+TT;>;TT;Ljava/util/Comparator<-TT;>;)I
+M <T:Ljava/lang/Object;>(Ljava/util/List<-TT;>;Ljava/util/List<+TT;>;)V
+M <T:Ljava/lang/Object;>(Ljava/util/List<-TT;>;TT;)V
+M <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;)Ljava/util/List<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;I)V
+M <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/lang/Object;)Ljava/util/List<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Comparator<-TT;>;)V
+M <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;TT;TT;)Z
+M <T:Ljava/lang/Object;>(Ljava/util/ListIterator<+TT;>;I)TT;
+M <T:Ljava/lang/Object;>(Ljava/util/Set<+TT;>;)Ljava/util/Set<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Set<TT;>;)Ljava/util/Set<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/Set<TT;>;Ljava/lang/Object;)Ljava/util/Set<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/SortedSet<TT;>;)Ljava/util/SortedSet<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/concurrent/Callable<TT;>;)Ljava/util/concurrent/Callable<TT;>;
+M <T:Ljava/lang/Object;>(Ljava/util/concurrent/Callable<TT;>;)Ljava/util/concurrent/Future<TT;>;
+M <T:Ljava/lang/Object;>(Ljavax/management/MBeanServerConnection;Ljava/lang/String;Ljava/lang/Class<TT;>;)TT;
+M <T:Ljava/lang/Object;>(TT;)Ljava/util/List<TT;>;
+M <T:Ljava/lang/Object;>(TT;)Ljava/util/Set<TT;>;
+M <T:Ljava/lang/Object;>(TT;)TT;
+M <T:Ljava/lang/Object;>(TT;Ljava/lang/Class<TT;>;)Z
+M <T:Ljava/lang/Object;>([TT;)Ljava/util/List<TT;>;
+M <T:Ljava/lang/Object;>([TT;)[TT;
+M <T:Ljava/lang/Object;>([TT;II)[TT;
+M <T:Ljava/lang/Object;>([TT;IILjava/util/Comparator<-TT;>;)V
+M <T:Ljava/lang/Object;>([TT;Ljava/util/Comparator<-TT;>;)V
+M <T:Ljava/lang/Object;>([TT;TT;Ljava/util/Comparator<-TT;>;)I
+M <U:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;)Ljava/lang/Class<+TU;>;
+M <U:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicIntegerFieldUpdater<TU;>;
+M <U:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicLongFieldUpdater<TU;>;
+M <U:Ljava/lang/Object;W:Ljava/lang/Object;>(Ljava/lang/Class<TU;>;Ljava/lang/Class<TW;>;Ljava/lang/String;)Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<TU;TW;>;
+M <V:Ljava/lang/Object;>(Ljava/util/concurrent/Callable<TV;>;JLjava/util/concurrent/TimeUnit;)Ljava/util/concurrent/ScheduledFuture<TV;>;
+T Ljava/lang/Class<*>;
+T Ljava/lang/Class<+Ljava/lang/Enum;>;
+T Ljava/lang/Class<Ljava/lang/Boolean;>;
+T Ljava/lang/Class<Ljava/lang/Byte;>;
+T Ljava/lang/Class<Ljava/lang/Character;>;
+T Ljava/lang/Class<Ljava/lang/Double;>;
+T Ljava/lang/Class<Ljava/lang/Float;>;
+T Ljava/lang/Class<Ljava/lang/Integer;>;
+T Ljava/lang/Class<Ljava/lang/Long;>;
+T Ljava/lang/Class<Ljava/lang/Short;>;
+T Ljava/lang/Class<Ljava/lang/Void;>;
+T Ljava/lang/Class<TE;>;
+T Ljava/lang/Class<TK;>;
+T Ljava/lang/Class<TT;>;
+T Ljava/lang/Class<TV;>;
+T Ljava/lang/ThreadLocal<Ljava/lang/Long;>;
+T Ljava/lang/ThreadLocal<Lsun/java2d/opengl/OGLContext;>;
+T Ljava/lang/ThreadLocal<Lsun/security/jca/ProviderList;>;
+T Ljava/lang/ref/Reference<+TT;>;
+T Ljava/lang/ref/Reference<Ljava/lang/Class;>;
+T Ljava/lang/ref/Reference<TT;>;
+T Ljava/lang/ref/ReferenceQueue<-TT;>;
+T Ljava/lang/ref/ReferenceQueue<TK;>;
+T Ljava/lang/ref/SoftReference<Ljava/util/Hashtable<Lsun/font/Font2DHandle;Ljava/lang/Object;>;>;
+T Ljava/lang/ref/SoftReference<Ljava/util/Map;>;
+T Ljava/lang/ref/SoftReference<[B>;
+T Ljava/lang/ref/SoftReference<[I>;
+T Ljava/lang/ref/SoftReference<[Ljava/lang/String;>;
+T Ljava/lang/ref/WeakReference<Ljava/awt/image/ImageObserver;>;
+T Ljava/lang/ref/WeakReference<Ljavax/swing/JFileChooser;>;
+T Ljava/lang/reflect/Constructor<TT;>;
+T Ljava/util/AbstractList<TE;>;
+T Ljava/util/ArrayList<Lcom/sun/media/sound/EventDispatcher$ClipInfo;>;
+T Ljava/util/ArrayList<Lcom/sun/media/sound/EventDispatcher$LineMonitor;>;
+T Ljava/util/ArrayList<Ljava/awt/event/InputEvent;>;
+T Ljava/util/ArrayList<Ljava/security/cert/PKIXCertPathChecker;>;
+T Ljava/util/ArrayList<Ljavax/sound/midi/Receiver;>;
+T Ljava/util/ArrayList<Ljavax/sound/midi/Transmitter;>;
+T Ljava/util/ArrayList<Lsun/net/ProgressListener;>;
+T Ljava/util/ArrayList<Lsun/net/ProgressSource;>;
+T Ljava/util/ArrayList<TE;>;
+T Ljava/util/Collection<+TE;>;
+T Ljava/util/Collection<Ljava/lang/String;>;
+T Ljava/util/Collection<Ljava/security/cert/X509Certificate;>;
+T Ljava/util/Collection<Ljava/util/List<*>;>;
+T Ljava/util/Collection<TE;>;
+T Ljava/util/Collection<TV;>;
+T Ljava/util/Comparator<-Ljava/awt/Component;>;
+T Ljava/util/Comparator<-TE;>;
+T Ljava/util/Comparator<-TK;>;
+T Ljava/util/Comparator<Ljava/lang/String;>;
+T Ljava/util/Comparator<Lsun/security/x509/AVA;>;
+T Ljava/util/Comparator<TT;>;
+T Ljava/util/Dictionary<Ljava/lang/Object;Ljava/lang/Object;>;
+T Ljava/util/Enumeration<Ljava/lang/String;>;
+T Ljava/util/Enumeration<Ljava/security/Permission;>;
+T Ljava/util/Enumeration<Ljavax/naming/directory/Attribute;>;
+T Ljava/util/Enumeration<Ljavax/swing/tree/TreeNode;>;
+T Ljava/util/HashMap$Entry<TK;TV;>;
+T Ljava/util/HashMap<Ljava/lang/Object;Ljava/lang/Object;>;
+T Ljava/util/HashMap<Ljava/lang/Short;Ljava/lang/Short;>;
+T Ljava/util/HashMap<Ljava/lang/Short;[I>;
+T Ljava/util/HashMap<Ljava/lang/Short;[Ljava/lang/Short;>;
+T Ljava/util/HashMap<Ljava/lang/Short;[S>;
+T Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Integer;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Object;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Short;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;
+T Ljava/util/HashMap<TE;Ljava/lang/Object;>;
+T Ljava/util/HashSet<Ljava/lang/Object;>;
+T Ljava/util/HashSet<Ljava/security/cert/X509CRL;>;
+T Ljava/util/HashSet<Ljava/util/concurrent/ThreadPoolExecutor$Worker;>;
+T Ljava/util/HashSet<Ljavax/security/auth/x500/X500Principal;>;
+T Ljava/util/HashSet<Lsun/security/x509/GeneralNameInterface;>;
+T Ljava/util/Hashtable$Entry<TK;TV;>;
+T Ljava/util/Hashtable<**>;
+T Ljava/util/Hashtable<Ljava/awt/Component;Ljava/awt/GridBagConstraints;>;
+T Ljava/util/Hashtable<Ljava/awt/Component;Ljava/lang/Integer;>;
+T Ljava/util/Hashtable<Ljava/io/File;Ljavax/swing/Icon;>;
+T Ljava/util/Hashtable<Ljava/lang/Object;Ljava/lang/Object;>;
+T Ljava/util/Hashtable<Ljava/lang/Object;Ljavax/swing/text/html/parser/Entity;>;
+T Ljava/util/Hashtable<Ljava/lang/String;Ljava/util/logging/Logger;>;
+T Ljava/util/Hashtable<Ljava/lang/String;Ljavax/swing/text/html/parser/Element;>;
+T Ljava/util/Hashtable<Ljava/lang/String;Lsun/security/x509/Extension;>;
+T Ljava/util/Hashtable<Ljava/util/Locale;[I>;
+T Ljava/util/Hashtable<Ljavax/swing/tree/TreePath;Ljava/lang/Boolean;>;
+T Ljava/util/Iterator<+Ljava/util/Map$Entry<+TK;+TV;>;>;
+T Ljava/util/Iterator<+TE;>;
+T Ljava/util/Iterator<Ljava/lang/String;>;
+T Ljava/util/Iterator<Ljava/security/PermissionCollection;>;
+T Ljava/util/Iterator<Ljava/security/Provider$Service;>;
+T Ljava/util/Iterator<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;
+T Ljava/util/Iterator<Ljava/util/Map$Entry<TK;TV;>;>;
+T Ljava/util/Iterator<Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;>;
+T Ljava/util/Iterator<TE;>;
+T Ljava/util/Iterator<TT;>;
+T Ljava/util/LinkedHashMap$Entry<TK;TV;>;
+T Ljava/util/LinkedList$Entry<TE;>;
+T Ljava/util/List<+Ljava/awt/image/BufferedImage;>;
+T Ljava/util/List<+TE;>;
+T Ljava/util/List<Ljava/lang/String;>;
+T Ljava/util/List<Ljava/lang/ref/SoftReference<Ljavax/swing/plaf/metal/CachedPainter$Cache$Entry;>;>;
+T Ljava/util/List<Ljava/security/Provider$Service;>;
+T Ljava/util/List<Ljava/security/Provider;>;
+T Ljava/util/List<Ljava/security/cert/CertStore;>;
+T Ljava/util/List<Ljava/security/cert/PKIXCertPathChecker;>;
+T Ljava/util/List<Ljava/util/Locale;>;
+T Ljava/util/List<Ljavax/imageio/event/IIOReadProgressListener;>;
+T Ljava/util/List<Ljavax/imageio/event/IIOReadUpdateListener;>;
+T Ljava/util/List<Ljavax/imageio/event/IIOReadWarningListener;>;
+T Ljava/util/List<Ljavax/imageio/event/IIOWriteProgressListener;>;
+T Ljava/util/List<Ljavax/imageio/event/IIOWriteWarningListener;>;
+T Ljava/util/List<Lsun/misc/ProxyGenerator$ConstantPool$Entry;>;
+T Ljava/util/List<Lsun/misc/ProxyGenerator$ExceptionTableEntry;>;
+T Ljava/util/List<Lsun/misc/ProxyGenerator$FieldInfo;>;
+T Ljava/util/List<Lsun/misc/ProxyGenerator$MethodInfo;>;
+T Ljava/util/List<Lsun/reflect/generics/tree/SimpleClassTypeSignature;>;
+T Ljava/util/List<Lsun/security/jca/ServiceId;>;
+T Ljava/util/List<Lsun/security/x509/AVA;>;
+T Ljava/util/List<Lsun/security/x509/CertificatePolicyMap;>;
+T Ljava/util/List<Lsun/security/x509/DistributionPoint;>;
+T Ljava/util/List<Lsun/security/x509/Extension;>;
+T Ljava/util/List<Lsun/security/x509/GeneralName;>;
+T Ljava/util/List<Lsun/security/x509/GeneralSubtree;>;
+T Ljava/util/List<Lsun/security/x509/PolicyInformation;>;
+T Ljava/util/List<Lsun/security/x509/RDN;>;
+T Ljava/util/List<TE;>;
+T Ljava/util/ListIterator<+TE;>;
+T Ljava/util/ListIterator<TE;>;
+T Ljava/util/Map$Entry<+TK;+TV;>;
+T Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;
+T Ljava/util/Map$Entry<TK;TV;>;
+T Ljava/util/Map<+TK;+TV;>;
+T Ljava/util/Map<Ljava/awt/Font;Ljava/awt/FontMetrics;>;
+T Ljava/util/Map<Ljava/lang/Class;Ljava/lang/annotation/Annotation;>;
+T Ljava/util/Map<Ljava/lang/Class;Lsun/management/MXBeanSupport;>;
+T Ljava/util/Map<Ljava/lang/Class;Lsun/misc/ProxyGenerator$PrimitiveTypeInfo;>;
+T Ljava/util/Map<Ljava/lang/Class<*>;Ljava/lang/Object;>;
+T Ljava/util/Map<Ljava/lang/Class<*>;Ljava/security/PermissionCollection;>;
+T Ljava/util/Map<Ljava/lang/Integer;Ljava/lang/String;>;
+T Ljava/util/Map<Ljava/lang/Integer;Ljava/lang/Thread$State;>;
+T Ljava/util/Map<Ljava/lang/Integer;Ljava/security/spec/DSAParameterSpec;>;
+T Ljava/util/Map<Ljava/lang/Integer;Ljavax/crypto/spec/DHParameterSpec;>;
+T Ljava/util/Map<Ljava/lang/Integer;[B>;
+T Ljava/util/Map<Ljava/lang/Object;Ljava/lang/Object;>;
+T Ljava/util/Map<Ljava/lang/Object;Ljava/lang/Short;>;
+T Ljava/util/Map<Ljava/lang/Object;Ljavax/swing/plaf/metal/CachedPainter$Cache;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/Boolean;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/Class;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/management/MemoryUsage;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/ref/SoftReference;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/reflect/Method;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/nio/charset/Charset;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Ljava/lang/reflect/Method;>;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/util/List<Lsun/misc/ProxyGenerator$ProxyMethod;>;>;
+T Ljava/util/Map<Ljava/lang/String;Lsun/management/MXBeanSupport$AttributeMethod;>;
+T Ljava/util/Map<Ljava/lang/String;Lsun/security/util/ObjectIdentifier;>;
+T Ljava/util/Map<Ljava/lang/String;Lsun/security/x509/AVAKeyword;>;
+T Ljava/util/Map<Ljava/lang/String;Lsun/security/x509/OIDMap$OIDInfo;>;
+T Ljava/util/Map<Ljava/lang/String;Lsun/util/calendar/ZoneInfo;>;
+T Ljava/util/Map<Ljava/lang/String;TT;>;
+T Ljava/util/Map<Ljava/lang/String;[B>;
+T Ljava/util/Map<Ljava/lang/reflect/Method;Lsun/management/MXBeanSupport$ProxyMethod;>;
+T Ljava/util/Map<Ljava/security/Provider$ServiceKey;Ljava/security/Provider$Service;>;
+T Ljava/util/Map<Ljava/util/Set<Ljava/lang/String;>;Ljava/lang/ClassLoader;>;
+T Ljava/util/Map<Lsun/rmi/transport/ObjectEndpoint;Lsun/rmi/transport/Target;>;
+T Ljava/util/Map<Lsun/rmi/transport/WeakRef;Lsun/rmi/transport/Target;>;
+T Ljava/util/Map<Lsun/security/util/ObjectIdentifier;Ljava/lang/String;>;
+T Ljava/util/Map<Lsun/security/util/ObjectIdentifier;Lsun/security/util/ObjectIdentifier;>;
+T Ljava/util/Map<Lsun/security/util/ObjectIdentifier;Lsun/security/x509/AVAKeyword;>;
+T Ljava/util/Map<Lsun/security/util/ObjectIdentifier;Lsun/security/x509/OIDMap$OIDInfo;>;
+T Ljava/util/Map<Lsun/security/x509/X509CRLImpl$X509IssuerSerial;Ljava/security/cert/X509CRLEntry;>;
+T Ljava/util/Map<TK;TV;>;
+T Ljava/util/PriorityQueue<TE;>;
+T Ljava/util/Set<Ljava/lang/Integer;>;
+T Ljava/util/Set<Ljava/lang/Short;>;
+T Ljava/util/Set<Ljava/lang/String;>;
+T Ljava/util/Set<Ljava/security/Provider$Service;>;
+T Ljava/util/Set<Ljava/security/PublicKey;>;
+T Ljava/util/Set<Ljava/security/cert/PolicyQualifierInfo;>;
+T Ljava/util/Set<Ljava/security/cert/TrustAnchor;>;
+T Ljava/util/Set<Ljava/security/cert/X509Certificate;>;
+T Ljava/util/Set<Ljava/util/List<*>;>;
+T Ljava/util/Set<Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/String;>;>;
+T Ljava/util/Set<Ljava/util/Map$Entry<TK;TV;>;>;
+T Ljava/util/Set<Ljavax/security/auth/x500/X500Principal;>;
+T Ljava/util/Set<Lsun/security/util/ObjectIdentifier;>;
+T Ljava/util/Set<Lsun/security/x509/AccessDescription;>;
+T Ljava/util/Set<Lsun/security/x509/GeneralNameInterface;>;
+T Ljava/util/Set<TE;>;
+T Ljava/util/Set<TK;>;
+T Ljava/util/Set<TT;>;
+T Ljava/util/SortedMap<TE;Ljava/lang/Object;>;
+T Ljava/util/SortedMap<TK;+TV;>;
+T Ljava/util/SortedMap<TK;TV;>;
+T Ljava/util/SortedSet<TE;>;
+T Ljava/util/Stack<Ljavax/swing/tree/TreeNode;>;
+T Ljava/util/TreeMap$Entry<TK;TV;>;
+T Ljava/util/Vector<*>;
+T Ljava/util/Vector<Ljava/lang/Object;>;
+T Ljava/util/Vector<Ljava/lang/String;>;
+T Ljava/util/Vector<Ljavax/accessibility/AccessibleRelation;>;
+T Ljava/util/Vector<Ljavax/accessibility/AccessibleState;>;
+T Ljava/util/Vector<Ljavax/naming/RefAddr;>;
+T Ljava/util/Vector<Ljavax/sound/midi/Track;>;
+T Ljava/util/Vector<Ljavax/swing/AbstractButton;>;
+T Ljava/util/Vector<Ljavax/swing/event/UndoableEditListener;>;
+T Ljava/util/Vector<Ljavax/swing/table/TableColumn;>;
+T Ljava/util/Vector<Ljavax/swing/text/DefaultStyledDocument$ElementSpec;>;
+T Ljava/util/Vector<Ljavax/swing/text/html/parser/Element;>;
+T Ljava/util/Vector<Ljavax/swing/undo/UndoableEdit;>;
+T Ljava/util/Vector<Lsun/security/util/ObjectIdentifier;>;
+T Ljava/util/Vector<Lsun/security/x509/CertificatePolicyId;>;
+T Ljava/util/WeakHashMap$Entry<TK;TV;>;
+T Ljava/util/WeakHashMap<Ljava/lang/reflect/Type;Lsun/management/MappedMXBeanType;>;
+T Ljava/util/concurrent/BlockingQueue<Ljava/lang/Runnable;>;
+T Ljava/util/concurrent/BlockingQueue<Ljava/util/concurrent/Future<TV;>;>;
+T Ljava/util/concurrent/Callable<TT;>;
+T Ljava/util/concurrent/Callable<TV;>;
+T Ljava/util/concurrent/ConcurrentHashMap$HashEntry<TK;TV;>;
+T Ljava/util/concurrent/ConcurrentLinkedQueue$Node<TE;>;
+T Ljava/util/concurrent/CopyOnWriteArrayList<TE;>;
+T Ljava/util/concurrent/DelayQueue<Ljava/util/concurrent/ScheduledThreadPoolExecutor$ScheduledFutureTask;>;
+T Ljava/util/concurrent/FutureTask<TV;>.Sync;
+T Ljava/util/concurrent/LinkedBlockingQueue$Node<TE;>;
+T Ljava/util/concurrent/atomic/AtomicReference<Ljava/util/concurrent/atomic/AtomicMarkableReference$ReferenceBooleanPair<TV;>;>;
+T Ljava/util/concurrent/atomic/AtomicReference<Ljava/util/concurrent/atomic/AtomicStampedReference$ReferenceIntegerPair<TV;>;>;
+T Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<Ljava/io/BufferedInputStream;[B>;
+T Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<Ljava/util/concurrent/ConcurrentLinkedQueue$Node;Ljava/lang/Object;>;
+T Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<Ljava/util/concurrent/ConcurrentLinkedQueue$Node;Ljava/util/concurrent/ConcurrentLinkedQueue$Node;>;
+T Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<Ljava/util/concurrent/ConcurrentLinkedQueue;Ljava/util/concurrent/ConcurrentLinkedQueue$Node;>;
+T Lsun/misc/LRUCache<Ljava/lang/String;Ljava/util/regex/Pattern;>;
+T TD;
+T TE;
+T TK;
+T TT;
+T TV;
+T [TE;
+T [TK;
+T [TT;
+T [TV;
+C <ActualEdgeType:Ledu/umd/cs/findbugs/graph/AbstractEdge<TActualEdgeType;TVertexType;>;VertexType:Ledu/umd/cs/findbugs/graph/AbstractVertex<TActualEdgeType;TVertexType;>;>Ljava/lang/Object;Ledu/umd/cs/findbugs/graph/GraphEdge<TActualEdgeType;TVertexType;>;
+C <ActualEdgeType:Ljava/lang/Object;VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex<TVertexType;>;>Ljava/lang/Object;Ljava/lang/Comparable<TActualEdgeType;>;
+C <ActualVertexType:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Comparable<TActualVertexType;>;
+C <Analysis:Ljava/lang/Object;>Ljava/lang/Object;
+C <AnalysisResult:Ljava/lang/Object;>Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<TAnalysisResult;>;
+C <EdgeType::Ledu/umd/cs/findbugs/graph/GraphEdge<TEdgeType;TVertexType;>;VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex<TVertexType;>;>Ljava/lang/Object;
+C <EdgeType:Ledu/umd/cs/findbugs/graph/AbstractEdge<TEdgeType;TActualVertexType;>;ActualVertexType:Ledu/umd/cs/findbugs/graph/AbstractVertex<TEdgeType;TActualVertexType;>;>Ljava/lang/Object;Ledu/umd/cs/findbugs/graph/GraphVertex<TActualVertexType;>;
+C <EdgeType:Ledu/umd/cs/findbugs/graph/AbstractEdge<TEdgeType;TVertexType;>;VertexType:Ledu/umd/cs/findbugs/graph/AbstractVertex<TEdgeType;TVertexType;>;>Ljava/lang/Object;Ledu/umd/cs/findbugs/graph/Graph<TEdgeType;TVertexType;>;
+C <EdgeType:Ledu/umd/cs/findbugs/graph/AbstractEdge<TEdgeType;TVertexType;>;VertexType:Ledu/umd/cs/findbugs/graph/AbstractVertex<TEdgeType;TVertexType;>;>Ljava/lang/Object;Ljava/util/Iterator<TEdgeType;>;
+C <Fact:Ljava/lang/Object;>Ledu/umd/cs/findbugs/ba/AbstractDataflowAnalysis<TFact;>;
+C <Fact:Ljava/lang/Object;>Ljava/lang/Object;
+C <Fact:Ljava/lang/Object;>Ljava/lang/Object;Ledu/umd/cs/findbugs/ba/DataflowAnalysis<TFact;>;
+C <Fact:Ljava/lang/Object;AnalysisType::Ledu/umd/cs/findbugs/ba/DataflowAnalysis<TFact;>;>Ljava/lang/Object;
+C <Fact:Ljava/lang/Object;AnalysisType:Ledu/umd/cs/findbugs/ba/AbstractDataflowAnalysis<TFact;>;>Ledu/umd/cs/findbugs/ba/CFGPrinter;
+C <Fact:Ljava/lang/Object;AnalysisType:Ledu/umd/cs/findbugs/ba/AbstractDataflowAnalysis<TFact;>;>Ljava/lang/Object;
+C <GraphType::Ledu/umd/cs/findbugs/graph/Graph<TEdgeType;TVertexType;>;EdgeType::Ledu/umd/cs/findbugs/graph/GraphEdge<TEdgeType;TVertexType;>;VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex<TVertexType;>;>Ledu/umd/cs/findbugs/graph/AbstractDepthFirstSearch<TGraphType;TEdgeType;TVertexType;>;
+C <GraphType::Ledu/umd/cs/findbugs/graph/Graph<TEdgeType;TVertexType;>;EdgeType::Ledu/umd/cs/findbugs/graph/GraphEdge<TEdgeType;TVertexType;>;VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex<TVertexType;>;>Ljava/lang/Object;
+C <GraphType::Ledu/umd/cs/findbugs/graph/Graph<TEdgeType;TVertexType;>;EdgeType::Ledu/umd/cs/findbugs/graph/GraphEdge<TEdgeType;TVertexType;>;VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex<TVertexType;>;>Ljava/lang/Object;Ledu/umd/cs/findbugs/graph/DFSEdgeTypes;
+C <Resource:Ljava/lang/Object;>Ledu/umd/cs/findbugs/ba/FrameDataflowAnalysis<Ledu/umd/cs/findbugs/ba/ResourceValue;Ledu/umd/cs/findbugs/ba/ResourceValueFrame;>;Ledu/umd/cs/findbugs/ba/EdgeTypes;
+C <Resource:Ljava/lang/Object;>Ljava/lang/Object;
+C <Resource:Ljava/lang/Object;ResourceTrackerType::Ledu/umd/cs/findbugs/ba/ResourceTracker<TResource;>;>Ljava/lang/Object;
+C <Resource:Ljava/lang/Object;ResourceTrackerType::Ledu/umd/cs/findbugs/ba/ResourceTracker<TResource;>;>Ljava/lang/Object;Ledu/umd/cs/findbugs/Detector;
+C <T:Ljava/lang/Object;>Ljava/lang/Object;
+C <Value:Ljava/lang/Object;FrameType:Ledu/umd/cs/findbugs/ba/Frame<TValue;>;>Ljava/lang/Object;Lorg/apache/bcel/generic/Visitor;
+C <ValueType:Ljava/lang/Object;>Ljava/lang/Object;Ledu/umd/cs/findbugs/ba/Debug;
+C <ValueType:Ljava/lang/Object;FrameType:Ledu/umd/cs/findbugs/ba/Frame<TValueType;>;>Ledu/umd/cs/findbugs/ba/ForwardDataflowAnalysis<TFrameType;>;
+C <VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex;>Ljava/lang/Object;Ljava/util/Comparator<TVertexType;>;
+C <VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex<TVertexType;>;>Ljava/lang/Object;
+C <VertexType::Ledu/umd/cs/findbugs/graph/GraphVertex<TVertexType;>;>Ljava/lang/Object;Ledu/umd/cs/findbugs/graph/SearchTreeCallback<TVertexType;>;
+C Ledu/umd/cs/findbugs/ba/AbstractFrameModelingVisitor<Ledu/umd/cs/findbugs/ba/IsNullValue;Ledu/umd/cs/findbugs/ba/IsNullValueFrame;>;
+C Ledu/umd/cs/findbugs/ba/AbstractFrameModelingVisitor<Ledu/umd/cs/findbugs/ba/ResourceValue;Ledu/umd/cs/findbugs/ba/ResourceValueFrame;>;
+C Ledu/umd/cs/findbugs/ba/AbstractFrameModelingVisitor<Ledu/umd/cs/findbugs/ba/ValueNumber;Ledu/umd/cs/findbugs/ba/ValueNumberFrame;>;Ledu/umd/cs/findbugs/ba/Debug;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysisFeatures;
+C Ledu/umd/cs/findbugs/ba/AbstractFrameModelingVisitor<Ledu/umd/cs/findbugs/ba/type/Type;Ledu/umd/cs/findbugs/ba/BetterTypeFrame;>;
+C Ledu/umd/cs/findbugs/ba/AbstractFrameModelingVisitor<Lorg/apache/bcel/generic/Type;Ledu/umd/cs/findbugs/ba/TypeFrame;>;Lorg/apache/bcel/Constants;Ledu/umd/cs/findbugs/ba/Debug;
+C Ledu/umd/cs/findbugs/ba/BackwardDataflowAnalysis<Ljava/util/BitSet;>;Ledu/umd/cs/findbugs/ba/Debug;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/CFG;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/BlockType;Ledu/umd/cs/findbugs/ba/BlockTypeAnalysis;>;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/DominatorsAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/IsNullValueDataflow;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/LiveLocalStoreDataflow;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/LockDataflow;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/PostDominatorsAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/ReturnPathDataflow;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/TypeDataflow;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/ValueNumberDataflow;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$NoDataflowAnalysisFactory<Ledu/umd/cs/findbugs/ba/DepthFirstSearch;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$NoDataflowAnalysisFactory<Ledu/umd/cs/findbugs/ba/ReverseDepthFirstSearch;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Ledu/umd/cs/findbugs/ba/ExceptionSetFactory;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Ledu/umd/cs/findbugs/ba/LoadedFieldSet;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Ljava/util/BitSet;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Lorg/apache/bcel/generic/MethodGen;>;
+C Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<[Ljava/lang/String;>;
+C Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/IsNullValueFrame;Ledu/umd/cs/findbugs/ba/IsNullValueAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/LockCount;Ledu/umd/cs/findbugs/ba/LockCountAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/LockSet;Ledu/umd/cs/findbugs/ba/LockAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ReturnPath;Ledu/umd/cs/findbugs/ba/ReturnPathAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/TypeFrame;Ledu/umd/cs/findbugs/ba/TypeAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/Dataflow<Ljava/util/BitSet;Ledu/umd/cs/findbugs/ba/LiveLocalStoreAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/IsNullValueFrame;Ledu/umd/cs/findbugs/ba/IsNullValueAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/LockCount;Ledu/umd/cs/findbugs/ba/LockCountAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/LockSet;Ledu/umd/cs/findbugs/ba/LockAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/ResourceValueFrame;Ledu/umd/cs/findbugs/ba/ResourceValueAnalysis<TResource;>;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/ReturnPath;Ledu/umd/cs/findbugs/ba/ReturnPathAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/StackDepth;Ledu/umd/cs/findbugs/ba/StackDepthAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/TypeFrame;Ledu/umd/cs/findbugs/ba/TypeAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ljava/util/BitSet;Ledu/umd/cs/findbugs/ba/LiveLocalStoreAnalysis;>;
+C Ledu/umd/cs/findbugs/ba/ForwardDataflowAnalysis<Ledu/umd/cs/findbugs/ba/LockCount;>;
+C Ledu/umd/cs/findbugs/ba/ForwardDataflowAnalysis<Ledu/umd/cs/findbugs/ba/LockSet;>;
+C Ledu/umd/cs/findbugs/ba/ForwardDataflowAnalysis<Ledu/umd/cs/findbugs/ba/ReturnPath;>;Ledu/umd/cs/findbugs/ba/EdgeTypes;
+C Ledu/umd/cs/findbugs/ba/ForwardDataflowAnalysis<Ledu/umd/cs/findbugs/ba/StackDepth;>;
+C Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/IsNullValue;>;
+C Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/ResourceValue;>;
+C Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/ValueNumber;>;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysisFeatures;
+C Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/type/Type;>;
+C Ledu/umd/cs/findbugs/ba/Frame<Lorg/apache/bcel/generic/Type;>;
+C Ledu/umd/cs/findbugs/ba/FrameDataflowAnalysis<Ledu/umd/cs/findbugs/ba/IsNullValue;Ledu/umd/cs/findbugs/ba/IsNullValueFrame;>;Ledu/umd/cs/findbugs/ba/EdgeTypes;
+C Ledu/umd/cs/findbugs/ba/FrameDataflowAnalysis<Ledu/umd/cs/findbugs/ba/ValueNumber;Ledu/umd/cs/findbugs/ba/ValueNumberFrame;>;
+C Ledu/umd/cs/findbugs/ba/FrameDataflowAnalysis<Ledu/umd/cs/findbugs/ba/type/Type;Ledu/umd/cs/findbugs/ba/BetterTypeFrame;>;
+C Ledu/umd/cs/findbugs/ba/FrameDataflowAnalysis<Lorg/apache/bcel/generic/Type;Ledu/umd/cs/findbugs/ba/TypeFrame;>;Ledu/umd/cs/findbugs/ba/EdgeTypes;
+C Ledu/umd/cs/findbugs/graph/AbstractEdge<Ledu/umd/cs/findbugs/CallGraphEdge;Ledu/umd/cs/findbugs/CallGraphNode;>;
+C Ledu/umd/cs/findbugs/graph/AbstractEdge<Ledu/umd/cs/findbugs/ba/Edge;Ledu/umd/cs/findbugs/ba/BasicBlock;>;Ledu/umd/cs/findbugs/ba/EdgeTypes;Ledu/umd/cs/findbugs/ba/Debug;
+C Ledu/umd/cs/findbugs/graph/AbstractEdge<Ledu/umd/cs/findbugs/ba/type/InheritanceGraphEdge;Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+C Ledu/umd/cs/findbugs/graph/AbstractEdge<Ledu/umd/cs/findbugs/plan/ConstraintEdge;Ledu/umd/cs/findbugs/plan/DetectorNode;>;
+C Ledu/umd/cs/findbugs/graph/AbstractGraph<Ledu/umd/cs/findbugs/CallGraphEdge;Ledu/umd/cs/findbugs/CallGraphNode;>;
+C Ledu/umd/cs/findbugs/graph/AbstractGraph<Ledu/umd/cs/findbugs/ba/Edge;Ledu/umd/cs/findbugs/ba/BasicBlock;>;Ledu/umd/cs/findbugs/ba/Debug;
+C Ledu/umd/cs/findbugs/graph/AbstractGraph<Ledu/umd/cs/findbugs/ba/type/InheritanceGraphEdge;Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+C Ledu/umd/cs/findbugs/graph/AbstractGraph<Ledu/umd/cs/findbugs/plan/ConstraintEdge;Ledu/umd/cs/findbugs/plan/DetectorNode;>;
+C Ledu/umd/cs/findbugs/graph/AbstractVertex<Ledu/umd/cs/findbugs/CallGraphEdge;Ledu/umd/cs/findbugs/CallGraphNode;>;
+C Ledu/umd/cs/findbugs/graph/AbstractVertex<Ledu/umd/cs/findbugs/ba/Edge;Ledu/umd/cs/findbugs/ba/BasicBlock;>;Ledu/umd/cs/findbugs/ba/Debug;
+C Ledu/umd/cs/findbugs/graph/AbstractVertex<Ledu/umd/cs/findbugs/ba/type/InheritanceGraphEdge;Ledu/umd/cs/findbugs/ba/type/ObjectType;>;Ledu/umd/cs/findbugs/ba/type/ReferenceType;
+C Ledu/umd/cs/findbugs/graph/AbstractVertex<Ledu/umd/cs/findbugs/plan/ConstraintEdge;Ledu/umd/cs/findbugs/plan/DetectorNode;>;
+C Ledu/umd/cs/findbugs/graph/DepthFirstSearch<Ledu/umd/cs/findbugs/ba/CFG;Ledu/umd/cs/findbugs/ba/Edge;Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+C Ledu/umd/cs/findbugs/graph/DepthFirstSearch<TGraphType;TEdgeType;TVertexType;>;
+C Ledu/umd/cs/findbugs/graph/ReverseDepthFirstSearch<Ledu/umd/cs/findbugs/ba/CFG;Ledu/umd/cs/findbugs/ba/Edge;Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+C Ljava/lang/Object;Ledu/umd/cs/findbugs/ba/DataflowAnalysis<Ledu/umd/cs/findbugs/ba/BlockType;>;
+C Ljava/lang/Object;Ledu/umd/cs/findbugs/ba/DataflowAnalysis<Ljava/util/BitSet;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ledu/umd/cs/findbugs/BugAnnotation;>;Ledu/umd/cs/findbugs/xml/XMLWriteable;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ledu/umd/cs/findbugs/CountBugs$Key;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ledu/umd/cs/findbugs/ba/AvailableLoad;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ledu/umd/cs/findbugs/ba/Location;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+C Ljava/lang/Object;Ljava/lang/Comparable<Ledu/umd/cs/findbugs/ba/XField;>;
+C Ljava/lang/Object;Ljava/util/Comparator<Ledu/umd/cs/findbugs/BugInstance;>;
+C Ljava/lang/Object;Ljava/util/Comparator<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugAnnotation;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ledu/umd/cs/findbugs/CallSite;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Location;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/lang/String;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Ljava/util/Set<TVertexType;>;>;
+C Ljava/lang/Object;Ljava/util/Iterator<Lorg/apache/bcel/generic/InstructionHandle;>;
+C Ljava/lang/Object;Ljava/util/Iterator<TVertexType;>;
+C Ljava/util/LinkedHashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/SourceFile;>;
+C Ljava/util/LinkedHashMap<Lorg/apache/bcel/classfile/JavaClass;Ledu/umd/cs/findbugs/ba/ClassContext;>;
+M ()Ljava/util/Collection<Ledu/umd/cs/findbugs/BugInstance;>;
+M ()Ljava/util/Collection<Ledu/umd/cs/findbugs/BugPattern;>;
+M ()Ljava/util/Collection<Ljava/lang/String;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugAnnotation;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugCode;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugInstance;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugPattern;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/CallSite;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/DetectorFactory;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/DetectorOrderingConstraint;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/Matcher;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/Plugin;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Location;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Target;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/bcp/ByteCodePatternMatch;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/bcp/PatternElementMatch;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/bcp/PatternMatcher$State;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;>;
+M ()Ljava/util/Iterator<Ledu/umd/cs/findbugs/xml/XMLAttributeList$NameValuePair;>;
+M ()Ljava/util/Iterator<Ljava/lang/String;>;
+M ()Ljava/util/Iterator<Ljava/util/List<Ledu/umd/cs/findbugs/ba/Edge;>;>;
+M ()Ljava/util/Iterator<Ljava/util/Set<TVertexType;>;>;
+M ()Ljava/util/Iterator<Lorg/apache/bcel/classfile/Method;>;
+M ()Ljava/util/Iterator<Lorg/apache/bcel/generic/InstructionHandle;>;
+M ()Ljava/util/Iterator<TEdgeType;>;
+M ()Ljava/util/Iterator<TFact;>;
+M ()Ljava/util/Iterator<TResource;>;
+M ()Ljava/util/Iterator<TVertexType;>;
+M ()Ljava/util/List<Ledu/umd/cs/findbugs/DetectorFactory;>;
+M ()Ljava/util/List<Ljava/lang/String;>;
+M ()Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/DetectorFactory;>;
+M ()Ljava/util/Set<Ljava/lang/String;>;
+M ()Ljava/util/Set<TVertexType;>;
+M ()TActualEdgeType;
+M ()TAnalysis;
+M ()TAnalysisType;
+M ()TEdgeType;
+M ()TFact;
+M ()TFrameType;
+M ()TGraphType;
+M ()TT;
+M ()TValue;
+M ()TValueType;
+M ()TVertexType;
+M (I)Ljava/util/Collection<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+M (I)TValueType;
+M (ITValueType;)V
+M (Ledu/umd/cs/findbugs/BugCollection;)Ljava/util/SortedSet<Ledu/umd/cs/findbugs/BugInstance;>;
+M (Ledu/umd/cs/findbugs/BugReporter;Ljava/util/Set<Ljava/lang/String;>;)V
+M (Ledu/umd/cs/findbugs/FindBugs$ArchiveWorkListItem;Ljava/util/LinkedList<Ledu/umd/cs/findbugs/FindBugs$ArchiveWorkListItem;>;Ljava/util/List<Ljava/lang/String;>;Ljava/util/List<Ljava/lang/String;>;)V
+M (Ledu/umd/cs/findbugs/SortedBugCollection;)Ljava/util/SortedSet<Ledu/umd/cs/findbugs/BugInstance;>;
+M (Ledu/umd/cs/findbugs/ba/BasicBlock;)Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$EscapeTarget;>;
+M (Ledu/umd/cs/findbugs/ba/BasicBlock;)Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Edge;>;
+M (Ledu/umd/cs/findbugs/ba/BasicBlock;)Ljava/util/Iterator<Lorg/apache/bcel/generic/InstructionHandle;>;
+M (Ledu/umd/cs/findbugs/ba/BasicBlock;)TFact;
+M (Ledu/umd/cs/findbugs/ba/BasicBlock;Lorg/apache/bcel/generic/InstructionHandle;Lorg/apache/bcel/generic/ConstantPoolGen;)TResource;
+M (Ledu/umd/cs/findbugs/ba/BasicBlock;Lorg/apache/bcel/generic/InstructionHandle;Lorg/apache/bcel/generic/ConstantPoolGen;TResource;Ledu/umd/cs/findbugs/ba/ResourceValueFrame;)Z
+M (Ledu/umd/cs/findbugs/ba/BasicBlock;Lorg/apache/bcel/generic/InstructionHandle;TFact;TFact;)V
+M (Ledu/umd/cs/findbugs/ba/CFG;Ledu/umd/cs/findbugs/ba/Dataflow<TFact;TAnalysisType;>;)V
+M (Ledu/umd/cs/findbugs/ba/CFG;Ledu/umd/cs/findbugs/ba/Dataflow<TFact;TAnalysisType;>;TAnalysisType;)V
+M (Ledu/umd/cs/findbugs/ba/CFG;Ljava/util/Comparator<Ledu/umd/cs/findbugs/ba/BasicBlock;>;)V
+M (Ledu/umd/cs/findbugs/ba/CFG;TAnalysisType;)V
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/IsNullValueFrame;Ledu/umd/cs/findbugs/ba/IsNullValueAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/LockCount;Ledu/umd/cs/findbugs/ba/LockCountAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/LockSet;Ledu/umd/cs/findbugs/ba/LockAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ResourceValueFrame;Ledu/umd/cs/findbugs/ba/ResourceValueAnalysis<TResource;>;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ReturnPath;Ledu/umd/cs/findbugs/ba/ReturnPathAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/StackDepth;Ledu/umd/cs/findbugs/ba/StackDepthAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/TypeFrame;Ledu/umd/cs/findbugs/ba/TypeAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ljava/util/BitSet;Ledu/umd/cs/findbugs/ba/LiveLocalStoreAnalysis;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<TFact;TAnalysisType;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;)TResourceTrackerType;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;TResourceTrackerType;)Ledu/umd/cs/findbugs/ResourceCollection<TResource;>;
+M (Ledu/umd/cs/findbugs/ba/ClassContext;Lorg/apache/bcel/classfile/Method;TResourceTrackerType;Ledu/umd/cs/findbugs/ResourceCollection<TResource;>;)V
+M (Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysis;>;)V
+M (Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/ResourceValue;>;)V
+M (Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/ResourceValue;>;)Z
+M (Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/ValueNumber;>;)V
+M (Ledu/umd/cs/findbugs/ba/Frame<TValueType;>;)V
+M (Ledu/umd/cs/findbugs/ba/Frame<TValueType;>;)Z
+M (Ledu/umd/cs/findbugs/ba/Location;)TFact;
+M (Ledu/umd/cs/findbugs/ba/Location;)TResource;
+M (Ledu/umd/cs/findbugs/ba/Location;TResource;)V
+M (Ledu/umd/cs/findbugs/ba/type/ObjectType;Ledu/umd/cs/findbugs/ba/type/TypeRepository;)Ljava/util/Set<Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+M (Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;)V
+M (Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;Ledu/umd/cs/findbugs/graph/Transpose<TGraphType;TEdgeType;TVertexType;>;)Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;
+M (Ledu/umd/cs/findbugs/graph/SearchTreeCallback<TVertexType;>;)V
+M (Ledu/umd/cs/findbugs/graph/VertexChooser<TVertexType;>;)V
+M (Ledu/umd/cs/findbugs/xml/XMLOutput;Ljava/lang/String;Ljava/util/Collection<Ljava/lang/String;>;)V
+M (Ledu/umd/cs/findbugs/xml/XMLOutput;Ljava/lang/String;Ljava/util/Iterator<Ljava/lang/String;>;)V
+M (Ledu/umd/cs/findbugs/xml/XMLOutput;Ljava/util/Collection<+Ledu/umd/cs/findbugs/xml/XMLWriteable;>;)V
+M (Ljava/lang/String;)Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/InnerClassAccess;>;
+M (Ljava/lang/String;)Ljava/util/Set<Ledu/umd/cs/findbugs/CountBugs$Key;>;
+M (Ljava/lang/String;)Ljava/util/Set<Ljava/lang/String;>;
+M (Ljava/lang/String;Ledu/umd/cs/findbugs/Project;)Ljava/util/TreeSet<Ledu/umd/cs/findbugs/BugInstance;>;
+M (Ljava/lang/String;Ljava/util/Collection<Ljava/lang/String;>;)V
+M (Ljava/lang/String;Ljava/util/List<Ljava/lang/String;>;)V
+M (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/CategorizeBugs$Stats;>;)V
+M (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/CategorizeBugs$Stats;>;ZI)V
+M (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/plan/DetectorNode;>;Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/DetectorFactory;>;Ledu/umd/cs/findbugs/plan/ConstraintGraph;)Ledu/umd/cs/findbugs/plan/DetectorNode;
+M (Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;Z)V
+M (Ljava/net/URL;Ledu/umd/cs/findbugs/Project$WorkList;Ljava/util/List<Ljava/lang/String;>;)V
+M (Ljava/net/URL;Ljava/util/LinkedList<Ledu/umd/cs/findbugs/FindBugs$ArchiveWorkListItem;>;Ljava/util/List<Ljava/lang/String;>;)V
+M (Ljava/util/ArrayList<Ledu/umd/cs/findbugs/graph/AbstractDepthFirstSearch<TGraphType;TEdgeType;TVertexType;>.Visit;>;TEdgeType;)V
+M (Ljava/util/BitSet;)Ljava/util/Collection<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+M (Ljava/util/Collection<+Ledu/umd/cs/findbugs/xml/XMLWriteable;>;)V
+M (Ljava/util/Collection<Ledu/umd/cs/findbugs/BugInstance;>;)V
+M (Ljava/util/Collection<Ljava/lang/String;>;)Ljava/lang/String;
+M (Ljava/util/Collection<Ljava/lang/String;>;Ledu/umd/cs/findbugs/URLClassPathRepository;)V
+M (Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugAnnotation;>;)V
+M (Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Edge;>;)V
+M (Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/Edge;>;)V
+M (Ljava/util/List<Ledu/umd/cs/findbugs/DetectorOrderingConstraint;>;Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/DetectorFactory;>;Ledu/umd/cs/findbugs/plan/AnalysisPass;)V
+M (Ljava/util/List<Ljava/lang/String;>;)V
+M (Ljava/util/List<Ljava/lang/String;>;Ljava/lang/String;)Z
+M (Ljava/util/List<Lorg/dom4j/Document;>;Ljava/lang/String;)V
+M (Ljava/util/List<Lorg/dom4j/Document;>;Ljava/lang/String;Ljava/lang/String;)Lorg/dom4j/Node;
+M (Ljava/util/Map$Entry<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/SourceFile;>;)Z
+M (Ljava/util/Map$Entry<Lorg/apache/bcel/classfile/JavaClass;Ledu/umd/cs/findbugs/ba/ClassContext;>;)Z
+M (Ljava/util/Map<Ledu/umd/cs/findbugs/ba/BasicBlock;Ledu/umd/cs/findbugs/ba/BlockType;>;Ledu/umd/cs/findbugs/ba/BasicBlock;)Ledu/umd/cs/findbugs/ba/BlockType;
+M (Ljava/util/Map<Ledu/umd/cs/findbugs/ba/BasicBlock;Ljava/util/BitSet;>;Ledu/umd/cs/findbugs/ba/BasicBlock;)Ljava/util/BitSet;
+M (Ljava/util/Map<Ledu/umd/cs/findbugs/ba/BasicBlock;TFact;>;Ledu/umd/cs/findbugs/ba/BasicBlock;)TFact;
+M (Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/plan/DetectorNode;>;Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/DetectorFactory;>;Ljava/util/List<Ledu/umd/cs/findbugs/DetectorOrderingConstraint;>;)Ledu/umd/cs/findbugs/plan/ConstraintGraph;
+M (Ljava/util/Set<TVertexType;>;)TVertexType;
+M (Ljava/util/Set<TVertexType;>;)V
+M (Ljava/util/Set<TVertexType;>;TGraphType;Ledu/umd/cs/findbugs/graph/VertexCombinator<TVertexType;>;Ledu/umd/cs/findbugs/graph/GraphToolkit<TGraphType;TEdgeType;TVertexType;>;)V
+M (Lorg/apache/bcel/classfile/JavaClass;Ljava/util/Set<Ledu/umd/cs/findbugs/ba/XField;>;)V
+M (Lorg/apache/bcel/classfile/JavaClass;Lorg/apache/bcel/generic/MethodGen;Ledu/umd/cs/findbugs/ba/CFG;Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ResourceValueFrame;Ledu/umd/cs/findbugs/ba/ResourceValueAnalysis<TResource;>;>;TResource;)V
+M (Lorg/apache/bcel/classfile/Method;)Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/BlockType;Ledu/umd/cs/findbugs/ba/BlockTypeAnalysis;>;
+M (Lorg/apache/bcel/classfile/Method;)Ljava/util/Set<Ledu/umd/cs/findbugs/ba/XField;>;
+M (Lorg/apache/bcel/classfile/Method;)TAnalysis;
+M (Lorg/apache/bcel/classfile/Method;)TAnalysisResult;
+M (Lorg/apache/bcel/classfile/Method;Ljava/util/Set<Ledu/umd/cs/findbugs/ba/XField;>;)V
+M (Lorg/apache/bcel/generic/Instruction;Lorg/apache/bcel/generic/ConstantPoolGen;)TValueType;
+M (Lorg/apache/bcel/generic/InstructionHandle;)Ljava/util/List<Lorg/apache/bcel/generic/CodeExceptionGen;>;
+M (Lorg/apache/bcel/generic/InstructionHandle;Ledu/umd/cs/findbugs/ba/BasicBlock;TFact;)V
+M (Lorg/apache/bcel/generic/MethodGen;Ledu/umd/cs/findbugs/ba/CFG;Ledu/umd/cs/findbugs/ba/DepthFirstSearch;Ledu/umd/cs/findbugs/ba/ResourceTracker<TResource;>;TResource;)V
+M (TActualEdgeType;)I
+M (TActualEdgeType;)V
+M (TActualVertexType;)I
+M (TAnalysis;)V
+M (TEdgeType;)I
+M (TEdgeType;)TVertexType;
+M (TEdgeType;)V
+M (TEdgeType;I)V
+M (TEdgeType;TEdgeType;)V
+M (TFact;)Ljava/lang/String;
+M (TFact;)V
+M (TFact;)Z
+M (TFact;Ledu/umd/cs/findbugs/ba/Edge;TFact;)V
+M (TFact;TFact;)V
+M (TFact;TFact;)Z
+M (TFrameType;)V
+M (TFrameType;)Z
+M (TFrameType;ITValueType;TValueType;)TValueType;
+M (TFrameType;TFrameType;)TFrameType;
+M (TFrameType;TFrameType;)V
+M (TFrameType;TFrameType;)Z
+M (TGraphType;)V
+M (TGraphType;Ledu/umd/cs/findbugs/graph/GraphToolkit<TGraphType;TEdgeType;TVertexType;>;)TGraphType;
+M (TGraphType;Ledu/umd/cs/findbugs/graph/GraphToolkit<TGraphType;TEdgeType;TVertexType;>;)V
+M (TGraphType;TVertexType;)Ljava/util/Iterator<TEdgeType;>;
+M (TResource;)V
+M (TResource;)Z
+M (TResource;I)Z
+M (TResource;Lorg/apache/bcel/generic/ConstantPoolGen;)Ledu/umd/cs/findbugs/ba/ResourceValueFrameModelingVisitor;
+M (TT;)V
+M (TValueType;)Ljava/lang/String;
+M (TValueType;)V
+M (TVertexType;)I
+M (TVertexType;)Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;
+M (TVertexType;)Ljava/util/Iterator<TEdgeType;>;
+M (TVertexType;)Ljava/util/Iterator<TVertexType;>;
+M (TVertexType;)TVertexType;
+M (TVertexType;)V
+M (TVertexType;)Z
+M (TVertexType;I)V
+M (TVertexType;TVertexType;)I
+M (TVertexType;TVertexType;)TEdgeType;
+M (TVertexType;TVertexType;)V
+M (ZI)Ljava/util/List<Ledu/umd/cs/findbugs/BugInstance;>;
+M ([TValueType;)V
+M <T:Ljava/lang/Object;>(Ljava/util/Iterator<TT;>;Ljava/util/Collection<TT;>;)V
+T Ledu/umd/cs/findbugs/AnalysisLocal<TT;>;
+T Ledu/umd/cs/findbugs/ResourceCollection<TResource;>;
+T Ledu/umd/cs/findbugs/ResourceTrackingDetector<TResource;TResourceTrackerType;>;
+T Ledu/umd/cs/findbugs/ba/AbstractDataflowAnalysis<TFact;>;
+T Ledu/umd/cs/findbugs/ba/AbstractFrameModelingVisitor<TValue;TFrameType;>;
+T Ledu/umd/cs/findbugs/ba/BackwardDataflowAnalysis<TFact;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/BlockType;Ledu/umd/cs/findbugs/ba/BlockTypeAnalysis;>;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/DominatorsAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/IsNullValueDataflow;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/LiveLocalStoreDataflow;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/LockDataflow;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/PostDominatorsAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/ReturnPathDataflow;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/TypeDataflow;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<Ledu/umd/cs/findbugs/ba/ValueNumberDataflow;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisFactory<TAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisResult<TAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoDataflowAnalysisFactory<Ledu/umd/cs/findbugs/ba/DepthFirstSearch;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoDataflowAnalysisFactory<Ledu/umd/cs/findbugs/ba/ReverseDepthFirstSearch;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoDataflowAnalysisFactory<TAnalysisResult;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Ledu/umd/cs/findbugs/ba/ExceptionSetFactory;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Ledu/umd/cs/findbugs/ba/LoadedFieldSet;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Ljava/util/BitSet;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<Lorg/apache/bcel/generic/MethodGen;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<TAnalysisResult;>;
+T Ledu/umd/cs/findbugs/ba/ClassContext$NoExceptionAnalysisFactory<[Ljava/lang/String;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/BlockType;Ledu/umd/cs/findbugs/ba/BlockTypeAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/LockCount;Ledu/umd/cs/findbugs/ba/LockCountAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ResourceValueFrame;Ledu/umd/cs/findbugs/ba/ResourceValueAnalysis<TResource;>;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/StackDepth;Ledu/umd/cs/findbugs/ba/StackDepthAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<Ljava/util/BitSet;Ledu/umd/cs/findbugs/ba/DominatorsAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<Ljava/util/BitSet;Ledu/umd/cs/findbugs/ba/PostDominatorsAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/Dataflow<TFact;TAnalysisType;>;
+T Ledu/umd/cs/findbugs/ba/DataflowCFGPrinter<TFact;TAnalysisType;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver.1;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/IsNullValueFrame;Ledu/umd/cs/findbugs/ba/IsNullValueAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/LockCount;Ledu/umd/cs/findbugs/ba/LockCountAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/LockSet;Ledu/umd/cs/findbugs/ba/LockAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/ResourceValueFrame;Ledu/umd/cs/findbugs/ba/ResourceValueAnalysis<TResource;>;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/ReturnPath;Ledu/umd/cs/findbugs/ba/ReturnPathAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/StackDepth;Ledu/umd/cs/findbugs/ba/StackDepthAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/TypeFrame;Ledu/umd/cs/findbugs/ba/TypeAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;Ledu/umd/cs/findbugs/ba/ValueNumberAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<Ljava/util/BitSet;Ledu/umd/cs/findbugs/ba/LiveLocalStoreAnalysis;>;
+T Ledu/umd/cs/findbugs/ba/DataflowTestDriver<TFact;TAnalysisType;>;
+T Ledu/umd/cs/findbugs/ba/ForwardDataflowAnalysis<TFact;>;
+T Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/ResourceValue;>;
+T Ledu/umd/cs/findbugs/ba/Frame<Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ledu/umd/cs/findbugs/ba/Frame<TValueType;>;
+T Ledu/umd/cs/findbugs/ba/FrameDataflowAnalysis<TValueType;TFrameType;>;
+T Ledu/umd/cs/findbugs/ba/ResourceTracker<TResource;>;
+T Ledu/umd/cs/findbugs/ba/ResourceValueAnalysis<TResource;>;
+T Ledu/umd/cs/findbugs/ba/ResourceValueAnalysisTestDriver.1;
+T Ledu/umd/cs/findbugs/ba/ResourceValueAnalysisTestDriver.2;
+T Ledu/umd/cs/findbugs/ba/ResourceValueAnalysisTestDriver<TResource;TResourceTrackerType;>;
+T Ledu/umd/cs/findbugs/graph/AbstractDepthFirstSearch<TGraphType;TEdgeType;TVertexType;>.Visit;
+T Ledu/umd/cs/findbugs/graph/AbstractDepthFirstSearch<TGraphType;TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/AbstractEdge<TActualEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/AbstractGraph$IncomingEdgeIterator<TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/AbstractGraph$OutgoingEdgeIterator<TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/AbstractGraph.1;
+T Ledu/umd/cs/findbugs/graph/AbstractGraph.2;
+T Ledu/umd/cs/findbugs/graph/AbstractGraph<TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/AbstractVertex<TEdgeType;TActualVertexType;>;
+T Ledu/umd/cs/findbugs/graph/DepthFirstSearch<Ledu/umd/cs/findbugs/plan/ConstraintGraph;Ledu/umd/cs/findbugs/plan/ConstraintEdge;Ledu/umd/cs/findbugs/plan/DetectorNode;>;
+T Ledu/umd/cs/findbugs/graph/DepthFirstSearch<TGraphType;TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/GraphToolkit<TGraphType;TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/MergeVertices<TGraphType;TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/ReverseDepthFirstSearch<TGraphType;TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/SearchTreeBuilder<TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/SearchTreeCallback<TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/StronglyConnectedComponents.1;
+T Ledu/umd/cs/findbugs/graph/StronglyConnectedComponents<TGraphType;TEdgeType;TVertexType;>.SCCSetIterator;
+T Ledu/umd/cs/findbugs/graph/StronglyConnectedComponents<TGraphType;TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/Transpose<TGraphType;TEdgeType;TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/VertexChooser<TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/VertexCombinator<TVertexType;>;
+T Ledu/umd/cs/findbugs/graph/VisitationTimeComparator<TVertexType;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/BugAnnotation;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/BugCode;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/BugPattern;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/Detector;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/DetectorFactory;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/DetectorOrderingConstraint;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/graph/AbstractDepthFirstSearch<TGraphType;TEdgeType;TVertexType;>.Visit;>;
+T Ljava/util/ArrayList<Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;>;
+T Ljava/util/ArrayList<Ljava/io/File;>;
+T Ljava/util/ArrayList<Ljava/lang/String;>;
+T Ljava/util/ArrayList<Ljava/util/regex/Pattern;>;
+T Ljava/util/ArrayList<Lorg/apache/bcel/generic/ObjectType;>;
+T Ljava/util/ArrayList<Lorg/dom4j/Document;>;
+T Ljava/util/ArrayList<TEdgeType;>;
+T Ljava/util/ArrayList<TValueType;>;
+T Ljava/util/ArrayList<TVertexType;>;
+T Ljava/util/Collection<+Ledu/umd/cs/findbugs/xml/XMLWriteable;>;
+T Ljava/util/Collection<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/Collection<Ledu/umd/cs/findbugs/BugPattern;>;
+T Ljava/util/Collection<Ledu/umd/cs/findbugs/WarningSuppressor;>;
+T Ljava/util/Collection<Ljava/lang/String;>;
+T Ljava/util/Collection<TT;>;
+T Ljava/util/Comparator<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+T Ljava/util/Enumeration<Ljava/lang/String;>;
+T Ljava/util/HashMap<Ledu/umd/cs/findbugs/ba/Location;Ledu/umd/cs/findbugs/ba/ValueNumberFrame;>;
+T Ljava/util/HashMap<Ledu/umd/cs/findbugs/ba/ValueNumber;Ljava/lang/String;>;
+T Ljava/util/HashMap<Ledu/umd/cs/findbugs/ba/ValueNumberCache$Entry;[Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ljava/util/HashMap<Ljava/lang/Byte;Ljava/lang/String;>;
+T Ljava/util/HashMap<Ljava/lang/Integer;Ljava/lang/String;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/BugCode;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/BugPattern;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/DetectorFactory;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/PackageStats;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/Plugin;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/type/Type;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/Boolean;>;
+T Ljava/util/HashMap<Ljava/lang/String;Ljava/lang/String;>;
+T Ljava/util/HashMap<Lorg/apache/bcel/classfile/Method;Ledu/umd/cs/findbugs/ba/ClassContext$AnalysisResult<TAnalysis;>;>;
+T Ljava/util/HashMap<Lorg/apache/bcel/generic/ObjectType;Ljava/lang/Integer;>;
+T Ljava/util/HashMap<TVertexType;Ledu/umd/cs/findbugs/graph/SearchTree;>;
+T Ljava/util/HashSet<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/HashSet<Ledu/umd/cs/findbugs/ba/Edge;>;
+T Ljava/util/HashSet<Ledu/umd/cs/findbugs/ba/XField;>;
+T Ljava/util/HashSet<Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+T Ljava/util/HashSet<Ljava/lang/String;>;
+T Ljava/util/HashSet<Lorg/apache/bcel/classfile/JavaClass;>;
+T Ljava/util/HashSet<Lorg/apache/bcel/classfile/Method;>;
+T Ljava/util/IdentityHashMap<Ledu/umd/cs/findbugs/ba/BasicBlock;Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+T Ljava/util/IdentityHashMap<Ledu/umd/cs/findbugs/ba/BasicBlock;Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ljava/util/IdentityHashMap<Ledu/umd/cs/findbugs/ba/BasicBlock;Ljava/util/BitSet;>;
+T Ljava/util/IdentityHashMap<Ledu/umd/cs/findbugs/ba/BasicBlock;Ljava/util/List<Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$EscapeTarget;>;>;
+T Ljava/util/IdentityHashMap<Ledu/umd/cs/findbugs/ba/BasicBlock;TFact;>;
+T Ljava/util/IdentityHashMap<Lorg/apache/bcel/classfile/Method;Ledu/umd/cs/findbugs/CallGraphNode;>;
+T Ljava/util/IdentityHashMap<Lorg/apache/bcel/generic/InstructionHandle;Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+T Ljava/util/IdentityHashMap<Lorg/apache/bcel/generic/InstructionHandle;Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$Subroutine;>;
+T Ljava/util/IdentityHashMap<Lorg/apache/bcel/generic/InstructionHandle;Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ljava/util/IdentityHashMap<Lorg/apache/bcel/generic/InstructionHandle;Ljava/util/List<Lorg/apache/bcel/generic/CodeExceptionGen;>;>;
+T Ljava/util/IdentityHashMap<Lorg/apache/bcel/generic/InstructionHandle;Lorg/apache/bcel/classfile/LineNumber;>;
+T Ljava/util/IdentityHashMap<Lorg/apache/bcel/generic/InstructionHandle;Lorg/apache/bcel/generic/CodeExceptionGen;>;
+T Ljava/util/IdentityHashMap<TVertexType;TVertexType;>;
+T Ljava/util/Iterator<+Ledu/umd/cs/findbugs/xml/XMLWriteable;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugAnnotation;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugCode;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugPattern;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/BugReporterObserver;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/CallGraphEdge;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/CountBugs$Key;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/DetectorFactory;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/DetectorOrderingConstraint;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/InstructionScanner;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/Matcher;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/PackageStats;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/Plugin;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/URLClassPath$Entry;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/AssertionMethods$UserAssertionMethod;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/AvailableLoad;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$EscapeTarget;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/ClassObserver;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Edge;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Location;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/PruneInfeasibleExceptionEdges$MarkedEdge;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/SourceFinder$SourceRepository;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/Target;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/ValueNumberFrame;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/bcp/ByteCodePatternMatch;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/bcp/PatternElementMatch;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/bcp/PatternMatcher$State;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/type/InheritanceGraphEdge;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ml/ConvertToARFF$Attribute;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/ml/ConvertToARFF$DataFile;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/plan/AnalysisPass;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/plan/ConstraintEdge;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/plan/DetectorNode;>;
+T Ljava/util/Iterator<Ledu/umd/cs/findbugs/xml/XMLAttributeList$NameValuePair;>;
+T Ljava/util/Iterator<Ljava/lang/Integer;>;
+T Ljava/util/Iterator<Ljava/lang/String;>;
+T Ljava/util/Iterator<Ljava/util/Map$Entry<Ljava/lang/String;Ledu/umd/cs/findbugs/CategorizeBugs$Stats;>;>;
+T Ljava/util/Iterator<Ljava/util/regex/Pattern;>;
+T Ljava/util/Iterator<Lorg/apache/bcel/generic/CodeExceptionGen;>;
+T Ljava/util/Iterator<Lorg/apache/bcel/generic/InstructionHandle;>;
+T Ljava/util/Iterator<Lorg/dom4j/Document;>;
+T Ljava/util/Iterator<TEdgeType;>;
+T Ljava/util/Iterator<TResource;>;
+T Ljava/util/Iterator<TT;>;
+T Ljava/util/Iterator<TVertexType;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/DetectorFactory;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/FindBugs$ArchiveWorkListItem;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/InstructionScanner;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/Project$WorkListItem;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/BasicBlock;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$EscapeTarget;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$Subroutine;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$WorkListItem;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/Edge;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/Target;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/bcp/ByteCodePatternMatch;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/bcp/PatternElementMatch;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/bcp/PatternMatcher$State;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/ba/type/ObjectType;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/graph/SearchTree<TVertexType;>;>;
+T Ljava/util/LinkedList<Ledu/umd/cs/findbugs/plan/AnalysisPass;>;
+T Ljava/util/LinkedList<Ljava/io/File;>;
+T Ljava/util/LinkedList<Ljava/lang/String;>;
+T Ljava/util/LinkedList<Ljava/util/regex/Pattern;>;
+T Ljava/util/LinkedList<Lorg/dom4j/Branch;>;
+T Ljava/util/LinkedList<TVertexType;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/BugPattern;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/BugReporterObserver;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/DetectorFactory;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/DetectorOrderingConstraint;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/Matcher;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/OpcodeStack$Item;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/Plugin;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/URLClassPath$Entry;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/ba/AssertionMethods$UserAssertionMethod;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/ba/BetterCFGBuilder2$EscapeTarget;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/ba/ClassObserver;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/ba/PruneInfeasibleExceptionEdges$MarkedEdge;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/ba/SourceFinder$SourceRepository;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/ml/ConvertToARFF$Attribute;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/ml/ConvertToARFF$DataFile;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/plan/DetectorNode;>;
+T Ljava/util/List<Ledu/umd/cs/findbugs/xml/XMLAttributeList$NameValuePair;>;
+T Ljava/util/List<Ljava/lang/String;>;
+T Ljava/util/List<Ljava/util/List<Ledu/umd/cs/findbugs/ba/Edge;>;>;
+T Ljava/util/List<Lorg/apache/bcel/generic/CodeExceptionGen;>;
+T Ljava/util/List<Lorg/dom4j/Document;>;
+T Ljava/util/List<TResource;>;
+T Ljava/util/Map$Entry<Ljava/lang/String;Ledu/umd/cs/findbugs/CategorizeBugs$Stats;>;
+T Ljava/util/Map$Entry<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/SourceFile;>;
+T Ljava/util/Map$Entry<Ljava/lang/String;Ljava/lang/Object;>;
+T Ljava/util/Map$Entry<Lorg/apache/bcel/classfile/JavaClass;Ledu/umd/cs/findbugs/ba/ClassContext;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ClassAnnotation;Ljava/util/Collection<Ledu/umd/cs/findbugs/WarningSuppressor;>;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/AvailableLoad;[Ledu/umd/cs/findbugs/ba/ValueNumber;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/BasicBlock;Ledu/umd/cs/findbugs/ba/BlockType;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/BasicBlock;Ledu/umd/cs/findbugs/ba/TypeAnalysis$CachedExceptionSet;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/BasicBlock;Ljava/util/BitSet;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/BasicBlock;TFact;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/Edge;Ledu/umd/cs/findbugs/ba/ExceptionSet;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/Location;TResource;>;
+T Ljava/util/Map<Ledu/umd/cs/findbugs/ba/XField;Ledu/umd/cs/findbugs/ba/LoadedFieldSet$LoadStoreCount;>;
+T Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/CategorizeBugs$Stats;>;
+T Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/DetectorFactory;>;
+T Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/InnerClassAccess;>;
+T Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/plan/DetectorNode;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/Boolean;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/Integer;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/Object;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/util/Collection<Ledu/umd/cs/findbugs/WarningSuppressor;>;>;
+T Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<Ljava/lang/String;Ledu/umd/cs/findbugs/ba/InnerClassAccess;>;>;
+T Ljava/util/Map<Ljava/lang/String;Lorg/apache/bcel/classfile/JavaClass;>;
+T Ljava/util/Map<Lorg/apache/bcel/classfile/Method;Ljava/util/Set<Ledu/umd/cs/findbugs/ba/XField;>;>;
+T Ljava/util/Map<Lorg/apache/bcel/generic/InstructionHandle;Ledu/umd/cs/findbugs/ba/XField;>;
+T Ljava/util/Set<Ledu/umd/cs/findbugs/CountBugs$Key;>;
+T Ljava/util/Set<Ledu/umd/cs/findbugs/ba/XField;>;
+T Ljava/util/Set<Ljava/lang/String;>;
+T Ljava/util/Set<TVertexType;>;
+T Ljava/util/SortedSet<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/TreeMap<Ledu/umd/cs/findbugs/CountBugs$Key;Ljava/lang/Integer;>;
+T Ljava/util/TreeMap<Ljava/lang/String;Ledu/umd/cs/findbugs/CategorizeBugs$Stats;>;
+T Ljava/util/TreeSet<Ledu/umd/cs/findbugs/BugInstance;>;
+T Ljava/util/TreeSet<Ledu/umd/cs/findbugs/CountBugs$Key;>;
+T Ljava/util/TreeSet<Ljava/lang/String;>;
+T Ljava/util/TreeSet<TEdgeType;>;
+T Ljava/util/TreeSet<TVertexType;>;
+T TActualEdgeType;
+T TActualVertexType;
+T TAnalysis;
+T TAnalysisType;
+T TEdgeType;
+T TFact;
+T TFrameType;
+T TGraphType;
+T TResource;
+T TResourceTrackerType;
+T TT;
+T TValue;
+T TValueType;
+T TVertexType;
+T [TValueType;
diff --git a/asmx/test/conform/org/objectweb/asm/tree/ClassNodeTest.class b/asmx/test/conform/org/objectweb/asm/tree/ClassNodeTest.class
new file mode 100644
index 0000000..09883fa
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/ClassNodeTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/tree/ClassNodeTest.java b/asmx/test/conform/org/objectweb/asm/tree/ClassNodeTest.java
new file mode 100644
index 0000000..039fe9f
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/ClassNodeTest.java
@@ -0,0 +1,57 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.tree;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+import junit.framework.TestSuite;
+
+/**
+ * ClassWriter tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class ClassNodeTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new ClassNodeTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassNode cn = new ClassNode();
+        cr.accept(cn, false);
+        ClassWriter cw = new ClassWriter(false, true);
+        cn.accept(cw);
+        assertEquals(cr, new ClassReader(cw.toByteArray()));
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/tree/UnitTest.class b/asmx/test/conform/org/objectweb/asm/tree/UnitTest.class
new file mode 100644
index 0000000..075507e
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/UnitTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/tree/UnitTest.java b/asmx/test/conform/org/objectweb/asm/tree/UnitTest.java
new file mode 100644
index 0000000..0f06fc9
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/UnitTest.java
@@ -0,0 +1,67 @@
+package org.objectweb.asm.tree;
+
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+
+import junit.framework.TestCase;
+
+public class UnitTest extends TestCase implements Opcodes {
+
+    public void testNodes() {
+        InsnNode in = InsnNode.getByOpcode(NOP);
+        assertEquals(in.getOpcode(), NOP);
+        assertEquals(in.getType(), AbstractInsnNode.INSN);
+
+        IntInsnNode iin = new IntInsnNode(BIPUSH, 0);
+        iin.setOpcode(SIPUSH);
+        assertEquals(iin.getOpcode(), SIPUSH);
+        assertEquals(iin.getType(), AbstractInsnNode.INT_INSN);
+
+        VarInsnNode vn = new VarInsnNode(ALOAD, 0);
+        vn.setOpcode(ASTORE);
+        assertEquals(vn.getOpcode(), ASTORE);
+        assertEquals(vn.getType(), AbstractInsnNode.VAR_INSN);
+
+        TypeInsnNode tin = new TypeInsnNode(NEW, "java/lang/Object");
+        tin.setOpcode(CHECKCAST);
+        assertEquals(tin.getOpcode(), CHECKCAST);
+        assertEquals(tin.getType(), AbstractInsnNode.TYPE_INSN);
+
+        FieldInsnNode fn = new FieldInsnNode(GETSTATIC, "owner", "name", "I");
+        fn.setOpcode(PUTSTATIC);
+        assertEquals(fn.getOpcode(), PUTSTATIC);
+        assertEquals(fn.getType(), AbstractInsnNode.FIELD_INSN);
+
+        MethodInsnNode mn = new MethodInsnNode(INVOKESTATIC,
+                "owner",
+                "name",
+                "I");
+        mn.setOpcode(INVOKESPECIAL);
+        assertEquals(mn.getOpcode(), INVOKESPECIAL);
+        assertEquals(mn.getType(), AbstractInsnNode.METHOD_INSN);
+
+        JumpInsnNode jn = new JumpInsnNode(GOTO, new Label());
+        jn.setOpcode(IFEQ);
+        assertEquals(jn.getOpcode(), IFEQ);
+        assertEquals(jn.getType(), AbstractInsnNode.JUMP_INSN);
+
+        LabelNode ln = new LabelNode(new Label());
+        assertEquals(ln.getType(), AbstractInsnNode.LABEL);
+
+        IincInsnNode iincn = new IincInsnNode(1, 1);
+        assertEquals(iincn.getType(), AbstractInsnNode.IINC_INSN);
+
+        LdcInsnNode ldcn = new LdcInsnNode("s");
+        assertEquals(ldcn.getType(), AbstractInsnNode.LDC_INSN);
+
+        LookupSwitchInsnNode lsn = new LookupSwitchInsnNode(null, null, null);
+        assertEquals(lsn.getType(), AbstractInsnNode.LOOKUPSWITCH_INSN);
+
+        TableSwitchInsnNode tsn = new TableSwitchInsnNode(0, 1, null, null);
+        assertEquals(tsn.getType(), AbstractInsnNode.TABLESWITCH_INSN);
+
+        MultiANewArrayInsnNode manan = new MultiANewArrayInsnNode("[[I", 2);
+        assertEquals(manan.getType(), AbstractInsnNode.MULTIANEWARRAY_INSN);
+    }
+
+}
diff --git a/asmx/test/conform/org/objectweb/asm/tree/analysis/BasicVerifierTest.class b/asmx/test/conform/org/objectweb/asm/tree/analysis/BasicVerifierTest.class
new file mode 100644
index 0000000..0743bfb
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/analysis/BasicVerifierTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/tree/analysis/BasicVerifierTest.java b/asmx/test/conform/org/objectweb/asm/tree/analysis/BasicVerifierTest.java
new file mode 100644
index 0000000..6cdaab0
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/analysis/BasicVerifierTest.java
@@ -0,0 +1,65 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.List;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Analysis tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class BasicVerifierTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new BasicVerifierTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassNode cn = new ClassNode();
+        cr.accept(cn, false);
+        List methods = cn.methods;
+        for (int i = 0; i < methods.size(); ++i) {
+            MethodNode method = (MethodNode) methods.get(i);
+            if (method.instructions.size() > 0) {
+                Analyzer a = new Analyzer(new BasicVerifier());
+                a.analyze(cn.name, method);
+            }
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/tree/analysis/DataflowTest.class b/asmx/test/conform/org/objectweb/asm/tree/analysis/DataflowTest.class
new file mode 100644
index 0000000..f9a616e
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/analysis/DataflowTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/tree/analysis/DataflowTest.java b/asmx/test/conform/org/objectweb/asm/tree/analysis/DataflowTest.java
new file mode 100644
index 0000000..ee878f9
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/analysis/DataflowTest.java
@@ -0,0 +1,65 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.List;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * DataflowInterpreter tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class DataflowTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new DataflowTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassNode cn = new ClassNode();
+        cr.accept(cn, false);
+        List methods = cn.methods;
+        for (int i = 0; i < methods.size(); ++i) {
+            MethodNode method = (MethodNode) methods.get(i);
+            if (method.instructions.size() > 0) {
+                Analyzer a = new Analyzer(new DataflowInterpreter());
+                a.analyze(cn.name, method);
+            }
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/tree/analysis/SimpleVerifierTest.class b/asmx/test/conform/org/objectweb/asm/tree/analysis/SimpleVerifierTest.class
new file mode 100644
index 0000000..e326778
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/analysis/SimpleVerifierTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/tree/analysis/SimpleVerifierTest.java b/asmx/test/conform/org/objectweb/asm/tree/analysis/SimpleVerifierTest.java
new file mode 100644
index 0000000..1e2e867
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/tree/analysis/SimpleVerifierTest.java
@@ -0,0 +1,65 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.tree.analysis;
+
+import java.util.List;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Verifier tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class SimpleVerifierTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new SimpleVerifierTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassNode cn = new ClassNode();
+        cr.accept(cn, false);
+        List methods = cn.methods;
+        for (int i = 0; i < methods.size(); ++i) {
+            MethodNode method = (MethodNode) methods.get(i);
+            if (method.instructions.size() > 0) {
+                Analyzer a = new Analyzer(new SimpleVerifier());
+                a.analyze(cn.name, method);
+            }
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/util/ASMifierTest$Compiler.class b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest$Compiler.class
new file mode 100644
index 0000000..9b94f29
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest$Compiler.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/ASMifierTest$TestClassLoader.class b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest$TestClassLoader.class
new file mode 100644
index 0000000..eaf1460
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest$TestClassLoader.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/ASMifierTest.class b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest.class
new file mode 100644
index 0000000..37da287
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/ASMifierTest.java b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest.java
new file mode 100644
index 0000000..a7babaa
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/ASMifierTest.java
@@ -0,0 +1,114 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.util;
+
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import junit.framework.TestSuite;
+
+import org.codehaus.janino.ClassLoaderIClassLoader;
+import org.codehaus.janino.DebuggingInformation;
+import org.codehaus.janino.IClassLoader;
+import org.codehaus.janino.Parser;
+import org.codehaus.janino.Scanner;
+import org.codehaus.janino.UnitCompiler;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.util.ASMifierClassVisitor;
+
+/**
+ * ASMifier tests.
+ * 
+ * @author Eugene Kuleshov
+ * @author Eric Bruneton
+ */
+public class ASMifierTest extends AbstractTest {
+
+    public static final Compiler COMPILER = new Compiler();
+
+    public static final TestClassLoader LOADER = new TestClassLoader();
+
+    public static TestSuite suite() throws Exception {
+        return new ASMifierTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+
+        if (cr.b.length > 20000) {
+            return;
+        }
+
+        StringWriter sw = new StringWriter();
+        ASMifierClassVisitor cv = new ASMifierClassVisitor(new PrintWriter(sw));
+        cr.accept(cv, false);
+
+        String generated = sw.toString();
+
+        byte[] generatorClassData;
+        try {
+            generatorClassData = COMPILER.compile(n, generated);
+        } catch (Exception ex) {
+            System.err.println(generated);
+            System.err.println("------------------");
+            throw ex;
+        }
+
+        Class c = LOADER.defineClass("asm." + n + "Dump", generatorClassData);
+        Method m = c.getMethod("dump", new Class[0]);
+        byte[] b = (byte[]) m.invoke(null, new Object[0]);
+
+        assertEquals(cr, new ClassReader(b));
+    }
+
+    public static class TestClassLoader extends ClassLoader {
+
+        public Class defineClass(final String name, final byte[] b) {
+            return defineClass(name, b, 0, b.length);
+        }
+    }
+
+    public static class Compiler {
+
+        final static IClassLoader CL = new ClassLoaderIClassLoader(new URLClassLoader(new URL[0]));
+
+        public byte[] compile(String name, String source) throws Exception {
+            Parser p = new Parser(new Scanner(name, new StringReader(source)));
+            UnitCompiler uc = new UnitCompiler(p.parseCompilationUnit(), CL);
+            return uc.compileUnit(DebuggingInformation.ALL)[0].toByteArray();
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/util/CheckClassAdapterTest.class b/asmx/test/conform/org/objectweb/asm/util/CheckClassAdapterTest.class
new file mode 100644
index 0000000..79964ce
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/CheckClassAdapterTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/CheckClassAdapterTest.java b/asmx/test/conform/org/objectweb/asm/util/CheckClassAdapterTest.java
new file mode 100644
index 0000000..b74d9b9
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/CheckClassAdapterTest.java
@@ -0,0 +1,55 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.util;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+
+/**
+ * CheckClassAdapter tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class CheckClassAdapterTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new CheckClassAdapterTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(false);
+        cr.accept(new CheckClassAdapter(cw), false);
+        assertEquals(cr, new ClassReader(cw.toByteArray()));
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/util/TraceClassAdapterTest.class b/asmx/test/conform/org/objectweb/asm/util/TraceClassAdapterTest.class
new file mode 100644
index 0000000..ba9efd2
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/TraceClassAdapterTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/TraceClassAdapterTest.java b/asmx/test/conform/org/objectweb/asm/util/TraceClassAdapterTest.java
new file mode 100644
index 0000000..0e36a80
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/TraceClassAdapterTest.java
@@ -0,0 +1,61 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.util;
+
+import java.io.CharArrayWriter;
+import java.io.PrintWriter;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+/**
+ * CheckClassAdapter tests.
+ * 
+ * @author Eric Bruneton
+ */
+public class TraceClassAdapterTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new TraceClassAdapterTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(false, true);
+        ClassVisitor cv = new TraceClassVisitor(cw,
+                new PrintWriter(new CharArrayWriter()));
+        cr.accept(cv, false);
+        assertEquals(cr, new ClassReader(cw.toByteArray()));
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest$TestData.class b/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest$TestData.class
new file mode 100644
index 0000000..e41091b
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest$TestData.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest.class b/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest.class
new file mode 100644
index 0000000..b462d1b
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest.java b/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest.java
new file mode 100644
index 0000000..4c0615b
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/TraceSignatureVisitorTest.java
@@ -0,0 +1,168 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.util;
+
+import java.util.StringTokenizer;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.signature.SignatureReader;
+
+/**
+ * ClassSignatureDecompilerTest
+ * 
+ * @author Eugene Kuleshov
+ */
+public class TraceSignatureVisitorTest extends TestCase {
+
+    private static String[] DATA = {
+        "C|E|<E extends java.lang.Enum<E>> implements java.lang.Comparable<E>, java.io.Serializable"
+                + "|<E:Ljava/lang/Enum<TE;>;>Ljava/lang/Object;Ljava/lang/Comparable<TE;>;Ljava/io/Serializable;",
+
+        "C|I|<D extends java.lang.reflect.GenericDeclaration> extends java.lang.reflect.Type"
+                + "|<D::Ljava/lang/reflect/GenericDeclaration;>Ljava/lang/Object;Ljava/lang/reflect/Type;",
+
+        "C|C|<K, V> extends java.util.AbstractMap<K, V> implements java.util.concurrent.ConcurrentMap<K, V>, java.io.Serializable"
+                + "|<K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/util/concurrent/ConcurrentMap<TK;TV;>;Ljava/io/Serializable;",
+
+        "C|C|<K extends java.lang.Enum<K>, V> extends java.util.AbstractMap<K, V> implements java.io.Serializable, java.lang.Cloneable"
+                + "|<K:Ljava/lang/Enum<TK;>;V:Ljava/lang/Object;>Ljava/util/AbstractMap<TK;TV;>;Ljava/io/Serializable;Ljava/lang/Cloneable;",
+
+        "F|C|java.lang.Class<?>|Ljava/lang/Class<*>;",
+        "F|C|java.lang.reflect.Constructor<T>|Ljava/lang/reflect/Constructor<TT;>;",
+        "F|C|T[]|[TT;",
+        "F|C|java.util.Hashtable<?, ?>|Ljava/util/Hashtable<**>;",
+        "F|C|java.util.concurrent.atomic.AtomicReferenceFieldUpdater<java.io.BufferedInputStream, byte[]>"
+                + "|Ljava/util/concurrent/atomic/AtomicReferenceFieldUpdater<Ljava/io/BufferedInputStream;[B>;",
+
+        "F|C|AA<byte[][]>|LAA<[[B>;",
+        "F|C|AA<java.util.Map<java.lang.String, java.lang.String>[][]>"
+                + "|LAA<[[Ljava/util/Map<Ljava/lang/String;Ljava/lang/String;>;>;",
+
+        "F|C|java.util.Hashtable<java.lang.Object, java.lang.String>"
+                + "|Ljava/util/Hashtable<Ljava/lang/Object;Ljava/lang/String;>;",
+
+        "M|C|void()E|()V^TE;",
+
+        "M|C|void(java.lang.String, java.lang.Class<?>, java.lang.reflect.Method[], java.lang.reflect.Method, java.lang.reflect.Method)"
+                + "|(Ljava/lang/String;Ljava/lang/Class<*>;[Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;Ljava/lang/reflect/Method;)V",
+
+        "M|C|java.util.Map<java.lang.Object, java.lang.String>(java.lang.Object, java.util.Map<java.lang.Object, java.lang.String>)"
+                + "|(Ljava/lang/Object;Ljava/util/Map<Ljava/lang/Object;Ljava/lang/String;>;)Ljava/util/Map<Ljava/lang/Object;Ljava/lang/String;>;",
+
+        "M|C|java.util.Map<java.lang.Object, java.lang.String><T>(java.lang.Object, java.util.Map<java.lang.Object, java.lang.String>, T)"
+                + "|<T:Ljava/lang/Object;>(Ljava/lang/Object;Ljava/util/Map<Ljava/lang/Object;Ljava/lang/String;>;TT;)Ljava/util/Map<Ljava/lang/Object;Ljava/lang/String;>;",
+
+        "M|C|java.util.Map<java.lang.Object, java.lang.String><E, T extends java.lang.Comparable<E>>(java.lang.Object, java.util.Map<java.lang.Object, java.lang.String>, T)"
+                + "|<E:Ljava/lang/Object;T::Ljava/lang/Comparable<TE;>;>(Ljava/lang/Object;Ljava/util/Map<Ljava/lang/Object;Ljava/lang/String;>;TT;)Ljava/util/Map<Ljava/lang/Object;Ljava/lang/String;>;",
+
+    };
+
+    public static TestSuite suite() {
+        TestSuite suite = new TestSuite(TraceSignatureVisitorTest.class.getName());
+
+        for (int i = 0; i < DATA.length; i++) {
+            suite.addTest(new TraceSignatureVisitorTest(new TestData(DATA[i])));
+        }
+
+        return suite;
+    }
+
+    private TestData data;
+
+    private TraceSignatureVisitorTest(TestData data) {
+        super("testSignature");
+        this.data = data;
+    }
+
+    public void testSignature() {
+        TraceSignatureVisitor d = new TraceSignatureVisitor(data.access);
+        SignatureReader r = new SignatureReader(data.signature);
+
+        switch (data.type) {
+            case 'C':
+                r.accept(d);
+                assertEquals(data.declaration, d.getDeclaration());
+                break;
+            case 'F':
+                r.acceptType(d);
+                assertEquals(data.declaration, d.getDeclaration());
+                break;
+            case 'M':
+                r.accept(d);
+                String fullMethodDeclaration = d.getReturnType()
+                        + d.getDeclaration()
+                        + (d.getExceptions() != null ? d.getExceptions() : "");
+                assertEquals(data.declaration, fullMethodDeclaration);
+                break;
+        }
+
+    }
+
+    public String getName() {
+        return super.getName() + " " + data.signature;
+    }
+
+    private static class TestData {
+
+        public final char type;
+
+        public final int access;
+
+        public final String declaration;
+
+        public final String signature;
+
+        private TestData(String data) {
+            StringTokenizer st = new StringTokenizer(data, "|");
+            this.type = st.nextToken().charAt(0);
+
+            String acc = st.nextToken();
+            switch (acc.charAt(0)) {
+                case 'E':
+                    this.access = Opcodes.ACC_ENUM;
+                    break;
+                case 'I':
+                    this.access = Opcodes.ACC_INTERFACE;
+                    break;
+                case 'A':
+                    this.access = Opcodes.ACC_ANNOTATION;
+                    break;
+                default:
+                    this.access = 0;
+            }
+
+            this.declaration = st.nextToken();
+            this.signature = st.nextToken();
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/util/UnitTest.class b/asmx/test/conform/org/objectweb/asm/util/UnitTest.class
new file mode 100644
index 0000000..3785f66
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/UnitTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/UnitTest.java b/asmx/test/conform/org/objectweb/asm/util/UnitTest.java
new file mode 100644
index 0000000..2a39e8e
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/UnitTest.java
@@ -0,0 +1,404 @@
+package org.objectweb.asm.util;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.attrs.StackMapTableAttribute;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+import junit.framework.TestCase;
+
+public class UnitTest extends TestCase implements Opcodes {
+
+    public void testTraceClassVisitor() throws Exception {
+        String s = getClass().getName();
+        TraceClassVisitor.main(new String[0]);
+        TraceClassVisitor.main(new String[] { "-debug" });
+        TraceClassVisitor.main(new String[] { s });
+        TraceClassVisitor.main(new String[] { "-debug", s });
+        try {
+            TraceClassVisitor.main(new String[] { s + ".class" });
+        } catch (Exception e) {
+        }
+    }
+
+    public void testASMifierClassVisitor() throws Exception {
+        String s = getClass().getName();
+        ASMifierClassVisitor.main(new String[0]);
+        ASMifierClassVisitor.main(new String[] { "-debug" });
+        ASMifierClassVisitor.main(new String[] { s });
+        ASMifierClassVisitor.main(new String[] { "-debug", s });
+        try {
+            ASMifierClassVisitor.main(new String[] { s + ".class" });
+        } catch (Exception e) {
+        }
+    }
+
+    public void testCheckClassVisitor() throws Exception {
+        String s = getClass().getName();
+        CheckClassAdapter.main(new String[0]);
+        CheckClassAdapter.main(new String[] { s });
+        try {
+            CheckClassAdapter.main(new String[] { s + ".class" });
+        } catch (Exception e) {
+        }
+
+        ClassVisitor cv = new CheckClassAdapter(new EmptyVisitor());        
+        try {
+            cv.visit(V1_1, 1 << 20, "C", null, "java/lang/Object", null);
+            fail();
+        } catch (Exception e) {
+        }
+        
+        cv = new CheckClassAdapter(new EmptyVisitor());
+        try {
+            cv.visit(V1_1, ACC_PUBLIC, "java/lang/Object", null, "java/lang/Object", null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        cv = new CheckClassAdapter(new EmptyVisitor());
+        try {
+            cv.visit(V1_1, ACC_INTERFACE, "I", null, "C", null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        cv = new CheckClassAdapter(new EmptyVisitor());
+        try {
+            cv.visit(V1_1, ACC_FINAL+ACC_ABSTRACT, "C", null, "java/lang/Object", null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        cv = new CheckClassAdapter(new EmptyVisitor());
+        try {
+            cv.visitSource(null, null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        cv.visit(V1_1, ACC_PUBLIC, "C", null, "java/lang/Object", null);
+        try {
+            cv.visit(V1_1, ACC_PUBLIC, "C", null, "java/lang/Object", null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        cv.visitSource(null, null);
+        try {
+            cv.visitSource(null, null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            cv.visitOuterClass(null, null, null);
+            fail();
+        } catch (Exception e) {
+        }
+        try {
+            cv.visitOuterClass(null, null, null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            cv.visitField(ACC_PUBLIC+ACC_PRIVATE, "i", "I", null, null);
+            fail();
+        } catch (Exception e) {
+        }
+        
+        cv.visitEnd();
+        try {
+            cv.visitSource(null, null);
+            fail();
+        } catch (Exception e) {
+        }
+        
+        FieldVisitor fv = new CheckFieldAdapter(new EmptyVisitor());
+        fv.visitEnd();
+        try {
+            fv.visitAttribute(new StackMapTableAttribute());
+            fail();
+        } catch (Exception e) {
+        }
+
+        fv = new CheckFieldAdapter(new EmptyVisitor());
+        try {
+            fv.visitAttribute(null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        MethodVisitor mv = new CheckMethodAdapter(new EmptyVisitor());
+        try {
+            mv.visitParameterAnnotation(0, "'", true);
+            fail();
+        } catch (Exception e) {
+        }
+
+        mv.visitEnd();
+        try {
+            mv.visitAttribute(new StackMapTableAttribute());
+            fail();
+        } catch (Exception e) {
+        }
+
+        mv = new CheckMethodAdapter(new EmptyVisitor());
+        try {
+            mv.visitAttribute(null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitInsn(NOP);
+            fail();
+        } catch (Exception e) {
+        }
+
+        mv.visitCode();
+        try {
+            mv.visitInsn(-1);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitIntInsn(BIPUSH, Integer.MAX_VALUE);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitIntInsn(SIPUSH, Integer.MAX_VALUE);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitVarInsn(ALOAD, -1);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitIntInsn(NEWARRAY, 0);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitTypeInsn(NEW, "[I");
+            fail();
+        } catch (Exception e) {
+        }
+
+        Label l = new Label();
+        mv.visitLabel(l);
+        try {
+            mv.visitLabel(l);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitTableSwitchInsn(1, 0, new Label(), new Label[0]);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitTableSwitchInsn(0, 1, null, new Label[0]);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitTableSwitchInsn(0, 1, new Label(), null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitTableSwitchInsn(0, 1, new Label(), new Label[0]);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitLookupSwitchInsn(new Label(), null, new Label[0]);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitLookupSwitchInsn(new Label(), new int[0], null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitLookupSwitchInsn(new Label(), new int[0], new Label[1]);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, null, "i", "I");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "-", "i", "I");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", null, "I");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "-", "I");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "a-", "I");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "i", null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "i", "V");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "i", "II");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "i", "[");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "i", "L");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitFieldInsn(GETFIELD, "C", "i", "L-;");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "C", null, "()V");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "C", "-", "()V");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "C", "a-", "()V");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "C", "m", null);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "C", "m", "I");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "C", "m", "(V)V");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "C", "m", "()VV");
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitLdcInsn(new Object());
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMultiANewArrayInsn("I", 0);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMultiANewArrayInsn("[[I", 0);
+            fail();
+        } catch (Exception e) {
+        }
+
+        try {
+            mv.visitMultiANewArrayInsn("[[I", 3);
+            fail();
+        } catch (Exception e) {
+        }
+        
+        Label m = new Label();
+        mv.visitInsn(NOP);
+        mv.visitLabel(m);
+        try {
+            mv.visitLocalVariable("i", "I", null, m, l, 0);
+            fail();
+        } catch (Exception e) {
+        }
+        
+        try {
+            mv.visitLineNumber(0, new Label());
+            fail();
+        } catch (Exception e) {
+        }
+
+        mv.visitMaxs(0,0);
+        try {
+            mv.visitInsn(NOP);
+            fail();
+        } catch (Exception e) {
+        }
+    }
+}
diff --git a/asmx/test/conform/org/objectweb/asm/util/attrs/ASMStackMapTableAttributeTest.class b/asmx/test/conform/org/objectweb/asm/util/attrs/ASMStackMapTableAttributeTest.class
new file mode 100644
index 0000000..d1e74a3
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/attrs/ASMStackMapTableAttributeTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/util/attrs/ASMStackMapTableAttributeTest.java b/asmx/test/conform/org/objectweb/asm/util/attrs/ASMStackMapTableAttributeTest.java
new file mode 100644
index 0000000..9626670
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/util/attrs/ASMStackMapTableAttributeTest.java
@@ -0,0 +1,84 @@
+/***
+ * ASM tests
+ * Copyright (c) 2002-2005 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 org.objectweb.asm.util.attrs;
+
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.attrs.StackMapTableAttributeTest;
+import org.objectweb.asm.util.ASMifierClassVisitor;
+import org.objectweb.asm.util.ASMifierTest;
+import org.objectweb.asm.util.AbstractVisitor;
+
+/**
+ * StackMapTableAttributeTest
+ * 
+ * @author Eugene Kuleshov
+ */
+public class ASMStackMapTableAttributeTest extends AbstractTest {
+
+    public ASMStackMapTableAttributeTest() {
+        super();
+    }
+
+    public void test() throws Exception {
+        String n = "org.objectweb.asm.attrs.StackMapTableSample";
+        InputStream is = StackMapTableAttributeTest.class.getResourceAsStream(StackMapTableAttributeTest.TEST_CLASS);
+        ClassReader cr = new ClassReader(is);
+
+        StringWriter sw = new StringWriter();
+        ASMifierClassVisitor cv = new ASMifierClassVisitor(new PrintWriter(sw));
+        cr.accept(cv, AbstractVisitor.getDefaultAttributes(), false);
+
+        String generated = sw.toString();
+
+        byte[] generatorClassData;
+        try {
+            generatorClassData = ASMifierTest.COMPILER.compile(n, generated);
+            System.err.println(generated);
+        } catch (Exception ex) {
+            System.err.println(generated);
+            System.err.println("------------------");
+            throw ex;
+        }
+
+        Class c = ASMifierTest.LOADER.defineClass("asm." + n + "Dump",
+                generatorClassData);
+        Method m = c.getMethod("dump", new Class[0]);
+        byte[] b = (byte[]) m.invoke(null, new Object[0]);
+
+        assertEquals(cr, new ClassReader(b));
+    }
+
+}
diff --git a/asmx/test/conform/org/objectweb/asm/xml/SAXAdapterTest.class b/asmx/test/conform/org/objectweb/asm/xml/SAXAdapterTest.class
new file mode 100644
index 0000000..504fe12
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/xml/SAXAdapterTest.class
Binary files differ
diff --git a/asmx/test/conform/org/objectweb/asm/xml/SAXAdapterTest.java b/asmx/test/conform/org/objectweb/asm/xml/SAXAdapterTest.java
new file mode 100644
index 0000000..97a6eb4
--- /dev/null
+++ b/asmx/test/conform/org/objectweb/asm/xml/SAXAdapterTest.java
@@ -0,0 +1,68 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import java.io.ByteArrayOutputStream;
+
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.AbstractTest;
+import org.objectweb.asm.ClassReader;
+
+/**
+ * SAXAdapter tests
+ * 
+ * @author Eugene Kuleshov
+ */
+public class SAXAdapterTest extends AbstractTest {
+
+    public static TestSuite suite() throws Exception {
+        return new SAXAdapterTest().getSuite();
+    }
+
+    public void test() throws Exception {
+        ClassReader cr = new ClassReader(is);
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        SAXTransformerFactory saxtf = (SAXTransformerFactory) TransformerFactory.newInstance();
+        TransformerHandler handler = saxtf.newTransformerHandler();
+        handler.setResult(new SAXResult(new ASMContentHandler(bos, false)));
+        handler.startDocument();
+        cr.accept(new SAXClassAdapter(handler, false), false);
+        handler.endDocument();
+
+        assertEquals(cr, new ClassReader(bos.toByteArray()));
+    }
+}
diff --git a/asmx/test/conform/saxadapter.xml b/asmx/test/conform/saxadapter.xml
new file mode 100644
index 0000000..d8c5395
--- /dev/null
+++ b/asmx/test/conform/saxadapter.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/SAXAdapterTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>
+  </target>
+
+</project>
diff --git a/asmx/test/conform/signature.xml b/asmx/test/conform/signature.xml
new file mode 100644
index 0000000..6492be7
--- /dev/null
+++ b/asmx/test/conform/signature.xml
@@ -0,0 +1,24 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/SignatureTest.java"/>
+          <include name="**/TraceSignatureVisitorTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/simpleverifier.xml b/asmx/test/conform/simpleverifier.xml
new file mode 100644
index 0000000..67a77ff
--- /dev/null
+++ b/asmx/test/conform/simpleverifier.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/SimpleVerifierTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/stackmaptable.xml b/asmx/test/conform/stackmaptable.xml
new file mode 100644
index 0000000..ee69909
--- /dev/null
+++ b/asmx/test/conform/stackmaptable.xml
@@ -0,0 +1,24 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/StackMapTableAttributeTest.java"/>
+          <include name="**/ASMStackMapTableAttributeTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/staticinitmerger.xml b/asmx/test/conform/staticinitmerger.xml
new file mode 100644
index 0000000..809a6d7
--- /dev/null
+++ b/asmx/test/conform/staticinitmerger.xml
@@ -0,0 +1,21 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/StaticInitMergerTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/svuidadder.xml b/asmx/test/conform/svuidadder.xml
new file mode 100644
index 0000000..f0ec25e
--- /dev/null
+++ b/asmx/test/conform/svuidadder.xml
@@ -0,0 +1,21 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/SerialVersionUIDAdderTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/traceclassadapter.xml b/asmx/test/conform/traceclassadapter.xml
new file mode 100644
index 0000000..7cc8e56
--- /dev/null
+++ b/asmx/test/conform/traceclassadapter.xml
@@ -0,0 +1,23 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/TraceClassAdapterTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <jvmarg value="-Dasm.test=${asm.test}"/>
+      <jvmarg value="-Dasm.test.class=${asm.test.class}"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/unit.xml b/asmx/test/conform/unit.xml
new file mode 100644
index 0000000..251d9e6
--- /dev/null
+++ b/asmx/test/conform/unit.xml
@@ -0,0 +1,21 @@
+<project name="conform" default="test">
+
+  <target name="test">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/UnitTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/conform/xannotation.xml b/asmx/test/conform/xannotation.xml
new file mode 100644
index 0000000..0a05e0d
--- /dev/null
+++ b/asmx/test/conform/xannotation.xml
@@ -0,0 +1,21 @@
+<project name="conform" default="test">
+
+  <target name="test" if="java5">
+    <junit fork="yes" 
+           printsummary="yes"
+           errorproperty="test.failed"
+           failureproperty="test.failed">
+      <batchtest fork="yes" todir="${out.test}/reports">
+        <fileset dir="${test}/conform">
+          <include name="**/TypeAnnotationTest.java"/>
+        </fileset>
+      </batchtest>
+      <formatter type="xml"/>
+      <classpath refid="test.classpath"/>
+      <assertions>
+	<enable/>
+      </assertions>
+    </junit>  
+  </target>
+
+</project>
diff --git a/asmx/test/lib/asm-2.1.jar b/asmx/test/lib/asm-2.1.jar
new file mode 100644
index 0000000..4f56d24
--- /dev/null
+++ b/asmx/test/lib/asm-2.1.jar
Binary files differ
diff --git a/asmx/test/lib/bcel-5.1.jar b/asmx/test/lib/bcel-5.1.jar
new file mode 100644
index 0000000..3195ae5
--- /dev/null
+++ b/asmx/test/lib/bcel-5.1.jar
Binary files differ
diff --git a/asmx/test/lib/ccl.jar b/asmx/test/lib/ccl.jar
new file mode 100644
index 0000000..5100c72
--- /dev/null
+++ b/asmx/test/lib/ccl.jar
Binary files differ
diff --git a/asmx/test/lib/cobertura-1.7.jar b/asmx/test/lib/cobertura-1.7.jar
new file mode 100644
index 0000000..622b1b0
--- /dev/null
+++ b/asmx/test/lib/cobertura-1.7.jar
Binary files differ
diff --git a/asmx/test/lib/jakarta-oro-2.0.8.jar b/asmx/test/lib/jakarta-oro-2.0.8.jar
new file mode 100644
index 0000000..23488d2
--- /dev/null
+++ b/asmx/test/lib/jakarta-oro-2.0.8.jar
Binary files differ
diff --git a/asmx/test/lib/janino-2.3.4.jar b/asmx/test/lib/janino-2.3.4.jar
new file mode 100644
index 0000000..7503f2a
--- /dev/null
+++ b/asmx/test/lib/janino-2.3.4.jar
Binary files differ
diff --git a/asmx/test/lib/javancss.jar b/asmx/test/lib/javancss.jar
new file mode 100644
index 0000000..86b45f8
--- /dev/null
+++ b/asmx/test/lib/javancss.jar
Binary files differ
diff --git a/asmx/test/lib/javassist.jar b/asmx/test/lib/javassist.jar
new file mode 100644
index 0000000..d210d2c
--- /dev/null
+++ b/asmx/test/lib/javassist.jar
Binary files differ
diff --git a/asmx/test/lib/jd.xslt-1.5.5.jar b/asmx/test/lib/jd.xslt-1.5.5.jar
new file mode 100644
index 0000000..31b14e4
--- /dev/null
+++ b/asmx/test/lib/jd.xslt-1.5.5.jar
Binary files differ
diff --git a/asmx/test/lib/log4j-1.2.9.jar b/asmx/test/lib/log4j-1.2.9.jar
new file mode 100644
index 0000000..a6568b0
--- /dev/null
+++ b/asmx/test/lib/log4j-1.2.9.jar
Binary files differ
diff --git a/asmx/test/lib/saxon7.jar b/asmx/test/lib/saxon7.jar
new file mode 100644
index 0000000..77ed492
--- /dev/null
+++ b/asmx/test/lib/saxon7.jar
Binary files differ
diff --git a/asmx/test/lib/serp.jar b/asmx/test/lib/serp.jar
new file mode 100644
index 0000000..d3b81bd
--- /dev/null
+++ b/asmx/test/lib/serp.jar
Binary files differ
diff --git a/asmx/test/lib/xalan-2.6.0.jar b/asmx/test/lib/xalan-2.6.0.jar
new file mode 100644
index 0000000..73cf175
--- /dev/null
+++ b/asmx/test/lib/xalan-2.6.0.jar
Binary files differ
diff --git a/asmx/test/perf/all.xml b/asmx/test/perf/all.xml
new file mode 100644
index 0000000..637a773
--- /dev/null
+++ b/asmx/test/perf/all.xml
@@ -0,0 +1,21 @@
+<project name="perf" default="test">
+  <target name="test">
+    <java classname="org.objectweb.asm.ALLPerfTest" fork="yes">
+      <classpath>
+        <pathelement location="${out.build}"/>
+        <pathelement location="${out.test}"/>
+        <fileset dir="${test}">
+          <include name="lib/bcel.jar"/>
+          <include name="lib/serp.jar"/>
+          <include name="lib/javassist.jar"/>
+          <!-- xslt engines -->
+          <include name="lib/jd.xslt-1.5.5.jar"/>
+          <include name="lib/saxon7.jar"/>
+          <include name="lib/xalan-2.6.0.jar"/>
+        </fileset>
+      </classpath>
+      <arg value="${out.test}/"/>
+      <arg value="${java.home}/../lib/tools.jar"/>
+    </java>
+  </target>
+</project>
diff --git a/asmx/test/perf/mem.xml b/asmx/test/perf/mem.xml
new file mode 100644
index 0000000..afcb27b
--- /dev/null
+++ b/asmx/test/perf/mem.xml
@@ -0,0 +1,12 @@
+<project name="perf" default="test">
+  <target name="test">
+    <java classname="org.objectweb.asm.ASMMemTest" fork="yes">
+      <classpath>
+        <pathelement location="${out.build}"/>
+        <pathelement location="${out.test}"/>
+      </classpath>
+      <arg value="${java.home}/lib/rt.jar"/>
+      <arg value="2000"/>
+    </java>
+  </target>
+</project>
diff --git a/asmx/test/perf/org/objectweb/asm/ALLPerfTest.java b/asmx/test/perf/org/objectweb/asm/ALLPerfTest.java
new file mode 100644
index 0000000..97ca19e
--- /dev/null
+++ b/asmx/test/perf/org/objectweb/asm/ALLPerfTest.java
@@ -0,0 +1,491 @@
+/***
+ * ASM performance test: measures the performances of asm package
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.commons.EmptyVisitor;
+import org.objectweb.asm.tree.ClassNode;
+
+/**
+ * @author Eric Bruneton
+ */
+public abstract class ALLPerfTest extends ClassLoader {
+
+    private static ZipFile zip;
+
+    private static ZipOutputStream dst;
+
+    private static int mode;
+
+    private static int total;
+
+    private static int totalSize;
+
+    private static double[][] perfs;
+
+    static boolean compute;
+
+    static boolean skipDebug;
+
+    public static void main(String[] args) throws Exception {
+        ZipFile zip = new ZipFile(System.getProperty("java.home")
+                + "/lib/rt.jar");
+        List classes = new ArrayList();
+
+        Enumeration entries = zip.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry e = (ZipEntry) entries.nextElement();
+            String s = e.getName();
+            if (s.endsWith(".class")) {
+                s = s.substring(0, s.length() - 6).replace('/', '.');
+                InputStream is = zip.getInputStream(e);
+                classes.add(readClass(is));
+            }
+        }
+
+        for (int i = 0; i < 10; ++i) {
+            long t = System.currentTimeMillis();
+            for (int j = 0; j < classes.size(); ++j) {
+                byte[] b = (byte[]) classes.get(j);
+                new ClassReader(b).accept(new EmptyVisitor(), false);
+            }
+            t = System.currentTimeMillis() - t;
+            System.out.println("Time to deserialize " + classes.size()
+                    + " classes = " + t + " ms");
+        }
+
+        for (int i = 0; i < 10; ++i) {
+            long t = System.currentTimeMillis();
+            for (int j = 0; j < classes.size(); ++j) {
+                byte[] b = (byte[]) classes.get(j);
+                ClassWriter cw = new ClassWriter(false);
+                new ClassReader(b).accept(cw, false);
+                cw.toByteArray();
+            }
+            t = System.currentTimeMillis() - t;
+            System.out.println("Time to deserialize and reserialize "
+                    + classes.size() + " classes = " + t + " ms");
+        }
+
+        for (int i = 0; i < 10; ++i) {
+            long t = System.currentTimeMillis();
+            for (int j = 0; j < classes.size(); ++j) {
+                byte[] b = (byte[]) classes.get(j);
+                ClassReader cr = new ClassReader(b);
+                ClassWriter cw = new ClassWriter(cr, false);
+                cr.accept(cw, false);
+                cw.toByteArray();
+            }
+            t = System.currentTimeMillis() - t;
+            System.out.println("Time to deserialize and reserialize "
+                    + classes.size() + " classes (with copyPool) = " + t + " ms");
+        }
+        
+        for (int i = 0; i < 10; ++i) {
+            long t = System.currentTimeMillis();
+            for (int j = 0; j < classes.size(); ++j) {
+                byte[] b = (byte[]) classes.get(j);
+                ClassReader cr = new ClassReader(b);
+                ClassWriter cw = new ClassWriter(true);
+                cr.accept(cw, false);
+                cw.toByteArray();
+            }
+            t = System.currentTimeMillis() - t;
+            System.out.println("Time to deserialize and reserialize "
+                    + classes.size() + " classes (with computeMaxs) = " + t + " ms");
+        }
+
+        for (int i = 0; i < 10; ++i) {
+            long t = System.currentTimeMillis();
+            for (int j = 0; j < classes.size(); ++j) {
+                byte[] b = (byte[]) classes.get(j);
+                new ClassReader(b).accept(new ClassNode(), false);
+            }
+            t = System.currentTimeMillis() - t;
+            System.out.println("Time to deserialize " + classes.size()
+                    + " classes with tree package = " + t + " ms");
+        }
+
+        for (int i = 0; i < 10; ++i) {
+            long t = System.currentTimeMillis();
+            for (int j = 0; j < classes.size(); ++j) {
+                byte[] b = (byte[]) classes.get(j);
+                ClassWriter cw = new ClassWriter(false);
+                ClassNode cn = new ClassNode();
+                new ClassReader(b).accept(cn, false);
+                cn.accept(cw);
+                cw.toByteArray();
+            }
+            t = System.currentTimeMillis() - t;
+            System.out.println("Time to deserialize and reserialize "
+                    + classes.size() + " classes with tree package = " + t
+                    + " ms");
+        }
+
+        classes = null;
+
+        System.out.println("\nComparing ASM, BCEL, SERP and Javassist performances...");
+        System.out.println("This may take 20 to 30 minutes\n");
+        // measures performances
+        System.out.println("ASM PERFORMANCES\n");
+        new ASMPerfTest().perfs(args);
+        double[][] asmPerfs = perfs;
+        System.out.println("\nBCEL PERFORMANCES\n");
+        new BCELPerfTest().perfs(args);
+        double[][] bcelPerfs = perfs;
+        System.out.println("\nSERP PERFORMANCES\n");
+        new SERPPerfTest().perfs(args);
+        double[][] serpPerfs = perfs;
+        System.out.println("\nJavassist PERFORMANCES\n");
+        new JavassistPerfTest().perfs(args);
+        double[][] javassistPerfs = perfs;
+
+        // prints results
+        System.out.println("\nGLOBAL RESULTS");
+        System.out.println("\nWITH DEBUG INFORMATION\n");
+        for (int step = 0; step < 2; ++step) {
+            for (mode = 0; mode < 4; ++mode) {
+                switch (mode) {
+                    case 0:
+                        System.out.print("NO ADAPT:     ");
+                        break;
+                    case 1:
+                        System.out.print("NULL ADAPT:   ");
+                        break;
+                    case 2:
+                        System.out.print("COMPUTE MAXS: ");
+                        break;
+                    default:
+                        System.out.print("ADD COUNTER:  ");
+                        break;
+                }
+                System.out.print((float) asmPerfs[step][mode] + " ms");
+                if (mode > 0) {
+                    System.out.print(" (*");
+                    System.out.print((float) (asmPerfs[step][mode] / asmPerfs[step][0]));
+                    System.out.print(")");
+                }
+                System.out.print(" ");
+                System.out.print((float) bcelPerfs[step][mode] + " ms");
+                if (mode > 0) {
+                    System.out.print(" (*");
+                    System.out.print((float) (bcelPerfs[step][mode] / bcelPerfs[step][0]));
+                    System.out.print(")");
+                }
+                System.out.print(" ");
+                System.out.print((float) serpPerfs[step][mode] + " ms");
+                if (mode > 0) {
+                    System.out.print(" (*");
+                    System.out.print((float) (serpPerfs[step][mode] / serpPerfs[step][0]));
+                    System.out.print(")");
+                }
+                System.out.print(" ");
+                System.out.print((float) javassistPerfs[step][mode] + " ms");
+                if (mode > 0) {
+                    System.out.print(" (*");
+                    System.out.print((float) (javassistPerfs[step][mode] / javassistPerfs[step][0]));
+                    System.out.print(")");
+                }
+                System.out.println();
+            }
+            if (step == 0) {
+                System.out.println("\nWITHOUT DEBUG INFORMATION\n");
+            }
+        }
+
+        System.out.println("\nRELATIVE RESULTS");
+        System.out.println("\nWITH DEBUG INFORMATION\n");
+        for (int step = 0; step < 2; ++step) {
+            System.err.println("[MEASURE      ASM       BCEL      SERP Javassist]");
+            for (mode = 1; mode < 4; ++mode) {
+                int base;
+                switch (mode) {
+                    case 1:
+                        System.out.print("NULL ADAPT:   ");
+                        base = 0;
+                        break;
+                    case 2:
+                        System.out.print("COMPUTE MAXS: ");
+                        base = 1;
+                        break;
+                    default:
+                        System.out.print("ADD COUNTER:  ");
+                        base = 1;
+                        break;
+                }
+                double ref = asmPerfs[step][mode] - asmPerfs[step][base];
+                System.out.print((float) ref + " ms ");
+                double f = bcelPerfs[step][mode] - bcelPerfs[step][base];
+                System.out.print((float) f + " ms (*");
+                System.out.print((float) (f / ref));
+                System.out.print(") ");
+                double g = serpPerfs[step][mode] - serpPerfs[step][base];
+                System.out.print((float) g + " ms (*");
+                System.out.print((float) (g / ref));
+                System.out.print(")");
+                double h = javassistPerfs[step][mode]
+                        - javassistPerfs[step][base];
+                System.out.print((float) h + " ms (*");
+                System.out.print((float) (h / ref));
+                System.out.print(")");
+                System.out.println();
+            }
+            if (step == 0) {
+                System.out.println("\nWITHOUT DEBUG INFORMATION\n");
+            }
+        }
+    }
+
+    void perfs(final String[] args) throws Exception {
+        // prepares zip files, if necessary
+        if (!(new File(args[0] + "classes1.zip").exists())) {
+            System.out.println("Preparing zip files from " + args[1] + "...");
+            for (int step = 0; step < 2; ++step) {
+                dst = new ZipOutputStream(new FileOutputStream(args[0]
+                        + "classes" + (step + 1) + ".zip"));
+                mode = step == 0 ? 1 : 4;
+                for (int i = 1; i < args.length; ++i) {
+                    ALLPerfTest loader = newInstance();
+                    zip = new ZipFile(args[i]);
+                    Enumeration entries = zip.entries();
+                    while (entries.hasMoreElements()) {
+                        String s = ((ZipEntry) entries.nextElement()).getName();
+                        if (s.endsWith(".class")) {
+                            s = s.substring(0, s.length() - 6)
+                                    .replace('/', '.');
+                            loader.loadClass(s);
+                        }
+                    }
+                }
+                dst.close();
+                dst = null;
+            }
+            System.out.println();
+        }
+
+        // measures performances
+        perfs = new double[2][4];
+        System.out.println("FIRST STEP: WITH DEBUG INFORMATION");
+        for (int step = 0; step < 2; ++step) {
+            zip = new ZipFile(args[0] + "classes" + (step + 1) + ".zip");
+            for (mode = 0; mode < 4; ++mode) {
+                for (int i = 0; i < 4; ++i) {
+                    ALLPerfTest loader = newInstance();
+                    total = 0;
+                    totalSize = 0;
+                    Enumeration entries = zip.entries();
+                    double t = System.currentTimeMillis();
+                    while (entries.hasMoreElements()) {
+                        String s = ((ZipEntry) entries.nextElement()).getName();
+                        if (s.endsWith(".class")) {
+                            s = s.substring(0, s.length() - 6)
+                                    .replace('/', '.');
+                            loader.loadClass(s);
+                        }
+                    }
+                    t = System.currentTimeMillis() - t;
+                    if (i == 0) {
+                        perfs[step][mode] = t;
+                    } else {
+                        perfs[step][mode] = Math.min(perfs[step][mode], t);
+                    }
+                    switch (mode) {
+                        case 0:
+                            System.out.print("NO ADAPT:     ");
+                            break;
+                        case 1:
+                            System.out.print("NULL ADAPT:   ");
+                            break;
+                        case 2:
+                            System.out.print("COMPUTE MAXS: ");
+                            break;
+                        default:
+                            System.out.print("ADD COUNTER:  ");
+                            break;
+                    }
+                    System.out.print((float) t + " ms ");
+                    System.out.print("(" + total + " classes");
+                    System.out.println(", " + totalSize + " bytes)");
+                    loader = null;
+                    gc();
+                }
+            }
+            if (step == 0) {
+                System.out.println("SECOND STEP: WITHOUT DEBUG INFORMATION");
+            }
+        }
+
+        // prints results
+        System.out.println("\nRESULTS");
+        System.out.println("\nWITH DEBUG INFORMATION\n");
+        for (int step = 0; step < 2; ++step) {
+            for (mode = 0; mode < 4; ++mode) {
+                switch (mode) {
+                    case 0:
+                        System.out.print("NO ADAPT:     ");
+                        break;
+                    case 1:
+                        System.out.print("NULL ADAPT:   ");
+                        break;
+                    case 2:
+                        System.out.print("COMPUTE MAXS: ");
+                        break;
+                    default:
+                        System.out.print("ADD COUNTER:  ");
+                        break;
+                }
+                System.out.println((float) perfs[step][mode] + " ms");
+            }
+            if (step == 0) {
+                System.out.println("\nWITHOUT DEBUG INFORMATION\n");
+            }
+        }
+    }
+
+    private static byte[] readClass(final InputStream is) throws IOException {
+        if (is == null) {
+            throw new IOException("Class not found");
+        }
+        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;
+            } else {
+                len += n;
+                if (len == b.length) {
+                    byte[] c = new byte[b.length + 1000];
+                    System.arraycopy(b, 0, c, 0, len);
+                    b = c;
+                }
+            }
+        }
+    }
+
+    protected Class findClass(final String name) throws ClassNotFoundException {
+        try {
+            byte[] b;
+            String fileName = name.replace('.', '/') + ".class";
+            InputStream is = zip.getInputStream(zip.getEntry(fileName));
+            switch (mode) {
+                case 0:
+                    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;
+                            }
+                            break;
+                        } else {
+                            len += n;
+                            if (len == b.length) {
+                                byte[] c = new byte[b.length + 1000];
+                                System.arraycopy(b, 0, c, 0, len);
+                                b = c;
+                            }
+                        }
+                    }
+                    break;
+                case 1:
+                    compute = false;
+                    skipDebug = false;
+                    b = nullAdaptClass(is, name);
+                    break;
+                case 2:
+                    compute = true;
+                    skipDebug = false;
+                    b = nullAdaptClass(is, name);
+                    break;
+                case 3:
+                    b = counterAdaptClass(is, name);
+                    break;
+                // case 4:
+                default:
+                    compute = false;
+                    skipDebug = true;
+                    b = nullAdaptClass(is, name);
+                    break;
+            }
+            if (dst != null) {
+                dst.putNextEntry(new ZipEntry(fileName));
+                dst.write(b, 0, b.length);
+                dst.closeEntry();
+            }
+            total += 1;
+            totalSize += b.length;
+            return defineClass(name, b, 0, b.length);
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new ClassNotFoundException(name);
+        }
+    }
+
+    private static void gc() {
+        try {
+            Runtime.getRuntime().gc();
+            Thread.sleep(50);
+            Runtime.getRuntime().gc();
+            Thread.sleep(50);
+            Runtime.getRuntime().gc();
+            Thread.sleep(50);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    abstract ALLPerfTest newInstance();
+
+    abstract byte[] nullAdaptClass(final InputStream is, final String name)
+            throws Exception;
+
+    abstract byte[] counterAdaptClass(final InputStream is, final String name)
+            throws Exception;
+}
diff --git a/asmx/test/perf/org/objectweb/asm/ASMMemTest.java b/asmx/test/perf/org/objectweb/asm/ASMMemTest.java
new file mode 100644
index 0000000..6581c4b
--- /dev/null
+++ b/asmx/test/perf/org/objectweb/asm/ASMMemTest.java
@@ -0,0 +1,212 @@
+/***
+ * ASM performance test: measures the performances of asm package
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/*
+ * Created on Nov 30, 2004 as part of ASMPerf by treffer
+ */
+
+/**
+ * Memory performances tests for tree package.
+ * 
+ * @author treffer
+ */
+public class ASMMemTest {
+
+    public static void main(String[] args) {
+        if (args.length < 2) {
+            System.out.println("java ASMMemTest <jar-file> <number-of-classes>");
+            System.exit(1);
+        }
+
+        Runtime runtime = Runtime.getRuntime();
+        memDown(runtime);
+        System.out.println("Initial memory load: ".concat(memFormat(getUsedMem(runtime))));
+
+        LinkedList fileData = new LinkedList();
+        int limit = Integer.parseInt(args[1]);
+        try {
+            long totalSize = 0;
+            JarInputStream jar = new JarInputStream(new FileInputStream(args[0]));
+            JarEntry entry = jar.getNextJarEntry();
+            while ((fileData.size() < limit) && (entry != null)) {
+                String name = entry.getName();
+                if (name.endsWith(".class")) {
+                    if (entry.getSize() != -1) {
+                        int len = (int) entry.getSize();
+                        byte[] data = new byte[len];
+                        jar.read(data);
+                        fileData.add(data);
+                        totalSize += data.length;
+                    } else {
+                        System.err.println("No jar-entry size given... Unimplemented, jar file not supported");
+                    }
+                }
+                entry = jar.getNextJarEntry();
+            }
+            System.out.println(memFormat(totalSize) + " class data, ~"
+                    + memFormat(totalSize / limit) + " per class.");
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+
+        ArrayList result = new ArrayList(fileData.size());
+        long startmem;
+
+        for (int i = 0; i < 10; i++) {
+            System.out.println("\n> Run ".concat(Integer.toString(i + 1)));
+            Iterator files = fileData.iterator();
+            result.clear();
+            memDown(runtime);
+            System.out.println("Empty memory load: ".concat(memFormat(startmem = getUsedMem(runtime))));
+
+            long time = -System.currentTimeMillis();
+            while (files.hasNext()) {
+                byte data[] = (byte[]) files.next();
+                ClassReader reader = new ClassReader(data);
+                ClassNode clazz = new ClassNode();
+                reader.accept(clazz, false);
+                result.add(clazz);
+            }
+            time += System.currentTimeMillis();
+
+            memDown(runtime);
+            System.out.println("Time: ".concat(timeFormat(time)));
+            System.out.println("Final memory load: ".concat(memFormat(getUsedMem(runtime))));
+            System.out.println("ASM memory load: ".concat(memFormat(getUsedMem(runtime)
+                    - startmem)));
+            for (int j = 0; j < limit; j++) {
+                ClassNode clazz = (ClassNode) result.get(j);
+                List l = clazz.methods;
+                for (int k = 0, lim = l.size(); k < lim; k++) {
+                    MethodNode m = (MethodNode) l.get(k);
+                    List insn = m.instructions;
+                    if (insn != null)
+                        insn.clear();
+                    /*
+                     * for (int ins = 0, insmax = insn.size(); ins < insmax;
+                     * ins++) { if (insn.get(ins) instanceof VarInsnNode) {
+                     * insn.set(ins, null); } }
+                     */
+                }
+            }
+            memDown(runtime);
+            System.out.println("ASM memory load (removed method code): ".concat(memFormat(getUsedMem(runtime)
+                    - startmem)));
+        }
+
+    }
+
+    public final static long getUsedMem(final Runtime r) {
+        return r.totalMemory() - r.freeMemory();
+    }
+
+    public final static String timeFormat(final long time) {
+        int min = (int) (time / (60 * 1000));
+        int sec = (int) ((time / (1000)) % 60);
+        int msec = (int) (time % 1000);
+        StringBuffer sbuf = new StringBuffer(30);
+        if (min > 0) {
+            sbuf.append(min);
+            sbuf.append("min ");
+        }
+        if ((sec > 0) || (min > 0)) {
+            sbuf.append(sec);
+            sbuf.append("s ");
+        }
+        if ((msec > 0) || (sec > 0) || (min > 0)) {
+            sbuf.append(msec);
+            sbuf.append("ms ");
+        }
+        sbuf.append("(");
+        sbuf.append(time);
+        sbuf.append("ms)");
+        return sbuf.toString();
+    }
+
+    public final static String memFormat(final long mem) {
+        int gb = (int) ((mem >> 30) & 0x3FF);
+        int mb = (int) ((mem >> 20) & 0x3FF);
+        int kb = (int) ((mem >> 10) & 0x3FF);
+        int bytes = (int) (mem & 0x3FF);
+        StringBuffer sbuf = new StringBuffer(30);
+        if (gb > 0) {
+            sbuf.append(gb);
+            sbuf.append("GB ");
+        }
+        if ((mb > 0) || (gb > 0)) {
+            sbuf.append(mb);
+            sbuf.append("MB ");
+        }
+        if ((kb > 0) || (mb > 0) || (gb > 0)) {
+            sbuf.append(kb);
+            sbuf.append("KB ");
+        }
+        if ((bytes > 0) || (kb > 0) || (mb > 0) || (gb > 0)) {
+            sbuf.append(bytes);
+            sbuf.append("bytes ");
+        }
+        sbuf.append("(");
+        sbuf.append(mem);
+        sbuf.append("bytes)");
+        return sbuf.toString();
+    }
+
+    public final static void memDown(final Runtime r) {
+        long oldmem;
+        do {
+            oldmem = getUsedMem(r);
+            for (int i = 0; i < 10; i++) {
+                // Calling System.gc once is very unsafe
+                System.gc();
+                try {
+                    Thread.sleep(10);
+                } catch (InterruptedException ie) {
+                }
+            }
+        } while (getUsedMem(r) < oldmem);
+    }
+}
\ No newline at end of file
diff --git a/asmx/test/perf/org/objectweb/asm/ASMPerfTest.java b/asmx/test/perf/org/objectweb/asm/ASMPerfTest.java
new file mode 100644
index 0000000..1ac7d18
--- /dev/null
+++ b/asmx/test/perf/org/objectweb/asm/ASMPerfTest.java
@@ -0,0 +1,138 @@
+/***
+ * ASM performance test: measures the performances of asm package
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.io.InputStream;
+
+/**
+ * @author Eric Bruneton
+ */
+public class ASMPerfTest extends ALLPerfTest {
+
+    final static Integer ONE = new Integer(1);
+
+    public static void main(final String args[]) throws Exception {
+        System.out.println("ASM PERFORMANCES\n");
+        new ASMPerfTest().perfs(args);
+    }
+
+    ALLPerfTest newInstance() {
+        return new ASMPerfTest();
+    }
+
+    byte[] nullAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(compute);
+        ClassAdapter ca = new ClassAdapter(cw);
+        cr.accept(ca, skipDebug);
+        return cw.toByteArray();
+    }
+
+    byte[] counterAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        ClassReader cr = new ClassReader(is);
+        ClassWriter cw = new ClassWriter(false);
+        ClassAdapter ca = new CounterClassAdapter(cw);
+        cr.accept(ca, false);
+        return cw.toByteArray();
+    }
+
+    static class CounterClassAdapter extends ClassAdapter implements Opcodes {
+
+        private String owner;
+
+        CounterClassAdapter(ClassVisitor cv) {
+            super(cv);
+        }
+
+        public void visit(
+            int version,
+            int access,
+            String name,
+            String signature,
+            String superName,
+            String[] interfaces)
+        {
+            super.visit(version, access, name, signature, superName, interfaces);
+            if ((access & ACC_INTERFACE) == 0) {
+                cv.visitField(ACC_PUBLIC, "_counter", "I", null, null);
+            }
+            owner = name;
+        }
+
+        public MethodVisitor visitMethod(
+            int access,
+            String name,
+            String desc,
+            String signature,
+            String[] exceptions)
+        {
+            MethodVisitor mv = super.visitMethod(access,
+                    name,
+                    desc,
+                    signature,
+                    exceptions);
+            if (!name.equals("<init>")
+                    && (access & (ACC_STATIC | ACC_NATIVE | ACC_ABSTRACT)) == 0)
+            {
+                return new CounterMethodAdapter(mv, owner);
+            }
+            return mv;
+        }
+    }
+
+    static class CounterMethodAdapter extends MethodAdapter implements Opcodes {
+
+        CounterMethodAdapter(MethodVisitor mv, String owner) {
+            super(mv);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETFIELD, owner, "_counter", "I");
+            mv.visitLdcInsn(ONE);
+            mv.visitInsn(IADD);
+            mv.visitFieldInsn(PUTFIELD, owner, "_counter", "I");
+        }
+
+        public void visitMaxs(int maxStack, int maxLocals) {
+            super.visitMaxs(Math.max(maxStack, 2), maxLocals);
+        }
+    }
+}
diff --git a/asmx/test/perf/org/objectweb/asm/BCELPerfTest.java b/asmx/test/perf/org/objectweb/asm/BCELPerfTest.java
new file mode 100644
index 0000000..ae99f78
--- /dev/null
+++ b/asmx/test/perf/org/objectweb/asm/BCELPerfTest.java
@@ -0,0 +1,142 @@
+/***
+ * ASM performance test: measures the performances of asm package
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import org.apache.bcel.Constants;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.generic.ALOAD;
+import org.apache.bcel.generic.ClassGen;
+import org.apache.bcel.generic.ConstantPoolGen;
+import org.apache.bcel.generic.FieldGen;
+import org.apache.bcel.generic.GETFIELD;
+import org.apache.bcel.generic.IADD;
+import org.apache.bcel.generic.ICONST;
+import org.apache.bcel.generic.InstructionHandle;
+import org.apache.bcel.generic.InstructionList;
+import org.apache.bcel.generic.MethodGen;
+import org.apache.bcel.generic.PUTFIELD;
+import org.apache.bcel.generic.Type;
+
+import java.io.InputStream;
+
+/**
+ * @author Eric Bruneton
+ */
+public class BCELPerfTest extends ALLPerfTest implements Constants {
+
+    public static void main(final String args[]) throws Exception {
+        System.out.println("BCEL PERFORMANCES\n");
+        new BCELPerfTest().perfs(args);
+    }
+
+    ALLPerfTest newInstance() {
+        return new BCELPerfTest();
+    }
+
+    byte[] nullAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        JavaClass jc = new ClassParser(is, name + ".class").parse();
+        ClassGen cg = new ClassGen(jc);
+        ConstantPoolGen cp = cg.getConstantPool();
+        Method[] ms = cg.getMethods();
+        for (int j = 0; j < ms.length; ++j) {
+            MethodGen mg = new MethodGen(ms[j], cg.getClassName(), cp);
+            boolean lv = ms[j].getLocalVariableTable() == null;
+            boolean ln = ms[j].getLineNumberTable() == null;
+            if (lv) {
+                mg.removeLocalVariables();
+            }
+            if (ln) {
+                mg.removeLineNumbers();
+            }
+            mg.stripAttributes(skipDebug);
+            InstructionList il = mg.getInstructionList();
+            if (il != null) {
+                InstructionHandle ih = il.getStart();
+                while (ih != null) {
+                    ih = ih.getNext();
+                }
+                if (compute) {
+                    mg.setMaxStack();
+                    mg.setMaxLocals();
+                }
+            }
+            cg.replaceMethod(ms[j], mg.getMethod());
+        }
+        return cg.getJavaClass().getBytes();
+    }
+
+    byte[] counterAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        JavaClass jc = new ClassParser(is, name + ".class").parse();
+        ClassGen cg = new ClassGen(jc);
+        ConstantPoolGen cp = cg.getConstantPool();
+        if (!cg.isInterface()) {
+            FieldGen fg = new FieldGen(ACC_PUBLIC,
+                    Type.getType("I"),
+                    "_counter",
+                    cp);
+            cg.addField(fg.getField());
+        }
+        Method[] ms = cg.getMethods();
+        for (int j = 0; j < ms.length; ++j) {
+            MethodGen mg = new MethodGen(ms[j], cg.getClassName(), cp);
+            if (!mg.getName().equals("<init>") && !mg.isStatic()
+                    && !mg.isAbstract() && !mg.isNative())
+            {
+                if (mg.getInstructionList() != null) {
+                    InstructionList il = new InstructionList();
+                    il.append(new ALOAD(0));
+                    il.append(new ALOAD(0));
+                    il.append(new GETFIELD(cp.addFieldref(name, "_counter", "I")));
+                    il.append(new ICONST(1));
+                    il.append(new IADD());
+                    il.append(new PUTFIELD(cp.addFieldref(name, "_counter", "I")));
+                    mg.getInstructionList().insert(il);
+                    mg.setMaxStack(Math.max(mg.getMaxStack(), 2));
+                    boolean lv = ms[j].getLocalVariableTable() == null;
+                    boolean ln = ms[j].getLineNumberTable() == null;
+                    if (lv) {
+                        mg.removeLocalVariables();
+                    }
+                    if (ln) {
+                        mg.removeLineNumbers();
+                    }
+                    cg.replaceMethod(ms[j], mg.getMethod());
+                }
+            }
+        }
+        return cg.getJavaClass().getBytes();
+    }
+}
diff --git a/asmx/test/perf/org/objectweb/asm/JavassistPerfTest.java b/asmx/test/perf/org/objectweb/asm/JavassistPerfTest.java
new file mode 100644
index 0000000..a8a1273
--- /dev/null
+++ b/asmx/test/perf/org/objectweb/asm/JavassistPerfTest.java
@@ -0,0 +1,112 @@
+/***
+ * ASM performance test: measures the performances of asm package
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import java.io.InputStream;
+import java.lang.reflect.Modifier;
+
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeIterator;
+import javassist.bytecode.MethodInfo;
+import javassist.bytecode.Opcode;
+
+/**
+ * @author Eric Bruneton
+ */
+public class JavassistPerfTest extends ALLPerfTest {
+
+    public static void main(final String args[]) throws Exception {
+        System.out.println("Javassist PERFORMANCES\n");
+        new JavassistPerfTest().perfs(args);
+    }
+
+    ClassPool pool;
+
+    public JavassistPerfTest() {
+        pool = new ClassPool(null);
+    }
+
+    ALLPerfTest newInstance() {
+        return new JavassistPerfTest();
+    }
+
+    byte[] nullAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        CtClass cc = pool.makeClass(is);
+        CtMethod[] ms = cc.getDeclaredMethods();
+        for (int j = 0; j < ms.length; ++j) {
+            if (skipDebug) {
+                // is there a mean to remove the debug attributes?
+            }
+            if (compute) {
+                // how to force recomputation of maxStack and maxLocals?
+            }
+        }
+        return cc.toBytecode();
+    }
+
+    byte[] counterAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        CtClass cc = pool.makeClass(is);
+        if (!cc.isInterface()) {
+            cc.addField(new CtField(CtClass.intType, "_counter", cc));
+        }
+        CtMethod[] ms = cc.getDeclaredMethods();
+        for (int j = 0; j < ms.length; ++j) {
+            CtMethod m = ms[j];
+            int modifiers = m.getModifiers();
+            if (!Modifier.isStatic(modifiers)
+                    && !Modifier.isAbstract(modifiers)
+                    && !Modifier.isNative(modifiers))
+            {
+                if (!m.isEmpty()) {
+                    MethodInfo info = m.getMethodInfo();
+                    Bytecode bc = new Bytecode(info.getConstPool(), 1, 0);
+                    bc.addAload(0);
+                    bc.addAload(0);
+                    bc.addGetfield(cc, "_counter", "I");
+                    bc.add(Opcode.ICONST_1);
+                    bc.add(Opcode.IADD);
+                    bc.addPutfield(cc, "_counter", "I");
+                    CodeIterator iter = info.getCodeAttribute().iterator();
+                    iter.begin();
+                    iter.insert(bc.get());
+                }
+            }
+        }
+        return cc.toBytecode();
+    }
+}
diff --git a/asmx/test/perf/org/objectweb/asm/SERPPerfTest.java b/asmx/test/perf/org/objectweb/asm/SERPPerfTest.java
new file mode 100644
index 0000000..3967868
--- /dev/null
+++ b/asmx/test/perf/org/objectweb/asm/SERPPerfTest.java
@@ -0,0 +1,112 @@
+/***
+ * ASM performance test: measures the performances of asm package
+ * Copyright (c) 2002-2005 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 org.objectweb.asm;
+
+import serp.bytecode.BCClass;
+import serp.bytecode.BCMethod;
+import serp.bytecode.Code;
+import serp.bytecode.Project;
+
+import java.io.InputStream;
+
+/**
+ * @author Eric Bruneton
+ */
+public class SERPPerfTest extends ALLPerfTest {
+
+    private static Project p = new Project();
+
+    private static BCClass c;
+
+    public static void main(final String args[]) throws Exception {
+        System.out.println("SERP PERFORMANCES\n");
+        new SERPPerfTest().perfs(args);
+    }
+
+    ALLPerfTest newInstance() {
+        return new SERPPerfTest();
+    }
+
+    byte[] nullAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        if (c != null) {
+            p.removeClass(c);
+        }
+        c = p.loadClass(is);
+        c.getDeclaredFields();
+        BCMethod[] methods = c.getDeclaredMethods();
+        for (int i = 0; i < methods.length; ++i) {
+            Code code = methods[i].getCode(false);
+            if (code != null) {
+                while (code.hasNext()) {
+                    code.next();
+                }
+                if (compute) {
+                    code.calculateMaxStack();
+                    code.calculateMaxLocals();
+                }
+            }
+        }
+        return c.toByteArray();
+    }
+
+    byte[] counterAdaptClass(final InputStream is, final String name)
+            throws Exception
+    {
+        if (c != null) {
+            p.removeClass(c);
+        }
+        c = p.loadClass(is);
+        c.getDeclaredFields();
+        if (!c.isInterface()) {
+            c.declareField("_counter", "I");
+        }
+        BCMethod[] methods = c.getDeclaredMethods();
+        for (int i = 0; i < methods.length; ++i) {
+            BCMethod m = methods[i];
+            if (!m.getName().equals("<init>") && !m.isStatic()
+                    && !m.isAbstract() && !m.isNative())
+            {
+                Code code = m.getCode(false);
+                if (code != null) {
+                    code.aload().setLocal(0);
+                    code.aload().setLocal(0);
+                    code.getfield().setField(name, "_counter", "I");
+                    code.constant().setValue(1);
+                    code.iadd();
+                    code.putfield().setField(name, "_counter", "I");
+                    code.setMaxStack(Math.max(code.getMaxStack(), 2));
+                }
+            }
+        }
+        return c.toByteArray();
+    }
+}
diff --git a/asmx/test/perf/org/objectweb/asm/xml/XMLPerfTest.java b/asmx/test/perf/org/objectweb/asm/xml/XMLPerfTest.java
new file mode 100644
index 0000000..5d138bf
--- /dev/null
+++ b/asmx/test/perf/org/objectweb/asm/xml/XMLPerfTest.java
@@ -0,0 +1,199 @@
+/***
+ * ASM XML Adapter
+ * Copyright (c) 2004, Eugene Kuleshov
+ * 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 org.objectweb.asm.xml;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+
+/**
+ * Performance test suite for ASM XML
+ * 
+ * @author Eugene Kuleshov
+ */
+public class XMLPerfTest {
+
+    private static final String[] ENGINES = {
+        "jd.xml.xslt.trax.TransformerFactoryImpl",
+        "net.sf.saxon.TransformerFactoryImpl",
+        "org.apache.xalan.processor.TransformerFactoryImpl", };
+
+    private static final String[] TEMPLATES = {
+        "copy.xsl",
+        "linenumbers.xsl",
+        "profile.xsl", };
+
+    public static void main(String[] args) throws Exception {
+        System.err.println("Comparing XSLT performance for ASM XSLT");
+        System.err.println("This may take 20 to 30 minutes\n");
+
+        File examplesDir = new File(args[0]);
+        if (!examplesDir.isDirectory()) {
+            System.err.println(args[0] + " must be directory");
+            return;
+        }
+
+        // File[] templates = examplesDir.listFiles(new FilenameFilter() {
+        // public boolean accept(File dir, String name) {
+        // return name.endsWith(".xsl");
+        // }
+        // });
+
+        for (int i = 0; i < ENGINES.length; i++) {
+            System.err.println(ENGINES[i]);
+            process(null, ENGINES[i]);
+            for (int j = 0; j < TEMPLATES.length; j++) {
+                process(new File(examplesDir, TEMPLATES[j]).getAbsolutePath(),
+                        ENGINES[i]);
+            }
+            System.err.println();
+        }
+
+    }
+
+    private static void process(String name, String engine) throws Exception {
+        System.setProperty("javax.xml.transform.TransformerFactory", engine);
+        processRep(name, Processor.BYTECODE);
+        processRep(name, Processor.MULTI_XML);
+        processRep(name, Processor.SINGLE_XML);
+    }
+
+    // private static void processEntry(
+    // String className,
+    // TransformerHandler handler) throws Exception
+    // {
+    // byte[] classData = getCode(new URL(className).openStream());
+    // ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    //
+    // handler.setResult(new SAXResult(new ASMContentHandler(bos, false)));
+    //
+    // ClassReader cr = new ClassReader(classData);
+    // cr.accept(new SAXClassAdapter(handler, cr.getVersion(), false), false);
+    // }
+    //
+    // private static byte[] getCode(InputStream is) throws IOException {
+    // ByteArrayOutputStream bos = new ByteArrayOutputStream();
+    // byte[] buff = new byte[1024];
+    // int n = -1;
+    // while ((n = is.read(buff)) > -1)
+    // bos.write(buff, 0, n);
+    // return bos.toByteArray();
+    // }
+
+    private static void processRep(String name, int outRep) {
+        long l1 = System.currentTimeMillis();
+        int n = 0;
+        try {
+            Class c = XMLPerfTest.class;
+            String u = c.getResource("/java/lang/String.class").toString();
+            final InputStream is = new BufferedInputStream(new URL(u.substring(4,
+                    u.indexOf('!'))).openStream());
+            final OutputStream os = new IgnoringOutputStream();
+            final StreamSource xslt = name == null
+                    ? null
+                    : new StreamSource(new FileInputStream(name));
+
+            Processor p = new DotObserver(Processor.BYTECODE,
+                    outRep,
+                    is,
+                    os,
+                    xslt);
+            n = p.process();
+
+        } catch (Exception ex) {
+            System.err.println();
+            // ex.printStackTrace();
+            System.err.println(ex);
+
+        }
+
+        long l2 = System.currentTimeMillis();
+
+        System.err.println();
+        System.err.println("  " + outRep + " " + name + "  " + (l2 - l1)
+                + "ms  " + (1000f * n / (l2 - l1)));
+
+        // SAXTransformerFactory saxtf = (SAXTransformerFactory)
+        // TransformerFactory.newInstance();
+        // Templates templates = saxtf.newTemplates(xslt);
+        //
+        // ZipEntry ze = null;
+        // int max = 10000;
+        // while ((ze = zis.getNextEntry()) != null && max > 0) {
+        // if (ze.getName().endsWith(".class")) {
+        // processEntry(u.substring(0, n + 2).concat(ze.getName()),
+        // saxtf.newTransformerHandler(templates));
+        // max--;
+        // }
+        // }
+    }
+
+    private static final class DotObserver extends Processor {
+        private int n = 0;
+
+        public DotObserver(
+            int inRepresenation,
+            int outRepresentation,
+            InputStream input,
+            OutputStream output,
+            Source xslt)
+        {
+            super(inRepresenation, outRepresentation, input, output, xslt);
+        }
+
+        public void update(Object arg) {
+            n++;
+            if ((n % 1000) == 0) {
+                System.err.print("" + (n / 1000));
+            } else if ((n % 100) == 0) {
+                System.err.print(".");
+            }
+        }
+    }
+
+    private static final class IgnoringOutputStream extends OutputStream {
+
+        public final void write(int b) throws IOException {
+        }
+
+        public final void write(byte[] b) throws IOException {
+        }
+
+        public final void write(byte[] b, int off, int len) throws IOException {
+        }
+    }
+}
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..6566935
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,222 @@
+<project name="annotation-tools" default="all"
+         xmlns:if="ant:if" xmlns:unless="ant:unless">
+
+  <property file="${user.home}/.annotations-tools.properties" />
+  <property file="build.properties" />
+
+  <!-- default location of projects, has effect only if not previously set -->
+  <property name="asmx" location="asmx"/>
+  <property name="scene-lib" location="scene-lib"/>
+  <property name="afu" location="annotation-file-utilities"/>
+
+  <target name="prep" description="Create required directories/files">
+    <!-- Ant's copy task does not retain file permissions,
+         so use <exec executable="cp"> instead.
+    <copy file="bin-devel/git.pre-commit" tofile="../.git/hooks/pre-commit" preservelastmodified="true" />
+    -->
+<!-- TODO
+    <exec executable="cp">
+      <arg value="-p"/>
+      <arg value=".git.pre-commit"/>
+      <arg value=".git/hooks/pre-commit"/>
+    </exec>
+-->
+  </target>
+
+  <target name="all" description="Compile, run Javadoc, and run tests"
+      depends="prep,compile,javadoc,test"/>
+
+  <target name="compile" description="Compile all annotation tools"
+      depends="compile-all"/>
+
+  <target name="test" description="Test all the tools"
+      depends="test-all"/>
+
+  <target name="clean" depends="clean-all"/>
+
+  <target name="compile-asmx">
+      <ant dir="${asmx}" target="compile">
+	  <property name="product.noshrink" value="true"/>
+      </ant>
+  </target>
+
+  <!-- TODO: Get this working -->
+  <!-- Known to fail -->
+  <target name="test-asmx">
+      <ant dir="${asmx}" target="test"/>
+  </target>
+
+  <target name="compile-scene-lib">
+      <ant dir="${scene-lib}" target="bin"/>
+  </target>
+
+  <target name="javadoc-scene-lib">
+      <ant dir="${scene-lib}" target="javadoc"/>
+  </target>
+
+  <target name="test-scene-lib">
+      <ant dir="${scene-lib}" target="test"/>
+  </target>
+
+  <target name="compile-afu">
+      <ant dir="${afu}" target="jarfile"/>
+  </target>
+
+  <target name="javadoc-afu">
+      <ant dir="${afu}" target="javadoc"/>
+  </target>
+
+  <target name="test-afu">
+      <ant dir="${afu}" target="run-tests"/>
+  </target>
+
+  <target name="compile-all"
+	 depends="compile-asmx,compile-scene-lib,compile-afu"/>
+  <!-- TODO: Add asmx tests when they work -->
+  <target name="test-all"
+	 depends="test-scene-lib,test-afu"/>
+  <target name="clean-all">
+    <ant dir="${asmx}" target="clean"/>
+    <ant dir="${scene-lib}" target="clean"/>
+    <ant dir="${afu}" target="clean"/>
+  </target>
+
+  <target name="javadoc" depends="javadoc-scene-lib,javadoc-afu">
+  </target>
+
+  <target name="tags-scene-lib">
+      <ant dir="${scene-lib}" target="tags"/>
+  </target>
+
+  <target name="tags-afu">
+      <ant dir="${afu}" target="tags"/>
+  </target>
+
+  <target name="tags" depends="tags-scene-lib,tags-afu">
+    <exec executable="etags" failonerror="true">
+      <arg value="-i"/>
+      <arg value="${scene-lib}/TAGS"/>
+      <arg value="-i"/>
+      <arg value="${afu}/TAGS"/>
+    </exec>
+  </target>
+
+  <target name="html-validate"
+	  description="Validate that HTML files are well-formed; only works with JDK 8"
+	  depends="prep">
+    <exec executable="html5validator">
+      <arg value="--ignore"/>
+      <arg value="/api/"/>
+      <arg value="/build/"/>
+      <arg value="/javadoc/"/>
+      <arg value="/annotation-file-utilities/annotation-file-format.html"/>
+      <arg value="/scene-lib/javadoc/"/>
+    </exec>
+  </target>
+
+  <property name="style.args1" value="-r -n -e"/>
+  <property name="style.args2" value="--exclude-dir=.git --exclude-dir=api --exclude-dir=asmx --exclude-dir=javadoc --exclude='*.aux' --exclude='*.class' --exclude='*.dvi' --exclude='*.eps' --exclude='*.jaif' --exclude='*.jar' --exclude='*.jtr' --exclude='*.log' --exclude='*.patch' --exclude='*.pdf' --exclude='*.png' --exclude='*.sty' --exclude='*.zip' --exclude='*~' --exclude='CFLogo.ai' --exclude='logfile.log.rec.index' --exclude='annotation-file-format.html' ."/>
+
+  <target name="check-style"
+	  description="Check basic style guidelines"
+	  depends="prep">
+    <!-- There should be a way to templatize the following. -->
+    <exec executable="grep" outputproperty="trailingwhitespace" failonerror="false">
+      <arg line="${style.args1}"/>
+      <arg value=" $"/>
+      <arg line="${style.args2}"/>
+    </exec>
+    <fail message="Trailing whitespace:${line.separator}${trailingwhitespace}">
+      <condition>
+	<not>
+	  <equals arg1="${trailingwhitespace}" arg2=""/>
+	</not>
+      </condition>
+    </fail>
+    <exec executable="grep" outputproperty="missingspace" failonerror="false">
+      <arg line="${style.args1}"/>
+      <arg value="[^\\]\b\(else\|finally\|try\){\|}\(catch\|else\|finally\)\b\|){\($\|[^0-9]\)\|\b\(catch\|for\|if\|while\)("/>
+      <arg line="${style.args2}"/>
+      <arg line="--exclude=build.xml"/>
+    </exec>
+    <fail message="Missing space:${line.separator}${missingspace}">
+      <condition>
+	<not>
+	  <equals arg1="${missingspace}" arg2=""/>
+	</not>
+      </condition>
+    </fail>
+  </target>
+
+  <fileset id="formatted.java.files" dir="." includes="**/*.java" excludes="**/asmx/"/>
+
+  <condition property="isMac">
+    <os family="mac" />
+  </condition>
+
+  <!-- Avoids "Argument list too long" message.  You can also set
+       this property in file local.properties. -->
+  <condition property="maxparallel" value="1000" else="-1">
+    <isset property="isMac"/>
+  </condition>
+
+  <target name="-run-google-java-format.check">
+    <condition property="run-google-java-format.exists">
+      <available file=".run-google-java-format" type="dir"/>
+    </condition>
+  </target>
+
+  <target name="-get-run-google-java-format"
+          description="Obtain the run-google-java-format project"
+          depends="-run-google-java-format.check"
+          unless="run-google-java-format.exists">
+    <exec executable="git"
+          dir=".">
+      <arg value="clone"/>
+      <arg value="-q"/>
+      <arg value="https://github.com/plume-lib/run-google-java-format.git"/>
+      <arg value=".run-google-java-format"/>
+    </exec>
+  </target>
+
+  <target name="-update-run-google-java-format"
+          description="Update the run-google-java-format project"
+          depends="-get-run-google-java-format">
+    <exec executable="git"
+          dir=".run-google-java-format">
+      <arg value="pull"/>
+      <arg value="-q"/>
+    </exec>
+  </target>
+
+<!-- TEMPORARY: Do not run this until branches have been merged. -->
+  <target name="reformat" depends="-update-run-google-java-format"
+          description="Reformat Java code">
+    <apply executable="python" parallel="true" maxparallel="${maxparallel}" failonerror="true">
+      <arg value="./.run-google-java-format/run-google-java-format.py"/>
+      <fileset refid="formatted.java.files"/>
+    </apply>
+  </target>
+
+  <target name="check-format" depends="-update-run-google-java-format"
+          description="Check Java code formatting">
+    <apply executable="python" parallel="true" maxparallel="${maxparallel}"
+       failonerror="false" resultproperty="check.format.result"
+       outputproperty="check.format.stdout" errorproperty="check.format.stderr">
+      <arg value="./.run-google-java-format/check-google-java-format.py"/>
+      <fileset refid="formatted.java.files"/>
+    </apply>
+    <echo unless:blank="${check.format.stdout}">${check.format.stdout}</echo>
+    <echo unless:blank="${check.format.stderr}">${check.format.stderr}</echo>
+    <echo unless:blank="${check.format.stderr}">Fix syntax errors, then re-run:  ant check-format</echo>
+    <echo unless:blank="${check.format.stdout}" if:blank="${check.format.stderr}">Try running:  ant reformat</echo>
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="0" arg2="${check.format.result}"/>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+</project>
diff --git a/buildtest/TestWrapper.java b/buildtest/TestWrapper.java
new file mode 100644
index 0000000..eec90fd
--- /dev/null
+++ b/buildtest/TestWrapper.java
@@ -0,0 +1,64 @@
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+
+/**
+ * TestWrapper is a class that examines JUnit test results and outputs reports
+ *  only if one or more tests failed.
+ */
+public class TestWrapper {
+
+  /**
+   * Prints each file listed in args to standard out, only if it
+   * contained a failed JUnit test.
+   * @param args the file names of the test results to examine
+   */
+  public static void main(String[] args) {
+    for (String filename : args) {
+      try {
+        if (containsJUnitFailure(filename)) {
+          System.out.println();
+          System.out.println("Failed tests in: " + filename);
+          print(filename);
+        }
+      } catch (Exception e) {
+        System.out.println("Problem reading file " + filename);
+        e.printStackTrace(System.out);
+      }
+    }
+  }
+
+  /**
+   * Examines the given file and displays it if there are failed tests.
+   *
+   * @param filename the name of the file to examine.
+   */
+  private static boolean containsJUnitFailure(String filename) throws IOException {
+    BufferedReader in = new BufferedReader(new FileReader(filename));
+    String line = in.readLine();
+    while (line != null) {
+      if (line.contains("FAILED")) {
+        in.close();
+        return true;
+      }
+      line = in.readLine();
+    }
+    in.close();
+    return false;
+  }
+
+  /**
+   * Prints the specified file.
+   * @param filename the name of the file to print
+   * @throws Exception if an error occurs
+   */
+  private static void print(String filename) throws IOException {
+    BufferedReader in = new BufferedReader(new FileReader(filename));
+    String line = in.readLine();
+    while (line != null) {
+      System.out.println(line);
+      line = in.readLine();
+    }
+    in.close();
+  }
+}
diff --git a/buildtest/build.xml b/buildtest/build.xml
new file mode 100644
index 0000000..d3a3e90
--- /dev/null
+++ b/buildtest/build.xml
@@ -0,0 +1,127 @@
+<project name="buildtest" default="test-all">
+
+    <!--
+        Initialization target to setup the build directory.
+    -->
+    <target name="init">
+        <tstamp/>
+
+        <property name="builddir" location="build-current-${DSTAMP}" />
+        <mkdir dir="${builddir}"/>
+
+        <property name="asmx" location="${builddir}/annotations/asmx" />
+        <property name="scene-lib" location="${builddir}/annotations/scene-lib" />
+        <property name="annotations-compiler" location="${builddir}/jsr308-langtools/src/share/opensource/javac" />
+        <property name="annotations-disassembler" location="${builddir}/annotations/disassembler" />
+        <property name="annotator" location="${builddir}/annotations/annotator" />
+        <property name="annotated-jdk" location="${builddir}/qualifiers/annotated-jdk" />
+
+    </target>
+
+    <!--
+        Targets to checkout all the projects.
+    -->
+
+    <target name="checkout-jsr308-langtools" depends="init">
+        <exec dir="${builddir}" executable="hg" failonerror="true">
+            <arg value="clone"/>
+            <arg value="https://bitbucket.org/typetools/jsr308-langtools"/>
+            <arg value="jsr308-langtools"/>
+        </exec>
+    </target>
+
+    <target name="checkout-annotation-tools" depends="init">
+        <exec dir="${builddir}" executable="git" failonerror="true">
+            <arg value="clone"/>
+            <arg value="https://github.com/typetools/annotation-tools.git"/>
+            <arg value="annotation-tools"/>
+        </exec>
+    </target>
+
+    <target name="checkout-checker-framework" depends="init">
+        <exec dir="${builddir}" executable="git" failonerror="true">
+            <arg value="clone"/>
+            <arg value="https://github.com/typetools/checker-framework.git"/>
+            <arg value="checker-framework"/>
+        </exec>
+    </target>
+
+    <target name="checkout-all" depends="checkout-jsr308-langtools, checkout-annotation-tools, checkout-checker-framework"/>
+
+    <!--
+        Targets to build all the projects.
+    -->
+    <target name="build-asmx" depends="checkout-annotations">
+        <ant dir="${asmx}" inheritAll="false" target="bin"/>
+    </target>
+
+    <target name="build-scene-lib" depends="checkout-annotations">
+        <ant dir="${scene-lib}" inheritAll="false" target="bin"/>
+    </target>
+
+    <target name="build-annotations-compiler" depends="checkout-annotations">
+        <ant dir="${annotations-compiler}" inheritAll="false" target="build"/>
+    </target>
+
+    <target name="build-annotations-disassembler" depends="checkout-annotations">
+        <ant dir="${annotations-disassembler}" inheritAll="false" target="bin"/>
+    </target>
+
+    <target name="build-annotated-jdk" depends="checkout-qualifiers">
+        <ant dir="${annotated-jdk}" inheritAll="false" target="bin"/>
+    </target>
+
+    <target name="build-annotator" depends="checkout-annotations">
+        <ant dir="${annotator}" inheritAll="false" target="bin"/>
+    </target>
+    <target name="build-all" depends="build-asmx,
+        build-scene-lib, build-annotations-compiler,
+        build-annotated-jdk, build-annotator">
+    </target>
+
+    <!--
+        Targets to actually run the tests.
+    -->
+    <!-- Runs the tests on the extensions to ASM. -->
+    <target name="test-asmx" depends="build-asmx">
+        <ant dir="${asmx}" inheritAll="false" target="test">
+            <property name="test.group" value="conform/xannotation"/>
+        </ant>
+        <copy todir="./reports">
+            <fileset dir="${asmx}/output/test/reports" includes="**/*"/>
+        </copy>
+    </target>
+
+    <!-- Runs the tests on the annotation scene library, and also
+        on the classfile to index file and vice versa part of the
+        annotation file utilities. -->
+    <target name="test-scene-lib" depends="build-scene-lib">
+        <ant dir="${scene-lib}" inheritAll="false" target="test-scene-lib"/>
+        <ant dir="${scene-lib}" inheritAll="false" target="test-classfile"/>
+        <copy todir="./reports">
+            <fileset dir="${scene-lib}/reports" includes="**/*"/>
+        </copy>
+    </target>
+
+    <!-- Runs the tests on the annotator - the index file to source code tool
+        of the annotation file utilities. -->
+    <target name="test-annotator" depends="build-annotator">
+        <exec dir="${annotator}/tests" executable="make" failonerror="true"
+            output="./reports/test-annotator.result">
+            <arg value="all"/>
+        </exec>
+    </target>
+
+    <target name="test-all-annotations" depends="test-asmx, test-scene-lib, test-annotator"/>
+
+    <target name="test-all" depends="test-all-annotations"/>
+
+    <target name="help">
+        <echo message="Buildfile for the Annotation File Utilities"/>
+        <echo message="Targets: "/>
+        <echo message="checkout-all: Checkout all known projects from cvs or svn." />
+        <echo message="build-all: Build all projects." />
+        <echo message="test-all: Run all known tests." />
+    </target>
+
+</project>
diff --git a/buildtest/buildtest b/buildtest/buildtest
new file mode 100755
index 0000000..a72ad8e
--- /dev/null
+++ b/buildtest/buildtest
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Simple shell file that runs the regression tests and displays output only
+# if tests fail.  You can run the entire test yourself by executing this file:
+#   ./buildtest
+# This takes a long time to run.
+
+# Note: To only run tests on the annotations projects,
+# instead of executing this script, run:
+#     ant test-all-annotations
+# This will create a new directory, build-current-<today's date>, in the
+# present directory, and will checkout, build and test all the annotations
+# projects in that directory.  To run those tests locally, pass in your
+# working directory to Ant as follows:
+#     ant -Dbuilddir=$HOME/research/types test-all-annotations
+
+
+# Remove last night's results.
+rm -rf reports
+rm -rf build-current*
+mkdir reports
+
+ant checkout-all 2>&1 > checkout-all.log
+ant build-all 2>&1 > build-all.log
+
+# Each of the following four commands places the results in the reports folder,
+# The TestWrapper reads them and outputs any that have errors.
+ant test-asmx 2>&1 > test-asmx.log
+ant test-annotation-scene-lib 2>&1 > test-annotation-scene-lib.log
+ant test-annotator 2>&1 > test-annotator.log
+
+# TestWrapper examines all reports and only displays those files that contain
+# failing test cases.
+# This effectively makes cron send email, since cron sends email only if
+# the script produces output.
+javac TestWrapper.java
+java TestWrapper reports/*
+
+# The .log files aren't part of the tests, so don't put them in reports
+# until all the tests have been analyzed.
+mv *.log reports
diff --git a/buildtest/buildtest.cron b/buildtest/buildtest.cron
new file mode 100644
index 0000000..680642d
--- /dev/null
+++ b/buildtest/buildtest.cron
@@ -0,0 +1,23 @@
+# This is the crontab for the nightly build test for
+# Annotation File Utilities (scene library, asm extension, AFU proper).
+#
+# This file needs to be installed before changes will take effect.
+# You can install this crontab by running:
+#  crontab buildtest.cron
+
+USER=mernst
+MAILTO=mernst@cs.washington.edu
+AFS=/afs/csail.mit.edu/u/j/jaimeq/bin/afs-cron-wrapper
+# AFS is a wrapper to be able to execute the build script under
+# unix user jaimeq and afs user jaimeq.cron (which is a member the pag group).
+# Basically, all file access should be taken care of.
+
+
+# Update the actual buildtest files before they are run.
+# (The build files are tiny and this should be fast,
+#   but budget half an hour just to be safe.)
+30 0 * * * cd $HOME/research/annotations/buildtest; $AFS ./svn-update-and-log
+
+# Run the tests nightly at 1:00 am.
+0 1 * * * cd $HOME/research/annotations/buildtest; $AFS ./buildtest
+
diff --git a/buildtest/svn-update-and-log b/buildtest/svn-update-and-log
new file mode 100755
index 0000000..00ec395
--- /dev/null
+++ b/buildtest/svn-update-and-log
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# Script to perform svn update and write a log.  Due to file access rules,
+# the entire command cannot be run from within the crontab.
+
+svn update > svn-update.log
diff --git a/global.build.properties b/global.build.properties
new file mode 100644
index 0000000..c9d1577
--- /dev/null
+++ b/global.build.properties
@@ -0,0 +1,25 @@
+# Annotation File Utilities project
+# Global properties file that contains absolute paths to all known projects.
+
+# This file must contain absolute paths for each project.
+# Environment variables can be used, but must be enclosed in brackets
+# and prefixed with 'env.' as such: ${env.PATH}
+
+# ${workspace} contains the top-level checkouts.
+
+# If this property file is included from elsewhere, ${workspace} should
+# already be defined (and hopefully to an absolute pathname, such as by
+# starting with ${basedir}); in that case, this definition has no effect.
+workspace : ..
+
+annotation-tools : ${workspace}/annotation-tools
+afu : ${annotation-tools}/annotation-file-utilities
+asmx : ${annotation-tools}/asmx
+scene-lib : ${annotation-tools}/scene-lib
+
+# Compiler.  For annotations-compiler builds, use:   antfile="make/build.xml"
+annotations-compiler : ${workspace}/jsr308-langtools
+annotations-disassembler : ${workspace}/jsr308-langtools
+
+# Other tools
+checkerframework : ${workspace}/checker-framework
diff --git a/notes-openjdk-build.txt b/notes-openjdk-build.txt
new file mode 100644
index 0000000..5fc8106
--- /dev/null
+++ b/notes-openjdk-build.txt
@@ -0,0 +1,97 @@
+This file contains some notes about the process of building the full
+OpenJDK distribution in RedHat Enterprise Linux 4 (Oracle's recommended
+environment) in VMWare.  It also contains some of Matt's recommendations as
+to when and how OpenJDK builds should be made.
+
+===========================================================================
+
+Notes on OpenJDK build process:
+
+Sun's "official" build environment (per /README-builds.html#MBE in the
+source distribution) is RHEL 4.
+
+Obtain RHEL 4 via MIT - IS&T offers it to students and faculty via
+https://web.mit.edu/rhlinux/rhel-4.0.
+
+There are three varieties of RHEL 4 - AS, ES, and WS. I used WS, which I
+believe is for workstations (the others are intended for servers).  This
+requires downloading the WS disc 1 and the AS discs 2 - 4.
+
+A single disk of size 8.0 GB was created in VMWare; VMWare's option for
+using .iso files as optical drives was also used (i.e., didn't need to
+mount loopback on the host). In retrospect, perhaps a larger disk should be
+used, as only ~500 MB remains after the JDK build.
+
+Default options were chosen for RHEL setup (including automatic
+partitioning and default packages). In the future, SELinux should be
+disabled in the installer (it can also be done later, but this will save a
+step).
+
+It seems that RHEL 4 doesn't have yum for package management, and that the
+"up2date" command should be used instead. up2date doesn't seem to have some
+way to search packages, though, so I had to google for some of the package
+names below. Before using up2date, I registered with IS&T (not Red Hat!),
+which was basically just a certificate check and running a script that they
+provide.
+
+I acquired the following packages:
+- JDK 6 (Linux x86 RPM version; for bootstrapping) --
+   http://java.sun.com/javase/downloads/index.jsp
+- SVN -- "up2date subversion"
+- JDK 7 -- "svn checkout https://openjdk.dev.java.net/svn/openjdk/jdk/trunk
+ openjdk --username guest" (hit enter for blank password)
+- JDK 7 binary plugs -- http://download.java.net/openjdk/jdk7/
+- ALSA headers -- "up2date alsa-lib-devel"
+- CUPS headers -- "up2date cups-devel"
+
+I installed the JDK 6 package via "rpm -Uvh [package]", and then did
+"/usr/sbin/alternatives" (apparently the RedHat version of Debian's
+update-alternatives) to install "java" and "javac" links. JDK 7 was a
+simple checkout (though it took a while) and the binary plugs were a "java
+-jar [package]".
+
+
+I set the following environment variables (having checked out the JDK 7
+sources into my home directory):
+
+export ALT_BOOTDIR=/usr/java/jdk1.6.0_01
+export ALT_CLOSED_JDK_IMPORT_PATH=~/openjdk/binary-plugs/jdk1.7.0
+export LANG=C # to suppress a sanity check warning
+
+
+I then did the sanity check and build per the /README-build.html
+instructions. The sanity check took a second or two, and the full build
+took about 40 minutes.
+
+The /control/build/linux-i586 folder contains j2sdk-image, which seems
+suitable as a binary distribution. I don't think there's a way to create
+the Oracle version, however (the .bin file that, when executed, prompts you to
+accept their license and then extracts files).
+
+There also doesn't seem to be a way to extract out just the compiler
+sources. In binary form, the compiler is in j2sdk-image/lib/tools.jar.
+
+If "make" is run after a successful build (even if no changes have been
+made), it takes ~17 minutes to complete.
+
+===========================================================================
+
+Matt's Recommendations:
+
+The build procedure (in Linux, at least) should not prevent us from
+releasing a binary JSR 308 OpenJDK package. We may have to figure out how
+to produce the Oracle package (with the license prompt and executable format)
+if a .zip is not a sufficient means of distribution. We may need some
+additional tools and setup for building in Windows.
+
+However, the build procedure takes too long (at least 17 minutes per build,
+vs around 5 seconds for the current compiler-only setup) for doing
+day-to-day development in this environment. I propose continuing to work
+with the compiler the way we have it in SVN (which mirrors Oracle's
+compiler-only distribution) and creating two scripts: one for updating the
+OpenJDK checkout with our compiler modifications and one for updating our
+compiler repository with vendor OpenJDK changes. We'd only use the former
+when doing a JSR 308 release, and only use the latter when Oracle does a
+release. In this way, we don't have to make any serious modifications (only
+a couple of additions) to our repositories or build scripts and can keep
+small build times for daily development.
diff --git a/scene-lib/.classpath b/scene-lib/.classpath
new file mode 100644
index 0000000..626641f
--- /dev/null
+++ b/scene-lib/.classpath
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry combineaccessrules="false" kind="src" path="/jsr308-langtools"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+	<classpathentry kind="lib" path="junit.jar"/>
+	<classpathentry combineaccessrules="false" kind="src" path="/asmx"/>
+    	<classpathentry combineaccessrules="false" kind="src" path="/plume-lib"/>
+	<classpathentry kind="lib" path="/annotation-file-utilities/lib/guava-20.0.jar"/>
+	<classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/scene-lib/.project b/scene-lib/.project
new file mode 100644
index 0000000..e5f14c7
--- /dev/null
+++ b/scene-lib/.project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>scene-lib</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/scene-lib/.settings/org.eclipse.jdt.core.prefs b/scene-lib/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..ff099ff
--- /dev/null
+++ b/scene-lib/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,5 @@
+#Wed Jul 12 14:32:46 EDT 2006
+=\=\=\=\=\=\=
+<<<<<<<=.mine
+>>>>>>>=.r75
+eclipse.preferences.version=1
diff --git a/scene-lib/.settings/org.eclipse.jdt.ui.prefs b/scene-lib/.settings/org.eclipse.jdt.ui.prefs
new file mode 100644
index 0000000..1aefa64
--- /dev/null
+++ b/scene-lib/.settings/org.eclipse.jdt.ui.prefs
@@ -0,0 +1,4 @@
+#Wed Jul 12 14:32:46 EDT 2006
+eclipse.preferences.version=1
+formatter_settings_version=10
+org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8"?><templates/>
diff --git a/scene-lib/README b/scene-lib/README
new file mode 100644
index 0000000..64a3103
--- /dev/null
+++ b/scene-lib/README
@@ -0,0 +1,20 @@
+File overview.html in the current directory gives a very brief
+overview of this project.  It also appears in the "Overview"
+section of the Javadoc API documentation.
+
+
+Test cases are split up over two directories:
+
+src/annotations/tests/classfile/cases/
+test/annotations/tests/classfile/cases/
+
+See file test/annotations/tests/classfile/AnnotationsTest.java for
+some documentation of the testing framework.
+
+TODO: there are currently two "golden" class files: the un-annoted file
+and the expected output after inserting annotations.
+Additionally, we could compare:
+Unannotated .java --> javac --> .class --> AFU insert into .class --> .class
+Unannotated .java --> AFU insert into .java --> javac --> .class
+that is, compare the result of inserting the annotations directly
+into the bytecode versus inserting into the source code and compiling.
diff --git a/scene-lib/anncat b/scene-lib/anncat
new file mode 100755
index 0000000..f50c2f8
--- /dev/null
+++ b/scene-lib/anncat
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# Concatenates multiple descriptions of annotations into a single one.
+
+SCENE_LIB=$(dirname $0)
+WORKSPACE=$SCENE_LIB/..
+ASMX=$WORKSPACE/asmx
+
+JAVAC_JAR=${JAVAC_JAR:-${SCENE_LIB}/../../jsr308-langtools/dist/lib/javac.jar}
+
+export CLASSPATH=${JAVAC_JAR}:$SCENE_LIB/bin:$ASMX/bin:$WORKSPACE/annotation-file-utilities/lib/plume-core.jar:$CLASSPATH
+java annotations.tools.Anncat "$@"
diff --git a/scene-lib/ant-contrib.jar b/scene-lib/ant-contrib.jar
new file mode 100644
index 0000000..ea817cd
--- /dev/null
+++ b/scene-lib/ant-contrib.jar
Binary files differ
diff --git a/scene-lib/build.properties b/scene-lib/build.properties
new file mode 100644
index 0000000..cd49722
--- /dev/null
+++ b/scene-lib/build.properties
@@ -0,0 +1,3 @@
+workspace : ${basedir}/../..
+global.build.properties : ${basedir}/../global.build.properties
+user.build.properties : ${basedir}/../user.build.properties
diff --git a/scene-lib/build.xml b/scene-lib/build.xml
new file mode 100644
index 0000000..9b66b06
--- /dev/null
+++ b/scene-lib/build.xml
@@ -0,0 +1,350 @@
+<?xml version="1.0"?>
+<project name="scene-lib" default="bin">
+
+    <property environment="env"/>
+
+    <macrodef name="echo-fileset">
+        <attribute name="filesetref" />
+        <sequential>
+            <pathconvert pathsep="
+" property="@{filesetref}.echopath">
+                <path>
+                    <fileset refid="@{filesetref}" />
+                </path>
+            </pathconvert>
+            <echo>   ------- echoing fileset @{filesetref} -------</echo>
+            <echo>${@{filesetref}.echopath}</echo>
+        </sequential>
+    </macrodef>
+
+    <taskdef resource="net/sf/antcontrib/antcontrib.properties">
+      <classpath>
+        <pathelement location="ant-contrib.jar" />
+      </classpath>
+    </taskdef>
+
+    <target name="init-properties">
+        <condition property="exists.build.properties">
+            <available file="build.properties"/>
+        </condition>
+        <fail
+            unless="exists.build.properties"
+            message="Local build.properites file is missing."/>
+
+        <property file="build.properties"/>
+
+        <fail
+            unless="global.build.properties"
+            message="Local build.properties file did not define global buildfile in property global.build.properties"/>
+        <condition property="exists.global.build.properties">
+            <available file="${global.build.properties}"/>
+        </condition>
+        <fail
+            unless="exists.global.build.properties"
+        message="File ${global.build.properties} file not found."/>
+        <property file="${global.build.properties}"/>
+
+        <fail
+           unless="user.build.properties"
+            message="Local build.properties file did not define global buildfile in property user.build.properties"/>
+        <condition property="exists.user.build.properties">
+            <available file="${user.build.properties}"/>
+        </condition>
+        <fail
+            unless="exists.user.build.properties"
+            message="File ${user.build.properties} file not found."/>
+        <property file="${user.build.properties}"/>
+
+    </target>
+
+    <target name="init-dependencies"
+            depends="init-properties">
+        <!-- I should reinstate this after figuring out how to make
+        it remake only when necessary, not always.  (I should
+        probably do that outside ASM rather than modifying ASM itself?)
+        And, supply -Dproduct.noshrink to asmx ant command, at least when
+        testing.
+        -->
+        <!--
+        <ant dir="${asmx}" inheritAll="false" target="bin"/>
+        -->
+        <!-- Next 2 lines only for repository version. -->
+        <!--
+        <ant dir="${annotations-compiler}" antfile="make/build.xml" inheritAll="false" target="build"/>
+        <ant dir="${checkers}" inheritAll="false" target="build"/>
+        -->
+    </target>
+
+    <target name="init-paths">
+        <path id="sourcepath">
+            <pathelement location="src"/>
+        </path>
+        <path id="testpath">
+            <pathelement location="test/annotations/tests/executable"/>
+            <pathelement location="test/annotations/tests/classfile/foo"/>
+        </path>
+
+        <fileset dir="." id="source.files.java">
+            <include name="src/**/*.java"/>
+            <exclude name="**/.svn"/>
+            <exclude name="**/package-info.java"/>
+        </fileset>
+
+        <fileset dir="." id="source.files.java.nopackageinfo">
+            <include name="src/**/*.java"/>
+            <exclude name="**/.svn"/>
+            <exclude name="**/package-info.java"/>
+        </fileset>
+
+        <fileset dir="." id="source.files.java.packageinfo">
+            <include name="**/package-info.java"/>
+        </fileset>
+
+        <fileset dir="." id="source.files.non-java">
+            <include name="src/**/*"/>
+            <exclude name="**/.svn"/>
+            <exclude name="**/*.java"/>
+        </fileset>
+
+        <path id="javadoc-sourcepath">
+            <pathelement location="src"/>
+        </path>
+
+        <path id="libpath">
+            <pathelement location="${asmx}/bin"/>
+            <pathelement location="${junit}"/>
+            <pathelement location="${annotation-tools}/annotation-file-utilities/lib/plume-core.jar"/>
+            <pathelement location="${annotation-tools}/annotation-file-utilities/lib/guava-20.0.jar"/>
+            <!-- remainder only for repository version -->
+            <pathelement location="${annotations-compiler}/dist/lib/javac.jar"/>
+            <pathelement location="${annotations-compiler}/dist/lib/javap.jar"/>
+            <pathelement location="bin"/>
+        </path>
+    </target>
+
+    <target name="init" depends="init-properties, init-dependencies, init-paths"/>
+
+    <target name="bin-clean">
+        <delete dir="bin"/>
+    </target>
+
+    <target name="bin-check-uptodate" depends="init">
+        <uptodate property="source.files.non-java.uptodate">
+            <srcfiles refid="source.files.non-java"/>
+            <mapper type="glob" from="src/*" to="bin/*"/>
+        </uptodate>
+
+        <uptodate property="source.files.java.nopackageinfo.uptodate">
+            <srcfiles refid="source.files.java.nopackageinfo"/>
+            <mapper type="glob" from="src/*.java" to="bin/*.class"/>
+        </uptodate>
+
+        <!-- I want to say that package-info.java does not force
+        recompilation if it is older than all source files in its own
+        package. -->
+        <uptodate property="source.files.java.packageinfo.uptodate" targetfile="bin">
+            <srcfiles refid="source.files.java.packageinfo"/>
+        </uptodate>
+
+        <condition property="bin.uptodate">
+          <and>
+            <isset property="source.files.non-java.uptodate"/>
+            <isset property="source.files.java.nopackageinfo.uptodate"/>
+            <isset property="source.files.java.packageinfo.uptodate"/>
+          </and>
+        </condition>
+
+        <!-- These print "true" if set and the property name in curly braces, such as "${source.files.java.nopackageinfo.uptodate}", if not set. -->
+        <echo message="source.files.non-java.uptodate: ${source.files.non-java.uptodate}"/>
+        <echo message="source.files.java.nopackageinfo.uptodate: ${source.files.java.nopackageinfo.uptodate}"/>
+        <echo message="source.files.java.packageinfo.uptodate: ${source.files.java.packageinfo.uptodate}"/>
+        <echo message="bin.uptodate: ${bin.uptodate}"/>
+    </target>
+
+    <target name="bin" depends="init, bin-check-uptodate" unless="bin.uptodate">
+        <echo message="Running bin"/>
+        <mkdir dir="bin"/>
+        <!-- Copy non-java files to bin.  These are mostly .jaif files. -->
+        <copy todir="bin">
+            <fileset dir="src" excludes="**/*.java"/>
+            <fileset dir="test" excludes="**/*.java"/>
+        </copy>
+        <javac
+               destdir="bin"
+               debug="true"
+               classpathref="libpath"
+               classpath="${libpath}"
+               includeantruntime="false"
+               fork="true"
+               executable="${annotations-compiler}/dist/bin/javac">
+            <src refid="sourcepath"/>
+            <src refid="testpath"/>
+            <!-- To prevent a cyclic dependency with the Checker
+                 Framework, ignore type annotations in comments here.
+                 A separate target could be added to check the qualifiers
+                 and have them in the generated code. -->
+            <compilerarg value="-XDTA:noannotationsincomments"/>
+            <compilerarg value="-Xlint:-options"/>
+            <compilerarg value="-Werror"/>
+            <compilerarg value="-version"/>
+            <!-- Make sure we only have Java 7 source code and generate Java 7 bytecode. -->
+            <compilerarg value="-source"/>
+            <compilerarg value="7"/>
+            <compilerarg value="-target"/>
+            <compilerarg value="7"/>
+            <classpath refid="libpath"/>
+            <!-- TODO: how can we include just these two files in testpath? -->
+            <compilerarg value="test/annotations/tests/classfile/AnnotationsTest.java"/>
+            <compilerarg value="test/annotations/tests/classfile/AnnotationVerifier.java"/>
+        </javac>
+<!--
+       <pathconvert property="libpath" refid="libpath"/>
+       <pathconvert property="source.files.java.spaceseparated" refid="source.files.java" pathsep=" "/>
+        <exec executable="javac" failonerror="true">
+          <arg value="-version"/>
+          <arg value="-d"/>
+          <arg value="bin"/>
+          <arg value="-g"/>
+          <arg value="-cp"/>
+          <arg value="${libpath}"/>
+          <arg line="source.files.java.spaceseparated"/>
+        </exec>
+-->
+    </target>
+
+    <target name="test-scene-lib" depends="init, bin">
+        <mkdir dir="reports"/>
+        <junit printsummary="withOutAndErr" showoutput="true" fork="yes" dir="." haltonerror="yes" haltonfailure="yes">
+            <classpath refid="libpath"/>
+            <formatter type="plain"/>
+            <test name="annotations.tests.executable.TestSceneLib" todir="reports"/>
+            <assertions>
+              <enable/>
+            </assertions>
+        </junit>
+    </target>
+
+    <target name="test-classfile" depends="init, bin">
+        <mkdir dir="reports"/>
+        <junit printsummary="withOutAndErr" showoutput="true" fork="yes" dir="." haltonerror="yes" haltonfailure="yes">
+            <classpath refid="libpath"/>
+            <formatter type="plain"/>
+            <test name="annotations.tests.classfile.AnnotationsTest" todir="reports"/>
+            <assertions>
+              <enable/>
+            </assertions>
+        </junit>
+    </target>
+
+    <target name="test-example" depends="init, bin">
+        <!-- Working directory is ignored when same JVM is used.  That means
+             that the relative path for ${scene-lib} works only if this target
+             is invoked from the same directory as the build.xml file appears
+             in.  We can fix the java task by adding this:
+              fork="true"
+              dir="${scene-lib}/src/annotations/tests"
+             but there are other uses of ${scene-lib} in this target.
+        -->
+        <java classname="annotations.tests.executable.Example"
+              output="${scene-lib}/test/annotations/tests/executable/example-stdout.jaif"
+              classpathref="libpath">
+            <arg value="${scene-lib}/test/annotations/tests/executable/example-input.jaif" />
+            <arg value="foo.Bar" />
+            <arg value="${scene-lib}/test/annotations/tests/executable/example-output.jaif" />
+        </java>
+        <condition property="example.output.matches">
+            <filesmatch file1="${scene-lib}/test/annotations/tests/executable/example-output.jaif.goal"
+                        file2="${scene-lib}/test/annotations/tests/executable/example-output.jaif"/>
+        </condition>
+        <!-- Debugging output in case I don't have access to the filesystem. -->
+        <if>
+            <isfalse value="${example.output.matches}"/>
+            <then>
+                <exec executable="cat">
+                  <arg value="${scene-lib}/test/annotations/tests/executable/example-output.jaif.goal"/>
+                  <arg value="${scene-lib}/test/annotations/tests/executable/example-output.jaif"/>
+                </exec>
+            </then>
+        </if>
+        <fail unless="example.output.matches"
+            message="In ${scene-lib}/test/annotations/tests/executable/, file example-output.jaif does not match goal."/>
+        <condition property="example.stdout.matches">
+            <filesmatch file1="${scene-lib}/test/annotations/tests/executable/example-stdout.jaif.goal"
+                        file2="${scene-lib}/test/annotations/tests/executable/example-stdout.jaif"/>
+        </condition>
+        <fail unless="example.stdout.matches"
+            message="In ${scene-lib}/test/annotations/tests/executable/, file example-stdout.jaif does not match goal."/>
+
+    </target>
+
+    <target name="test-clean">
+        <delete dir="reports"/>
+    </target>
+
+    <target name="test" depends="test-scene-lib, test-classfile, test-example"
+       description="Run tests"/>
+    <target name="clean" depends="bin-clean, javadoc-clean, test-clean"
+       description="Remove generated files"/>
+
+    <target name="javadoc-clean">
+        <delete dir="javadoc"/>
+    </target>
+
+    <target name="javadoc" depends="javadoc-clean, init"
+       description="Generate Javadoc API documentation">
+        <javadoc sourcepathref="javadoc-sourcepath"
+                classpathref="libpath"
+                packagenames="*"
+                excludepackagenames=""
+                Overview="overview.html"
+                destdir="javadoc"
+                access="public"
+                noqualifier="annotations:annotations.el:annotations.field:annotations.io:annotations.io.classfile:annotations.util:annotations.util.coll:java.lang"
+		failonerror="true"
+                />
+    </target>
+
+    <target name="test-package" depends="bin">
+        <property name="test-package" value="scene-lib-test"/>
+        <mkdir dir="${test-package}"/>
+        <mkdir dir="${test-package}/src"/>
+        <copy todir="${test-package}/src">
+            <fileset dir="src" excludes="**/.svn"/>
+        </copy>
+        <jar destfile="${test-package}/deps.jar">
+            <fileset dir="${asmx}/bin" includes="org/**"/>
+        </jar>
+        <zip destfile="${test-package}.zip">
+            <zipfileset dir="${test-package}" prefix="${test-package}"/>
+        </zip>
+        <delete dir="${test-package}"/>
+    </target>
+
+    <!-- = = = = = = = = = = = = = = = = =
+         macrodef: echopath
+         Use as:    <echopath pathid="mypath"/>
+         = = = = = = = = = = = = = = = = = -->
+    <macrodef name="echopath">
+      <attribute name="pathid"/>
+      <sequential>
+        <property name="line.pathprefix" value="| |-- "/>
+        <!-- get given path in a printable form -->
+        <pathconvert pathsep="${line.separator}${line.pathprefix}"
+             property="echo.@{pathid}"
+             refid="@{pathid}">
+        </pathconvert>
+        <echo>Path @{pathid}</echo>
+        <echo>${line.pathprefix}${echo.@{pathid}}</echo>
+      </sequential>
+    </macrodef>
+
+  <target name="etags" depends="tags">
+  </target>
+  <target name="tags" description="Create Emacs TAGS table">
+    <exec executable="/bin/sh" failonerror="true">
+      <arg value="-c"/>
+      <arg value="etags `find -name '*.java' | sort-directory-order`"/>
+    </exec>
+  </target>
+
+</project>
diff --git a/scene-lib/junit.jar b/scene-lib/junit.jar
new file mode 100644
index 0000000..00770a4
--- /dev/null
+++ b/scene-lib/junit.jar
Binary files differ
diff --git a/scene-lib/overview.html b/scene-lib/overview.html
new file mode 100644
index 0000000..9c802b9
--- /dev/null
+++ b/scene-lib/overview.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+  <title>Annotation Scene Library overview</title>
+</head>
+
+<!-- Everything between <body> and </body> ends up in file overview-summary.html in the Javadoc. -->
+
+<body>
+The Annotation Scene Library provides classes to represent the annotations on a
+Java program and read and write those annotations in various formats.
+
+<h2>Structure</h2>
+
+<ul>
+<li>An {@link annotations.el.AScene} holds annotations for a set of classes
+and packages.
+</li>
+<li>A {@link annotations.el.AElement} represents one particular element of a
+Java program within an <code>AScene</code>.
+</li>
+<li>Package {@link annotations.io} provides routines to read and write
+{@link annotations.el.AScene}s in various formats.
+</li>
+<li>An {@link annotations.Annotation} represents an annotation (which might be a
+field of another annotation).  It can be attached to an {@link annotations.el.AElement}.
+</li>
+<li>An {@link annotations.el.AnnotationDef} represents an annotation definition,
+consisting of a definition name and field names and types
+({@link annotations.field.AnnotationFieldType}s).  It also indicates the
+annotation's retention policy.
+</li>
+</ul>
+
+<h2>Example</h2>
+
+<p>
+The example program <code>annotations.tests.Example</code> demonstrates the
+library's annotation-processing capabilities.  Its source code (and also
+example input and output) are distributed with the Annotation Scene Library.
+</p>
+
+</body>
+</html>
diff --git a/scene-lib/src/annotations/Annotation.java b/scene-lib/src/annotations/Annotation.java
new file mode 100644
index 0000000..acbf54c
--- /dev/null
+++ b/scene-lib/src/annotations/Annotation.java
@@ -0,0 +1,308 @@
+package annotations;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import annotations.el.AnnotationDef;
+import annotations.field.AnnotationFieldType;
+
+import java.util.*;
+import java.lang.reflect.*;
+
+
+/**
+ * A very simple annotation representation constructed with a map of field names
+ * to values. See the rules for values on {@link Annotation#getFieldValue};
+ * furthermore, subannotations must be {@link Annotation}s.
+ * {@link Annotation}s are immutable.
+ *
+ * <p>
+ * {@link Annotation}s can be constructed directly or through
+ * {@link AnnotationFactory#saf}. Either way works, but if you construct
+ * one directly, you must provide a matching {@link AnnotationDef} yourself.
+ */
+public final class Annotation {
+
+    /**
+     * The annotation definition.
+     */
+    public final AnnotationDef def;
+
+    /**
+     * An unmodifiable copy of the passed map of field values.
+     */
+    public final Map<String, Object> fieldValues;
+
+    /** Check the representation, throw assertion failure if it is violated. */
+    public void checkRep() {
+        assert fieldValues != null;
+        assert fieldValues.keySet() != null;
+        assert def != null;
+        assert def.fieldTypes != null;
+        assert def.fieldTypes.keySet() != null;
+        if (! fieldValues.keySet().equals(def.fieldTypes.keySet())) {
+            for (String s : fieldValues.keySet()) {
+                assert def.fieldTypes.containsKey(s)
+                    : String.format("Annotation contains field %s but AnnotationDef does not%n  annotation: %s%n  def: %s%n", s, this, this.def);
+            }
+            // TODO: Faulty assertions, fails when default value is used
+//            for (String s : def.fieldTypes.keySet()) {
+//                assert fieldValues.containsKey(s)
+//                    : String.format("AnnotationDef contains field %s but Annotation does not", s);
+//            }
+//            assert false : "This can't happen.";
+        }
+
+        for (String fieldname : fieldValues.keySet()) {
+            AnnotationFieldType aft = def.fieldTypes.get(fieldname);
+            Object value = fieldValues.get(fieldname);
+            String valueString;
+            String classString = value.getClass().toString();
+            if (value instanceof Object[]) {
+                Object[] arr = (Object[]) value;
+                valueString = Arrays.toString(arr);
+                classString += " {";
+                for (Object elt : arr) {
+                    classString += " " + elt.getClass();
+                }
+                classString += "}";
+            } else if (value instanceof Collection) {
+                Collection<?> coll = (Collection<?>) value;
+                valueString = Arrays.toString(coll.toArray());
+                classString += " {";
+                for (Object elt : coll) {
+                    classString += " " + elt.getClass();
+                }
+                classString += " }";
+            } else {
+                valueString = value.toString();
+                // No need to modify valueString.
+            }
+            assert aft.isValidValue(value)
+                : String.format("Bad field value%n  %s (%s)%nfor field%n  %s (%s)%nin annotation%n  %s",
+                                valueString, classString, aft, aft.getClass(), def);
+        }
+    }
+
+    // TODO make sure the field values are valid?
+    /**
+     * Constructs a {@link Annotation} with the given definition and
+     * field values.  Make sure that the field values obey the rules given on
+     * {@link Annotation#getFieldValue} and that subannotations are also
+     * {@link Annotation}s; this constructor does not validate the
+     * values.
+     */
+    public Annotation(AnnotationDef def,
+            Map<String, ? extends Object> fields) {
+        this.def = def;
+        this.fieldValues = Collections.unmodifiableMap(
+                new LinkedHashMap<String, Object>(fields));
+        checkRep();
+    }
+
+    /** Use adefs to look up (or insert into it) missing AnnotationDefs. */
+    public Annotation(java.lang.annotation.Annotation ja, Map<String, AnnotationDef> adefs) {
+        Class<? extends java.lang.annotation.Annotation> jaType = ja.annotationType();
+        String name = jaType.getName();
+        if (adefs.containsKey(name)) {
+            def = adefs.get(name);
+        } else {
+            def = AnnotationDef.fromClass(jaType, adefs);
+            adefs.put(name, def);
+        }
+        fieldValues = new LinkedHashMap<String,Object>();
+        try {
+            for (String fieldname : def.fieldTypes.keySet()) {
+                AnnotationFieldType aft = def.fieldTypes.get(fieldname);
+                Method m = jaType.getDeclaredMethod(fieldname);
+                Object val = m.invoke(ja);
+                if (! aft.isValidValue(val)) {
+                    if (val instanceof Class[]) {
+                        Class<?>[] vala = (Class[]) val;
+                        List<Class<?>> vall = new ArrayList<Class<?>>(vala.length);
+                        for (Class<?> elt : vala) {
+                            vall.add(elt);
+                        }
+                        val = vall;
+                    } else if (val instanceof Object[]) {
+                        Object[] vala = (Object[]) val;
+                        List<Object> vall = new ArrayList<Object>(vala.length);
+                        for (Object elt : vala) {
+                            vall.add(elt.toString());
+                        }
+                        val = vall;
+                    } else {
+                        val = val.toString();
+                    }
+                }
+                assert aft.isValidValue(val)
+                    : String.format("invalid value \"%s\" for field \"%s\" of class \"%s\" and expected type \"%s\"; ja=%s", val, val.getClass(), fieldname, aft, ja);
+                fieldValues.put(fieldname, val);
+            }
+        } catch (NoSuchMethodException e) {
+            throw new Error(String.format("no such method (annotation field) in %s%n  from: %s %s", jaType, ja, adefs), e);
+        } catch (InvocationTargetException e) {
+            throw new Error(e);
+        } catch (IllegalAccessException e) {
+            throw new Error(e);
+        }
+        checkRep();
+    }
+
+    /**
+     * Returns the value of the field whose name is given.
+     *
+     * <p>
+     * Everywhere in the annotation scene library, field values are to be
+     * represented as follows:
+     *
+     * <ul>
+     * <li>Primitive value: wrapper object, such as {@link Integer}.
+     * <li>{@link String}: {@link String}.
+     * <li>Class token: name of the type as a {@link String}, using the source
+     * code notation <code>int[]</code> for arrays.
+     * <li>Enumeration constant: name of the constant as a {@link String}.
+     * <li>Subannotation: <code>Annotation</code> object.
+     * <li>Array: {@link List} of elements in the formats defined here.  If
+     * the element type is unknown (see
+     * {@link AnnotationBuilder#addEmptyArrayField}), the array must have zero
+     * elements.
+     * </ul>
+     */
+    public Object getFieldValue(String fieldName) {
+        return fieldValues.get(fieldName);
+    }
+
+    /**
+     * Returns the definition of the annotation type to which this annotation
+     * belongs.
+     */
+    public final AnnotationDef def() {
+        return def;
+    }
+
+    /**
+     * This {@link Annotation} equals <code>o</code> if and only if
+     * <code>o</code> is a nonnull {@link Annotation} and <code>this</code> and
+     * <code>o</code> have recursively equal definitions and field values,
+     * even if they were created by different {@link AnnotationFactory}s.
+     */
+    @Override
+    public final boolean equals(Object o) {
+        return o instanceof Annotation && equals((Annotation) o);
+    }
+
+    /**
+     * Returns whether this annotation equals <code>o</code>; a slightly faster
+     * variant of {@link #equals(Object)} for when the argument is statically
+     * known to be another nonnull {@link Annotation}. Subclasses may wish to
+     * override this with a hard-coded "&amp;&amp;" of field comparisons to improve
+     * performance.
+     */
+    public boolean equals(Annotation o) {
+        return def.equals(o.def())
+            && fieldValues.equals(o.fieldValues);
+    }
+
+    /**
+     * Returns the hash code of this annotation as defined on
+     * {@link Annotation#hashCode}.  Subclasses may wish to override
+     * this with a hard-coded XOR/addition of fields to improve performance.
+     */
+    @Override
+    public int hashCode() {
+        return def.hashCode() + fieldValues.hashCode();
+    }
+
+    /**
+     * Returns a string representation of this for
+     * debugging purposes.  For now, this method relies on
+     * {@link AbstractMap#toString} and the {@link Object#toString toString}
+     * methods of the field values, so the representation is only a first
+     * approximation to how the annotation would appear in source code.
+     */
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("@");
+        sb.append(def.name);
+        if (!fieldValues.isEmpty()) {
+            sb.append('(');
+            sb.append(fieldValues.toString());
+            sb.append(')');
+        }
+        return sb.toString();
+    }
+
+}
+
+// package annotations;
+//
+// import org.checkerframework.checker.nullness.qual.Nullable;
+//
+// import annotations.el.*;
+// import annotations.util.coll.Keyer;
+//
+// /**
+//  * A top-level annotation containing an ordinary annotation plus a retention
+//  * policy.  These are attached to {@link AElement}s.
+//  */
+// public final class Annotation {
+//     public static final Keyer<String, Annotation> nameKeyer
+//         = new Keyer<String, Annotation>() {
+//         public String getKeyFor(
+//                 Annotation v) {
+//             return v.tldef.name;
+//         }
+//     };
+//
+//     /**
+//      * The annotation definition.
+//      */
+//     public final AnnotationDef tldef;
+//
+//     /**
+//      * The ordinary annotation, which contains the data and the ordinary
+//      * definition.
+//      */
+//     public final Annotation ann;
+//
+//     /**
+//      * Wraps the given annotation in a top-level annotation using the given
+//      * top-level annotation definition, which provides a retention policy.
+//      */
+//     public Annotation(AnnotationDef tldef, Annotation ann) {
+//         if (!ann.def().equals(tldef))
+//             throw new IllegalArgumentException("Definitions mismatch");
+//         this.tldef = tldef;
+//         this.ann = ann;
+//     }
+//
+//     /**
+//      * Wraps the given annotation in a top-level annotation with the given
+//      * retention policy, generating the top-level annotation definition
+//      * automatically for convenience.
+//      */
+//     public Annotation(Annotation ann1,
+//             RetentionPolicy retention) {
+//         this(new AnnotationDef(ann1.def(), retention), ann1);
+//     }
+//
+//     /**
+//      * {@inheritDoc}
+//      */
+//     @Override
+//     public int hashCode() {
+//         return tldef.hashCode() + ann.hashCode();
+//     }
+//
+//     @Override
+//     public String toString() {
+//       StringBuilder sb = new StringBuilder();
+//       sb.append("tla: ");
+//       sb.append(tldef.retention);
+//       sb.append(":");
+//       sb.append(ann.toString());
+//       return sb.toString();
+//     }
+// }
diff --git a/scene-lib/src/annotations/AnnotationBuilder.java b/scene-lib/src/annotations/AnnotationBuilder.java
new file mode 100644
index 0000000..e4892bc
--- /dev/null
+++ b/scene-lib/src/annotations/AnnotationBuilder.java
@@ -0,0 +1,235 @@
+package annotations;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.*;
+
+import annotations.field.*;
+import annotations.el.AnnotationDef;
+
+/**
+ * An {@link AnnotationBuilder} builds a single annotation object after the
+ * annotation's fields have been supplied one by one.
+ *
+ * <p>
+ * It is not possible to specify the type name or the retention policy.
+ * Either the {@link AnnotationBuilder} expects a certain definition (and
+ * may throw exceptions if the fields deviate from it) or it determines the
+ * definition automatically from the supplied fields.
+ *
+ * <p>
+ * Each {@link AnnotationBuilder} is mutable and single-use; the purpose of an
+ * {@link AnnotationFactory} is to produce as many {@link AnnotationBuilder}s
+ * as needed.
+ */
+public class AnnotationBuilder {
+
+    // Sometimes, we build the AnnotationDef at the very end, and sometimes
+    // we have it before starting.
+    AnnotationDef def;
+
+    private String typeName;
+    Set<Annotation> tlAnnotationsHere;
+
+    boolean arrayInProgress = false;
+
+    boolean active = true;
+
+    // Generally, don't use this.  Use method fieldTypes() instead.
+    private Map<String, AnnotationFieldType> fieldTypes =
+        new LinkedHashMap<String, AnnotationFieldType>();
+
+    Map<String, Object> fieldValues =
+        new LinkedHashMap<String, Object>();
+
+    public String typeName() {
+        if (def != null) {
+            return def.name;
+        } else {
+            return typeName;
+        }
+    }
+
+    public Map<String, AnnotationFieldType> fieldTypes() {
+        if (def != null) {
+            return def.fieldTypes;
+        } else {
+            return fieldTypes;
+        }
+    }
+
+    class SimpleArrayBuilder implements ArrayBuilder {
+        boolean abActive = true;
+
+        String fieldName;
+        AnnotationFieldType aft; // the type for the elements
+
+        List<Object> arrayElements =
+            new ArrayList<Object>();
+
+        SimpleArrayBuilder(String fieldName, AnnotationFieldType aft) {
+            assert aft != null;
+            assert fieldName != null;
+            this.fieldName = fieldName;
+            this.aft = aft;
+        }
+
+        public void appendElement(Object x) {
+            if (!abActive) {
+                throw new IllegalStateException("Array is finished");
+            }
+            if (!aft.isValidValue(x)) {
+                throw new IllegalArgumentException(String.format("Bad array element value%n  %s (%s)%nfor field %s%n  %s (%s)",
+                                                                 x, x.getClass(), fieldName, aft, aft.getClass()));
+            }
+            arrayElements.add(x);
+        }
+
+        public void finish() {
+            if (!abActive) {
+                throw new IllegalStateException("Array is finished");
+            }
+            fieldValues.put(fieldName, Collections
+                            .<Object>unmodifiableList(arrayElements));
+            arrayInProgress = false;
+            abActive = false;
+        }
+    }
+
+    private void checkAddField(String fieldName) {
+        if (!active) {
+            throw new IllegalStateException("Already finished");
+        }
+        if (arrayInProgress) {
+            throw new IllegalStateException("Array in progress");
+        }
+        if (fieldValues.containsKey(fieldName)) {
+            throw new IllegalArgumentException("Duplicate field \'"
+                                               + fieldName + "\' in " + fieldValues);
+        }
+    }
+
+    /**
+     * Supplies a scalar field of the given name, type, and value for inclusion
+     * in the annotation returned by {@link #finish}. See the rules for values
+     * on {@link Annotation#getFieldValue}.
+     *
+     * <p>
+     * Each field may be supplied only once. This method may throw an exception
+     * if the {@link AnnotationBuilder} expects a certain definition for
+     * the built annotation and the given field does not exist in that
+     * definition or has the wrong type.
+     */
+    public void addScalarField(String fieldName, ScalarAFT aft, Object x) {
+        checkAddField(fieldName);
+        if (x instanceof Annotation && !(x instanceof Annotation)) {
+            throw new IllegalArgumentException("All subannotations must be Annotations");
+        }
+        if (def == null) {
+            fieldTypes.put(fieldName, aft);
+        }
+        fieldValues.put(fieldName, x);
+    }
+
+    /**
+     * Begins supplying an array field of the given name and type. The elements
+     * of the array must be passed to the returned {@link ArrayBuilder} in
+     * order, and the {@link ArrayBuilder} must be finished before any other
+     * methods on this {@link AnnotationBuilder} are called.
+     * <code>aft.{@link ArrayAFT#elementType elementType}</code> must be known
+     * (not <code>null</code>).
+     *
+     * <p>
+     * Each field may be supplied only once. This method may throw an exception
+     * if the {@link AnnotationBuilder} expects a certain definition for
+     * the built annotation and the given field does not exist in that
+     * definition or has the wrong type.
+     */
+    public ArrayBuilder beginArrayField(String fieldName, ArrayAFT aft) {
+        checkAddField(fieldName);
+        if (def == null) {
+            fieldTypes.put(fieldName, aft);
+        } else {
+            aft = (ArrayAFT) fieldTypes().get(fieldName);
+            if (aft == null) {
+                throw new Error(String.format("Definition for %s lacks field %s:%n  %s",
+                                              def.name, fieldName, def));
+            }
+            assert aft != null;
+        }
+        arrayInProgress = true;
+        assert aft.elementType != null;
+        return new SimpleArrayBuilder(fieldName, aft.elementType);
+    }
+
+    /**
+     * Supplies an zero-element array field whose element type is unknown.  The
+     * field type of this array is represented by an {@link ArrayAFT} with
+     * {@link ArrayAFT#elementType elementType} == <code>null</code>.
+     *
+     * <p>
+     * This can sometimes happen due to a design flaw in the format of
+     * annotations in class files.  An array value does not specify an type
+     * itself; instead, each element carries a type.  Thus, a zero-length array
+     * carries no indication of its element type.
+     */
+    public void addEmptyArrayField(String fieldName) {
+        checkAddField(fieldName);
+        if (def == null) {
+            fieldTypes.put(fieldName, new ArrayAFT(null));
+        }
+        fieldValues.put(fieldName, Collections.emptyList());
+    }
+
+    /**
+     * Returns the completed annotation. This method may throw an exception if
+     * the {@link AnnotationBuilder} expects a certain definition for the
+     * built annotation and one or more fields in that definition were not
+     * supplied.  Once this method has been called, no more method calls may be
+     * made on this {@link AnnotationBuilder}.
+     */
+    public Annotation finish() {
+        if (!active) {
+            throw new IllegalStateException("Already finished: " + this);
+        }
+        if (arrayInProgress) {
+            throw new IllegalStateException("Array in progress: " + this);
+        }
+        active = false;
+        if (def == null) {
+            assert fieldTypes != null;
+            def = new AnnotationDef(typeName, tlAnnotationsHere, fieldTypes);
+        } else {
+            assert typeName == null;
+            assert fieldTypes.isEmpty();
+        }
+        return new Annotation(def, fieldValues);
+    }
+
+    AnnotationBuilder(AnnotationDef def) {
+        assert def != null;
+        this.def = def;
+    }
+
+    AnnotationBuilder(String typeName) {
+        assert typeName != null;
+        this.typeName = typeName;
+    }
+
+    AnnotationBuilder(String typeName, Set<Annotation> tlAnnotationsHere) {
+        assert typeName != null;
+        this.typeName = typeName;
+        this.tlAnnotationsHere = tlAnnotationsHere;
+    }
+
+    public String toString() {
+        if (def != null) {
+            return String.format("AnnotationBuilder %s", def);
+        } else {
+            return String.format("(AnnotationBuilder %s : %s)", typeName, tlAnnotationsHere);
+        }
+    }
+
+}
diff --git a/scene-lib/src/annotations/AnnotationFactory.java b/scene-lib/src/annotations/AnnotationFactory.java
new file mode 100644
index 0000000..e4b5eda
--- /dev/null
+++ b/scene-lib/src/annotations/AnnotationFactory.java
@@ -0,0 +1,52 @@
+package annotations;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.Map;
+import java.util.Set;
+
+import annotations.el.AnnotationDef;
+
+/**
+ * A very simple {@link annotations.AnnotationFactory AnnotationFactory} that
+ * creates {@link Annotation}s. It is interested in all annotations and
+ * determines their definitions automatically from the fields supplied. Use the
+ * singleton {@link #saf}.
+ */
+public final class AnnotationFactory {
+    private AnnotationFactory() {
+    }
+
+    /**
+     * The singleton {@link AnnotationFactory}.
+     */
+    public static final AnnotationFactory saf = new AnnotationFactory();
+
+    /**
+     * Returns an {@link AnnotationBuilder} appropriate for building a
+     * {@link Annotation} of the given type name.
+     */
+    public AnnotationBuilder beginAnnotation(AnnotationDef def) {
+        return new AnnotationBuilder(def);
+    }
+
+    /**
+     * Returns an {@link AnnotationBuilder}.
+     * Tries to look up the AnnotationDef in adefs; if not found, inserts in adefs.
+     */
+    public AnnotationBuilder beginAnnotation(java.lang.annotation.Annotation a, Map<String, AnnotationDef> adefs) {
+        AnnotationDef def = AnnotationDef.fromClass(a.getClass(), adefs);
+        return new AnnotationBuilder(def);
+    }
+
+    /**
+     * Returns an {@link AnnotationBuilder} appropriate for building a
+     * {@link Annotation} of the given type name.
+     */
+    public AnnotationBuilder beginAnnotation(String typeName, Set<Annotation> tlAnnotationsHere) {
+        assert typeName != null;
+        return new AnnotationBuilder(typeName, tlAnnotationsHere);
+    }
+}
diff --git a/scene-lib/src/annotations/Annotations.java b/scene-lib/src/annotations/Annotations.java
new file mode 100644
index 0000000..cadf8d5
--- /dev/null
+++ b/scene-lib/src/annotations/Annotations.java
@@ -0,0 +1,237 @@
+package annotations;
+
+import annotations.el.AnnotationDef;
+import annotations.field.AnnotationAFT;
+import annotations.field.AnnotationFieldType;
+import annotations.field.ArrayAFT;
+import annotations.field.EnumAFT;
+import annotations.field.ScalarAFT;
+
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * This noninstantiable class provides useful static methods related to
+ * annotations, following the convention of {@link java.util.Collections}.
+ */
+public abstract class Annotations {
+    private Annotations() {}
+
+    public static Set<Annotation> noAnnotations;
+    public static Map<String, ? extends AnnotationFieldType> noFieldTypes;
+    public static Map<String, ? extends Object> noFieldValues;
+    public static Set<Annotation> typeQualifierMetaAnnotations;
+
+    public static EnumAFT aftRetentionPolicy;
+    public static AnnotationDef adRetention;
+    public static Annotation aRetentionClass;
+    public static Annotation aRetentionRuntime;
+    public static Annotation aRetentionSource;
+    public static Set<Annotation> asRetentionClass;
+    public static Set<Annotation> asRetentionRuntime;
+    public static Set<Annotation> asRetentionSource;
+
+    public static AnnotationDef adTarget;
+    public static Annotation aTargetTypeUse;
+
+    public static AnnotationDef adDocumented;
+    public static Annotation aDocumented;
+
+    public static AnnotationDef adNonNull;
+    public static Annotation aNonNull;
+
+    public static AnnotationDef adTypeQualifier;
+    public static Annotation aTypeQualifier;
+
+    /**
+     * Annotations that are meta-annotated with themselves.  Due to a flaw
+     * in the the Scene Library, it is unable to read them from classfiles.
+     * An expedient workaround is to pre-define them, so they never need be
+     * read from a classfile.
+     */
+    public static Set<AnnotationDef> standardDefs;
+
+    // the field types for an annotation with only one field, named "value".
+    static Map<String, ? extends AnnotationFieldType>
+                          valueFieldTypeOnly(AnnotationFieldType aft) {
+        return Collections.singletonMap("value", aft);
+    }
+
+    // the field values for an annotation with only one field, named "value".
+    public static Map<String, ? extends Object> valueFieldOnly(Object valueValue) {
+        return Collections.singletonMap("value", valueValue);
+    }
+
+    // Create an annotation definition with only a value field.
+    public static AnnotationDef createValueAnnotationDef(String name, Set<Annotation> metaAnnotations, AnnotationFieldType aft) {
+        return new AnnotationDef(name, metaAnnotations, valueFieldTypeOnly(aft));
+    }
+
+    // Create an annotation with only a value field.
+    public static Annotation createValueAnnotation(AnnotationDef ad, Object value) {
+        return new Annotation(ad, valueFieldOnly(value));
+    }
+
+    public static Annotation getRetentionPolicyMetaAnnotation(RetentionPolicy rp) {
+        switch (rp) {
+        case CLASS: return aRetentionClass;
+        case RUNTIME: return aRetentionRuntime;
+        case SOURCE: return aRetentionSource;
+        default:
+            throw new Error("This can't happen");
+        }
+    }
+
+    public static Set<Annotation> getRetentionPolicyMetaAnnotationSet(RetentionPolicy rp) {
+        switch (rp) {
+        case CLASS: return asRetentionClass;
+        case RUNTIME: return asRetentionRuntime;
+        case SOURCE: return asRetentionSource;
+        default:
+            throw new Error("This can't happen");
+        }
+    }
+
+    static {
+        noAnnotations = Collections.<Annotation> emptySet();
+        noFieldTypes = Collections.<String, AnnotationFieldType> emptyMap();
+        noFieldValues = Collections.<String, Object> emptyMap();
+
+        // This is slightly complicated because Retention's definition is
+        // meta-annotated by itself, we have to define the annotation
+        // before we can create the annotation on it.
+        aftRetentionPolicy = new EnumAFT("java.lang.annotation.RetentionPolicy");
+        adRetention = new AnnotationDef("java.lang.annotation.Retention");
+        adRetention.setFieldTypes(valueFieldTypeOnly(aftRetentionPolicy));
+        aRetentionRuntime = createValueAnnotation(adRetention, "RUNTIME");
+        adRetention.tlAnnotationsHere.add(aRetentionRuntime);
+        aRetentionClass = createValueAnnotation(adRetention, "CLASS");
+        aRetentionSource = createValueAnnotation(adRetention, "SOURCE");
+        asRetentionClass = Collections.singleton(aRetentionClass);
+        asRetentionRuntime = Collections.singleton(aRetentionRuntime);
+        asRetentionSource = Collections.singleton(aRetentionSource);
+
+        // Documented's definition is also self-meta-annotated.
+        adDocumented = new AnnotationDef("java.lang.annotation.Documented");
+        adDocumented.setFieldTypes(noFieldTypes);
+        aDocumented = new Annotation(adDocumented, noFieldValues);
+        adDocumented.tlAnnotationsHere.add(aDocumented);
+
+        adTarget = createValueAnnotationDef("java.lang.annotation.Target",
+                                            asRetentionRuntime,
+                                            new ArrayAFT(new EnumAFT("java.lang.annotation.ElementType")));
+        aTargetTypeUse = createValueAnnotation(adTarget,
+                                               // Problem:  ElementType.TYPE_USE is defined only in JDK 7.
+                                               // need to decide what the canonical format for these strings is.
+                                               // Collections.singletonList("java.lang.annotation.ElementType.TYPE_USE")
+                                               // This is the way that naively reading them from classfile gives.
+                                               Collections.singletonList("TYPE_USE")
+                                               );
+
+        typeQualifierMetaAnnotations = new HashSet<Annotation>();
+        typeQualifierMetaAnnotations.add(aRetentionRuntime);
+        typeQualifierMetaAnnotations.add(aTargetTypeUse);
+
+        adNonNull = new AnnotationDef("org.checkerframework.checker.nullness.qual.NonNull",
+                                      typeQualifierMetaAnnotations,
+                                      noFieldTypes);
+        aNonNull = new Annotation(adNonNull, noFieldValues);
+
+        adTypeQualifier = new AnnotationDef("org.checkerframework.framework.qual.TypeQualifier",
+                                            asRetentionRuntime,
+                                            noFieldTypes);
+        aTypeQualifier = new Annotation(adTypeQualifier, noFieldValues);
+
+        standardDefs = new LinkedHashSet<AnnotationDef>();
+        standardDefs.add(adTarget);
+        standardDefs.add(adDocumented);
+        standardDefs.add(adRetention);
+        // Because annotations can be read from classfiles, it isn't really
+        // necessary to add any more here.
+
+    }
+
+
+    /**
+     * Converts the given scalar annotation field value to one appropriate for
+     * passing to an {@link AnnotationBuilder} created by <code>af</code>.
+     * Conversion is only necessary if <code>x</code> is a subannotation, in
+     * which case we rebuild it with <code>af</code> since
+     * {@link AnnotationBuilder#addScalarField addScalarField} of an
+     * {@link AnnotationBuilder} created by <code>af</code> only accepts
+     * subannotations built by <code>af</code>.
+     */
+    private static Object convertAFV(ScalarAFT aft, Object x) {
+        if (aft instanceof AnnotationAFT) {
+            return rebuild((Annotation) x);
+        } else {
+            return x;
+        }
+    }
+
+    /**
+     * Rebuilds the annotation <code>a</code> using the factory
+     * <code>af</code> by iterating through its fields according to its
+     * definition and getting the values with {@link Annotation#getFieldValue}.
+     * Returns null if the factory is not interested in <code>a</code>.
+     */
+    public static final Annotation rebuild(Annotation a) {
+        AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(a.def());
+        if (ab != null) {
+            for (Map. Entry<String, AnnotationFieldType> fieldDef
+                    : a.def().fieldTypes.entrySet()) {
+
+                String fieldName = fieldDef.getKey();
+                AnnotationFieldType fieldType =
+                        fieldDef.getValue();
+                Object fieldValue =
+                        a.getFieldValue(fieldName);
+
+                Object nnFieldValue;
+                if (fieldValue != null) {
+                    nnFieldValue = fieldValue;
+                } else throw new IllegalArgumentException(
+                        "annotation has no field value");
+
+                if (fieldType instanceof ArrayAFT) {
+                    ArrayAFT aFieldType =
+                            (ArrayAFT) fieldType;
+                    ArrayBuilder arrb =
+                            ab.beginArrayField(fieldName, aFieldType);
+                    List<? extends Object> l =
+                            (List<? extends Object>) fieldValue;
+                    ScalarAFT nnElementType;
+                    if (aFieldType.elementType != null) {
+                        nnElementType = aFieldType.elementType;
+                    } else {
+                        throw new IllegalArgumentException(
+                                "annotation field type is missing element type");
+                    }
+                    for (Object o : l) {
+                        arrb.appendElement(convertAFV(
+                                nnElementType, o));
+                    }
+                    arrb.finish();
+                } else {
+                    ScalarAFT sFieldType =
+                            (ScalarAFT) fieldType;
+                    ab.addScalarField(fieldName, sFieldType,
+                            convertAFV(sFieldType, fieldValue));
+                }
+            }
+            return ab.finish();
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/scene-lib/src/annotations/ArrayBuilder.java b/scene-lib/src/annotations/ArrayBuilder.java
new file mode 100644
index 0000000..d05a5a6
--- /dev/null
+++ b/scene-lib/src/annotations/ArrayBuilder.java
@@ -0,0 +1,26 @@
+package annotations;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * Builds an array that will serve as a field of an annotation; created by
+ * {@link AnnotationBuilder#beginArrayField}.
+ */
+public interface ArrayBuilder {
+    /**
+     * Appends a value to the array. Call this method for each desired array
+     * element, in order. See the rules for values on
+     * {@link Annotation#getFieldValue}; furthermore, a subannotation must have
+     * been created by the same factory as the annotation of which it is a
+     * field.
+     */
+    void appendElement(Object x);
+
+    /**
+     * Finishes building the array. Call this method after all elements have
+     * been appended.
+     */
+    void finish();
+}
diff --git a/scene-lib/src/annotations/el/ABlock.java b/scene-lib/src/annotations/el/ABlock.java
new file mode 100644
index 0000000..bf064e0
--- /dev/null
+++ b/scene-lib/src/annotations/el/ABlock.java
@@ -0,0 +1,84 @@
+package annotations.el;
+
+import java.util.Map;
+
+import annotations.util.coll.VivifyingMap;
+
+/**
+ * ABlock has local variables in scope.
+ * We currently directly use them only for static initializer blocks, which are
+ * not methods, but can declare local variables.
+ */
+public class ABlock extends AExpression {
+    // Currently we don't validate the local locations (e.g., that no two
+    // distinct ranges for the same index overlap).
+    /** The method's annotated local variables; map key contains local variable location numbers */
+    public final VivifyingMap<LocalLocation, AField> locals =
+            AField.<LocalLocation>newVivifyingLHMap_AF();
+
+    ABlock(Object id) {
+        super(id);
+    }
+
+    ABlock(ABlock block) {
+        super(block);
+        copyMapContents(block.locals, locals);
+    }
+
+    @Override
+    public ABlock clone() {
+        return new ABlock(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(AElement o) {
+        return o instanceof ABlock &&
+            ((ABlock) o).equalsBlock(this);
+    }
+
+    protected boolean equalsBlock(ABlock o) {
+        return super.equalsExpression(o)
+                && o.locals.equals(locals);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + locals.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean prune() {
+        return super.prune() & locals.prune();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        // sb.append("ABlock ");
+        // sb.append(id);
+        for (Map.Entry<LocalLocation, AField> em : locals.entrySet()) {
+            LocalLocation loc = em.getKey();
+            sb.append(loc);
+            sb.append(": ");
+            AElement ae = em.getValue();
+            sb.append(ae.toString());
+            sb.append(' ');
+        }
+        sb.append(super.toString());
+        return sb.toString();
+    }
+
+    @Override
+    public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+        return v.visitBlock(this, t);
+    }
+}
diff --git a/scene-lib/src/annotations/el/AClass.java b/scene-lib/src/annotations/el/AClass.java
new file mode 100644
index 0000000..4adb8a5
--- /dev/null
+++ b/scene-lib/src/annotations/el/AClass.java
@@ -0,0 +1,215 @@
+package annotations.el;
+
+import java.util.LinkedHashMap;
+
+import annotations.Annotation;
+import annotations.util.coll.VivifyingMap;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/** An annotated class */
+public final class AClass extends ADeclaration {
+    /** The class's annotated type parameter bounds */
+    public final VivifyingMap<BoundLocation, ATypeElement> bounds =
+            ATypeElement.<BoundLocation>newVivifyingLHMap_ATE();
+
+    public final VivifyingMap<TypeIndexLocation, ATypeElement> extendsImplements =
+        ATypeElement.<TypeIndexLocation>newVivifyingLHMap_ATE();
+
+    private static VivifyingMap<String, AMethod> createMethodMap() {
+        return new VivifyingMap<String, AMethod>(
+                new LinkedHashMap<String, AMethod>()) {
+            @Override
+            public  AMethod createValueFor(String k) {
+                return new AMethod(k);
+            }
+
+            @Override
+            public boolean subPrune(AMethod v) {
+                return v.prune();
+            }
+        };
+    }
+
+    private static VivifyingMap<Integer, ABlock> createInitBlockMap() {
+        return new VivifyingMap<Integer, ABlock>(
+                new LinkedHashMap<Integer, ABlock>()) {
+            @Override
+            public  ABlock createValueFor(Integer k) {
+                return new ABlock(k);
+            }
+
+            @Override
+            public boolean subPrune(ABlock v) {
+                return v.prune();
+            }
+        };
+    }
+
+    private static VivifyingMap<String, AExpression> createFieldInitMap() {
+        return new VivifyingMap<String, AExpression>(
+                new LinkedHashMap<String, AExpression>()) {
+            @Override
+            public  AExpression createValueFor(String k) {
+                return new AExpression(k);
+            }
+
+            @Override
+            public boolean subPrune(AExpression v) {
+                return v.prune();
+            }
+        };
+    }
+
+
+    /**
+     * The class's annotated methods; a method's key consists of its name
+     * followed by its erased signature in JVML format.
+     * For example, <code>foo()V</code> or
+     * <code>bar(B[I[[Ljava/lang/String;)I</code>.  The annotation scene library
+     * does not validate the keys, nor does it check that annotated subelements
+     * of the {@link AMethod}s exist in the signature.
+     */
+    public final VivifyingMap<String, AMethod> methods =
+        createMethodMap();
+
+    public final VivifyingMap<Integer, ABlock> staticInits =
+        createInitBlockMap();
+
+    public final VivifyingMap<Integer, ABlock> instanceInits =
+        createInitBlockMap();
+
+    /** The class's annotated fields; map key is field name */
+    public final VivifyingMap<String, AField> fields =
+        AField.<String>newVivifyingLHMap_AF();
+
+    public final VivifyingMap<String, AExpression> fieldInits =
+        createFieldInitMap();
+
+    private final String className;
+
+    // debug fields to keep track of all classes created
+    // private static List<AClass> debugAllClasses = new ArrayList<AClass>();
+    // private final List<AClass> allClasses;
+
+    AClass(String className) {
+      super("class: " + className);
+      this.className = className;
+      // debugAllClasses.add(this);
+      // allClasses = debugAllClasses;
+    }
+
+    AClass(AClass clazz) {
+      super(clazz);
+      className = clazz.className;
+      copyMapContents(clazz.bounds, bounds);
+      copyMapContents(clazz.extendsImplements, extendsImplements);
+      copyMapContents(clazz.fieldInits, fieldInits);
+      copyMapContents(clazz.fields, fields);
+      copyMapContents(clazz.instanceInits, instanceInits);
+      copyMapContents(clazz.methods, methods);
+      copyMapContents(clazz.staticInits, staticInits);
+    }
+
+    @Override
+    public AClass clone() {
+      return new AClass(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof AClass
+            && ((AClass) o).equalsClass(this);
+    }
+
+    final boolean equalsClass(AClass o) {
+        return super.equals(o)
+            && className.equals(o.className)
+            && bounds.equals(o.bounds)
+            && methods.equals(o.methods)
+            && fields.equals(o.fields)
+            && extendsImplements.equals(o.extendsImplements);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + bounds.hashCode()
+            + methods.hashCode() + fields.hashCode()
+            + staticInits.hashCode() + instanceInits.hashCode()
+            + extendsImplements.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean prune() {
+        return super.prune() & bounds.prune()
+            & methods.prune() & fields.prune()
+            & staticInits.prune() & instanceInits.prune()
+            & extendsImplements.prune();
+    }
+
+    @Override
+    public String toString() {
+        return "AClass: " + className;
+    }
+
+    public String unparse() {
+        return unparse("");
+    }
+
+    public String unparse(String linePrefix) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(linePrefix);
+        sb.append(toString());
+        sb.append("\n");
+        sb.append(linePrefix);
+        sb.append("Annotations:\n");
+        for (Annotation a : tlAnnotationsHere) {
+            sb.append(linePrefix);
+            sb.append("  " + a + "\n");
+        }
+        sb.append(linePrefix);
+        sb.append("Bounds:\n");
+        plume.UtilMDE.mapToString(sb, bounds, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("Extends/implements:\n");
+        plume.UtilMDE.mapToString(sb, extendsImplements, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("Fields:\n");
+        plume.UtilMDE.mapToString(sb, fields, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("Field Initializers:\n");
+        plume.UtilMDE.mapToString(sb, fieldInits, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("Static Initializers:\n");
+        plume.UtilMDE.mapToString(sb, staticInits, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("Instance Initializers:\n");
+        plume.UtilMDE.mapToString(sb, instanceInits, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("AST Typecasts:\n");
+        plume.UtilMDE.mapToString(sb, insertTypecasts, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("AST Annotations:\n");
+        plume.UtilMDE.mapToString(sb, insertAnnotations, linePrefix + "  ");
+        sb.append(linePrefix);
+        sb.append("Methods:\n");
+        plume.UtilMDE.mapToString(sb, methods, linePrefix + "  ");
+        return sb.toString();
+    }
+
+    @Override
+    public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+        return v.visitClass(this, t);
+    }
+}
diff --git a/scene-lib/src/annotations/el/ADeclaration.java b/scene-lib/src/annotations/el/ADeclaration.java
new file mode 100644
index 0000000..1241b2e
--- /dev/null
+++ b/scene-lib/src/annotations/el/ADeclaration.java
@@ -0,0 +1,126 @@
+package annotations.el;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import annotations.io.ASTPath;
+import annotations.util.coll.VivifyingMap;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * A declaration, as opposed to an expression.  Base class for AClass,
+ * AMethod, and AField.
+ *
+ * @author dbro
+ */
+public abstract class ADeclaration extends AElement {
+  /** The element's insert-annotation invocations; map key is the AST path to the insertion place */
+  public final VivifyingMap<ASTPath, ATypeElement> insertAnnotations =
+          new VivifyingMap<ASTPath, ATypeElement>(
+                  new TreeMap<ASTPath, ATypeElement>()) {
+      @Override
+      public  ATypeElement createValueFor(ASTPath k) {
+          return new ATypeElement(k);
+      }
+
+      @Override
+      public boolean subPrune(ATypeElement v) {
+          return v.prune();
+      }
+  };
+
+  /** The element's annotated insert-typecast invocations; map key is the AST path to the insertion place */
+  public final VivifyingMap<ASTPath, ATypeElementWithType> insertTypecasts =
+          new VivifyingMap<ASTPath, ATypeElementWithType>(
+                new TreeMap<ASTPath, ATypeElementWithType>()) {
+      @Override
+      public ATypeElementWithType createValueFor(ASTPath k) {
+          return new ATypeElementWithType(k);
+      }
+
+      @Override
+      public boolean subPrune(ATypeElementWithType v) {
+          return v.prune();
+      }
+  };
+
+  protected ADeclaration(Object description) {
+    super(description, true);
+  }
+
+  ADeclaration(ADeclaration decl) {
+    super(decl);
+    copyMapContents(decl.insertAnnotations, insertAnnotations);
+    copyMapContents(decl.insertTypecasts, insertTypecasts);
+  }
+
+  ADeclaration(Object description, ADeclaration decl) {
+    super(decl, description);
+    copyMapContents(decl.insertAnnotations, insertAnnotations);
+    copyMapContents(decl.insertTypecasts, insertTypecasts);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return o instanceof ADeclaration
+        && ((ADeclaration) o).equalsDeclaration(this);
+  }
+
+  private boolean equalsDeclaration(ADeclaration o) {
+    return super.equals(o)
+            && insertAnnotations.equals(o.insertAnnotations)
+            && insertTypecasts.equals(o.insertTypecasts);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public int hashCode() {
+    return super.hashCode()
+        + (insertAnnotations == null ? 0 : insertAnnotations.hashCode())
+        + (insertTypecasts == null ? 0 : insertTypecasts.hashCode());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public boolean prune() {
+    return super.prune()
+        & (insertAnnotations == null || insertAnnotations.prune())
+        & (insertTypecasts == null || insertTypecasts.prune());
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    for (Map.Entry<ASTPath, ATypeElement> em :
+            insertAnnotations.entrySet()) {
+        sb.append("insert-annotation: ");
+        ASTPath loc = em.getKey();
+        sb.append(loc);
+        sb.append(": ");
+        ATypeElement ae = em.getValue();
+        sb.append(ae.toString());
+        sb.append(' ');
+    }
+    for (Map.Entry<ASTPath, ATypeElementWithType> em :
+        insertTypecasts.entrySet()) {
+      sb.append("insert-typecast: ");
+      ASTPath loc = em.getKey();
+      sb.append(loc);
+      sb.append(": ");
+      AElement ae = em.getValue();
+      sb.append(ae.toString());
+      sb.append(' ');
+    }
+    return sb.toString();
+  }
+
+  @Override
+  public abstract <R, T> R accept(ElementVisitor<R, T> v, T t);
+}
diff --git a/scene-lib/src/annotations/el/AElement.java b/scene-lib/src/annotations/el/AElement.java
new file mode 100644
index 0000000..926b7c9
--- /dev/null
+++ b/scene-lib/src/annotations/el/AElement.java
@@ -0,0 +1,217 @@
+package annotations.el;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import annotations.Annotation;
+import annotations.util.coll.VivifyingMap;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * An <code>AElement</code> represents a Java element and the annotations it
+ * carries. Some <code>AElements</code> may contain others; for example, an
+ * {@link AClass} may contain {@link AMethod}s. Every <code>AElement</code>
+ * usually belongs directly or indirectly to an {@link AScene}. Each subclass
+ * of <code>AElement</code> represents one kind of annotatable element; its
+ * name should make this clear.
+ */
+public class AElement implements Cloneable {
+    static <K extends Object> VivifyingMap<K, AElement> newVivifyingLHMap_AE() {
+        return new VivifyingMap<K, AElement>(
+                new LinkedHashMap<K, AElement>()) {
+            @Override
+            public AElement createValueFor(K k) {
+                return new AElement(k);
+            }
+
+            @Override
+            public boolean subPrune(AElement v) {
+                return v.prune();
+            }
+        };
+    }
+
+    // Different from the above in that the elements are guaranteed to
+    // contain a non-null "type" field.
+    static <K extends Object> VivifyingMap<K, AElement> newVivifyingLHMap_AET() {
+        return new VivifyingMap<K, AElement>(
+                new LinkedHashMap<K, AElement>()) {
+            @Override
+            public AElement createValueFor(K k) {
+                return new AElement(k, true);
+            }
+
+            @Override
+            public boolean subPrune(AElement v) {
+                return v.prune();
+            }
+        };
+    }
+
+    @SuppressWarnings("unchecked")
+    <K, V extends AElement> void
+    copyMapContents(VivifyingMap<K, V> orig, VivifyingMap<K, V> copy) {
+        for (K key : orig.keySet()) {
+            V val = orig.get(key);
+            copy.put(key, (V) val.clone());
+        }
+    }
+
+    /**
+     * The top-level annotations directly on this element.  Annotations on
+     * subelements are in those subelements' <code>tlAnnotationsHere</code>
+     * sets, not here.
+     */
+    public final Set<Annotation> tlAnnotationsHere;
+
+    /** The type of a field or a method parameter */
+    public final ATypeElement type; // initialized in constructor
+
+    public Annotation lookup(String name) {
+        for (Annotation anno : tlAnnotationsHere) {
+            if (anno.def.name.equals(name)) {
+                return anno;
+            }
+        }
+        return null;
+    }
+
+    // general description of the element
+    final Object description;
+
+    AElement(Object description) {
+        this(description, false);
+    }
+
+    AElement(Object description, boolean hasType) {
+        this(description,
+            hasType ? new ATypeElement("type of " + description) : null);
+    }
+
+    AElement(Object description, ATypeElement type) {
+        tlAnnotationsHere = new LinkedHashSet<Annotation>();
+        this.description = description;
+        this.type = type;
+    }
+
+    AElement(AElement elem) {
+        this(elem, elem.type);
+    }
+
+    AElement(AElement elem, ATypeElement type) {
+        this(elem.description,
+            type == null ? null : type.clone());
+        tlAnnotationsHere.addAll(elem.tlAnnotationsHere);
+    }
+
+    AElement(AElement elem, Object description) {
+        this(description, elem.type == null ? null : elem.type.clone());
+        tlAnnotationsHere.addAll(elem.tlAnnotationsHere);
+    }
+
+    // Q: Are there any fields other than elements and maps that can't be shared?
+
+    @Override
+    public AElement clone() {
+        return new AElement(this);
+    }
+
+    /**
+     * Returns whether this {@link AElement} equals <code>o</code> (see
+     * warnings below). Generally speaking, two {@link AElement}s are equal if
+     * they are of the same type, have the same {@link #tlAnnotationsHere}, and
+     * have recursively equal, corresponding subelements.  Two warnings:
+     *
+     * <ul>
+     * <li>While subelement collections usually remember the order in which
+     * subelements were added and regurgitate them in this order, two
+     * {@link AElement}s are equal even if order of subelements differs.
+     * <li>Two {@link AElement}s are unequal if one has a subelement that the
+     * other does not, even if the tree of elements rooted at the subelement
+     * contains no annotations.  Thus, if you want to compare the
+     * <em>annotations</em>, you should {@link #prune} both {@link AElement}s
+     * first.
+     * </ul>
+     */
+    @Override
+    // Was final.  Removed that so that AnnotationDef can redefine.
+    public boolean equals(Object o) {
+        return o instanceof AElement
+            && ((AElement) o).equals(this);
+    }
+
+    /**
+     * Returns whether this {@link AElement} equals <code>o</code>; a
+     * slightly faster variant of {@link #equals(Object)} for when the argument
+     * is statically known to be another nonnull {@link AElement}.
+     */
+    /*
+     * We need equals to be symmetric and operate correctly over the class
+     * hierarchy.  Let x and y be objects of subclasses S and T, respectively,
+     * of AElement.  x.equals((AElement) y) shall check that y is an S.  If so,
+     * it shall call ((S) y).equalsS(x), which checks that x is a T and then
+     * compares fields.
+     */
+    public boolean equals(AElement o) {
+        return o.equalsElement(this);
+    }
+
+    final boolean equalsElement(AElement o) {
+        return o.tlAnnotationsHere.equals(tlAnnotationsHere)
+            && (o.type == null ? type == null : o.type.equals(type));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return getClass().getName().hashCode() + tlAnnotationsHere.hashCode()
+            + (type == null ? 0 : type.hashCode());
+    }
+
+    /**
+     * Removes empty subelements of this {@link AElement} depth-first; returns
+     * whether this {@link AElement} is itself empty after pruning.
+     */
+    // In subclasses, we use & (not &&) because we want complete evaluation:
+    // we should prune everything even if the first subelement is nonempty.
+    public boolean prune() {
+        return tlAnnotationsHere.isEmpty()
+            & (type == null || type.prune());
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("AElement: ");
+      sb.append(description);
+      sb.append(" : ");
+      tlAnnotationsHereFormatted(sb);
+      if (type!=null) {
+          sb.append(' ');
+          sb.append(type.toString());
+      }
+      // typeargs?
+      return sb.toString();
+    }
+
+    public void tlAnnotationsHereFormatted(StringBuilder sb) {
+        boolean first = true;
+        for (Annotation aElement : tlAnnotationsHere) {
+            if (!first) {
+                sb.append(", ");
+            }
+            first = false;
+            sb.append(aElement.toString());
+        }
+    }
+
+    public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+        return v.visitElement(this, t);
+    }
+}
diff --git a/scene-lib/src/annotations/el/AExpression.java b/scene-lib/src/annotations/el/AExpression.java
new file mode 100644
index 0000000..755dfc6
--- /dev/null
+++ b/scene-lib/src/annotations/el/AExpression.java
@@ -0,0 +1,210 @@
+package annotations.el;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import annotations.util.coll.VivifyingMap;
+
+/**
+ * Manages all annotations within expressions, that is, annotations on typecasts,
+ * instanceofs, and object creations.
+ * We can use this class for methods, field initializers, and static initializers.
+ */
+public class AExpression extends AElement {
+    /** The method's annotated typecasts; map key is the offset of the checkcast bytecode */
+    public final VivifyingMap<RelativeLocation, ATypeElement> typecasts =
+            ATypeElement.<RelativeLocation>newVivifyingLHMap_ATE();
+
+    /** The method's annotated "instanceof" tests; map key is the offset of the instanceof bytecode */
+    public final VivifyingMap<RelativeLocation, ATypeElement> instanceofs =
+            ATypeElement.<RelativeLocation>newVivifyingLHMap_ATE();
+
+    /** The method's annotated "new" invocations; map key is the offset of the new bytecode */
+    public final VivifyingMap<RelativeLocation, ATypeElement> news =
+            ATypeElement.<RelativeLocation>newVivifyingLHMap_ATE();
+
+    /** A method invocation's annotated type arguments; map key is the offset of the invokestatic bytecode */
+    public final VivifyingMap<RelativeLocation, ATypeElement> calls =
+            ATypeElement.<RelativeLocation>newVivifyingLHMap_ATE();
+
+    /** A member reference's annotated type parameters; map key is the offset of the invokestatic bytecode */
+    public final VivifyingMap<RelativeLocation, ATypeElement> refs =
+            ATypeElement.<RelativeLocation>newVivifyingLHMap_ATE();
+
+    /** The method's annotated lambda expressions; map key is the offset of the invokedynamic bytecode */
+    public final VivifyingMap<RelativeLocation, AMethod> funs =
+            new VivifyingMap<RelativeLocation, AMethod>(
+                    new LinkedHashMap<RelativeLocation, AMethod>()) {
+        @Override
+        public AMethod createValueFor(RelativeLocation k) {
+            return new AMethod("" + k);  // FIXME: find generated method name
+        }
+
+        @Override
+        public boolean subPrune(AMethod v) {
+            return v.prune();
+        }
+    };
+
+    protected Object id;
+
+    AExpression(Object id) {
+        super(id);
+
+        this.id = id;
+    }
+
+    AExpression(AExpression expr) {
+        super(expr);
+        copyMapContents(expr.typecasts, typecasts);
+        copyMapContents(expr.instanceofs, instanceofs);
+        copyMapContents(expr.news, news);
+        copyMapContents(expr.calls, calls);
+        copyMapContents(expr.refs, refs);
+        copyMapContents(expr.funs, funs);
+        id = expr.id;
+    }
+
+    @Override
+    public AExpression clone() {
+        return new AExpression(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(AElement o) {
+        return o instanceof AExpression &&
+            ((AExpression) o).equalsExpression(this);
+    }
+
+    protected boolean equalsExpression(AExpression o) {
+        return super.equals(o)
+                && typecasts.equals(o.typecasts)
+                && instanceofs.equals(o.instanceofs)
+                && news.equals(o.news)
+                && refs.equals(o.refs)
+                && calls.equals(o.calls)
+                && funs.equals(o.funs);
+        }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode() + typecasts.hashCode()
+            + instanceofs.hashCode() + news.hashCode()
+            + refs.hashCode() + calls.hashCode() + funs.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean prune() {
+        return super.prune() & typecasts.prune() & instanceofs.prune()
+                & news.prune() & refs.prune() & calls.prune() & funs.prune();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        SortedMap<RelativeLocation, ATypeElement> map =
+            new TreeMap<RelativeLocation, ATypeElement>();
+        RelativeLocation prev = null;
+        // sb.append("AExpression ");
+        // sb.append(id);
+        for (Map.Entry<RelativeLocation, ATypeElement> em : typecasts.entrySet()) {
+            sb.append("typecast: ");
+            RelativeLocation loc = em.getKey();
+            sb.append(loc);
+            sb.append(": ");
+            AElement ae = em.getValue();
+            sb.append(ae.toString());
+            sb.append(' ');
+        }
+        for (Map.Entry<RelativeLocation, ATypeElement> em : instanceofs.entrySet()) {
+            sb.append("instanceof: ");
+            RelativeLocation loc = em.getKey();
+            sb.append(loc);
+            sb.append(": ");
+            AElement ae = em.getValue();
+            sb.append(ae.toString());
+            sb.append(' ');
+        }
+        for (Map.Entry<RelativeLocation, ATypeElement> em : news.entrySet()) {
+            sb.append("new ");
+            RelativeLocation loc = em.getKey();
+            sb.append(loc);
+            sb.append(": ");
+            AElement ae = em.getValue();
+            sb.append(ae.toString());
+            sb.append(' ');
+        }
+        map.putAll(refs);
+        for (Entry<RelativeLocation, ATypeElement> em : map.entrySet()) {
+            RelativeLocation loc = em.getKey();
+            AElement ae = em.getValue();
+            boolean isOffset = loc.index < 0;
+            if (prev == null
+                    || (isOffset ? loc.offset == prev.offset
+                        : loc.index == prev.index)) {
+                sb.append("reference ");
+                sb.append(isOffset ? "*" + loc.offset : "#" + loc.index);
+                sb.append(": ");
+                sb.append(ae.toString());
+            }
+            if (loc.type_index >= 0) {
+                sb.append("typearg " + loc);
+                sb.append(": ");
+                sb.append(ae.toString());
+                sb.append(' ');
+            }
+            prev = loc;
+        }
+        prev = null;
+        map.clear();
+        map.putAll(calls);
+        for (Entry<RelativeLocation, ATypeElement> em : map.entrySet()) {
+            RelativeLocation loc = em.getKey();
+            AElement ae = em.getValue();
+            boolean isOffset = loc.index < 0;
+            if (prev == null
+                    || (isOffset ? loc.offset == prev.offset
+                        : loc.index == prev.index)) {
+                sb.append("call ");
+                sb.append(isOffset ? "*" + loc.offset : "#" + loc.index);
+                sb.append(": ");
+            }
+            if (loc.type_index >= 0) {
+                sb.append("typearg " + loc);
+                sb.append(": ");
+                sb.append(ae.toString());
+                sb.append(' ');
+            }
+            prev = loc;
+        }
+        prev = null;
+        map.clear();
+        for (Map.Entry<RelativeLocation, AMethod> em : funs.entrySet()) {
+            sb.append("lambda ");
+            RelativeLocation loc = em.getKey();
+            sb.append(loc);
+            sb.append(": ");
+            AElement ae = em.getValue();
+            sb.append(ae.toString());
+            sb.append(' ');
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+        return v.visitExpression(this, t);
+    }
+}
diff --git a/scene-lib/src/annotations/el/AField.java b/scene-lib/src/annotations/el/AField.java
new file mode 100644
index 0000000..aec4d9e
--- /dev/null
+++ b/scene-lib/src/annotations/el/AField.java
@@ -0,0 +1,70 @@
+package annotations.el;
+
+import java.util.LinkedHashMap;
+
+import annotations.util.coll.VivifyingMap;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+public class AField extends ADeclaration {
+  static <K extends Object> VivifyingMap<K, AField>
+  newVivifyingLHMap_AF() {
+    return new VivifyingMap<K, AField>(
+        new LinkedHashMap<K, AField>()) {
+      @Override
+      public AField createValueFor(K k) {
+        return new AField("" + k);
+      }
+
+      @Override
+      public boolean subPrune(AField v) {
+        return v.prune();
+      }
+    };
+  }
+
+  public AExpression init;
+  private final String fieldName;
+
+  AField(String fieldName) {
+    super(fieldName);
+    this.fieldName = fieldName;
+  }
+
+  AField(AField field) {
+    super(field.fieldName, field);
+    fieldName = field.fieldName;
+    init = field.init == null ? null : field.init.clone();
+  }
+
+  @Override
+  public AField clone() {
+    return new AField(this);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return o instanceof AField
+        && ((AField) o).equalsField(this);
+  }
+
+  final boolean equalsField(AField o) {
+    return super.equals(o);
+  }
+
+  @Override  // TODO: remove?
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("AField ");
+    sb.append(fieldName);
+    sb.append(super.toString());
+    return sb.toString();
+  }
+
+  @Override
+  public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+    return v.visitField(this, t);
+  }
+}
diff --git a/scene-lib/src/annotations/el/AMethod.java b/scene-lib/src/annotations/el/AMethod.java
new file mode 100644
index 0000000..980addc
--- /dev/null
+++ b/scene-lib/src/annotations/el/AMethod.java
@@ -0,0 +1,137 @@
+package annotations.el;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import java.util.Map;
+
+import annotations.util.coll.VivifyingMap;
+
+/**
+ * An annotated method; contains bounds, return, parameters, receiver, and throws.
+ */
+public final class AMethod extends ADeclaration {
+    /** The method's annotated type parameter bounds */
+    public final VivifyingMap<BoundLocation, ATypeElement> bounds =
+            ATypeElement.<BoundLocation>newVivifyingLHMap_ATE();
+
+    /** The method's annotated return type */
+    public final ATypeElement returnType; // initialized in constructor
+
+    /** The method's annotated receiver parameter type */
+    public final AField receiver; // initialized in constructor
+
+    /** The method's annotated parameters; map key is parameter index */
+    public final VivifyingMap<Integer, AField> parameters =
+            AField.<Integer>newVivifyingLHMap_AF();
+
+    public final VivifyingMap<TypeIndexLocation, ATypeElement> throwsException =
+        ATypeElement.<TypeIndexLocation>newVivifyingLHMap_ATE();
+
+    public ABlock body;
+    public final String methodName;
+
+    AMethod(String methodName) {
+      super("method: " + methodName);
+      this.methodName = methodName;
+      this.body = new ABlock(methodName);
+      returnType = new ATypeElement("return type of " + methodName);
+      receiver = new AField("receiver parameter type of " + methodName);
+    }
+
+    AMethod(AMethod method) {
+      super("method: " + method.methodName, method);
+      methodName = method.methodName;
+      body = method.body.clone();
+      returnType = method.returnType.clone();
+      receiver = method.receiver.clone();
+      copyMapContents(method.bounds, bounds);
+      copyMapContents(method.parameters, parameters);
+      copyMapContents(method.throwsException, throwsException);
+      copyMapContents(method.bounds, bounds);
+    }
+
+    @Override
+    public AMethod clone() {
+      return new AMethod(this);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(AElement o) {
+        return o instanceof AMethod &&
+            ((AMethod) o).equalsMethod(this);
+    }
+
+    boolean equalsMethod(AMethod o) {
+        parameters.prune();
+        o.parameters.prune();
+
+        return super.equals(o)
+            && returnType.equalsTypeElement(o.returnType)
+            && bounds.equals(o.bounds)
+            && receiver.equals(o.receiver)
+            && parameters.equals(o.parameters)
+            && body.equals(o.body)
+            && methodName.equals(o.methodName)
+            && throwsException.equals(o.throwsException);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return super.hashCode()
+                + bounds.hashCode() + receiver.hashCode()
+                + parameters.hashCode() + throwsException.hashCode()
+                + body.hashCode() + methodName.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean prune() {
+        return super.prune() & bounds.prune()
+            & returnType.prune()
+            & receiver.prune() & parameters.prune()
+            & throwsException.prune() & body.prune();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("AMethod ");
+        sb.append(methodName);
+        sb.append(": (");
+        sb.append(" -1:");
+        sb.append(receiver.toString());
+        // int size = parameters.size();
+        for (Map.Entry<Integer, AField> em : parameters.entrySet()) {
+            Integer i = em.getKey();
+            sb.append(" ");
+            sb.append(i);
+            sb.append(":");
+            AElement ae = em.getValue();
+            sb.append(ae.toString());
+            sb.append(" ");
+            ATypeElement ate = ae.type;
+            sb.append(ate.toString());
+        }
+        sb.append(" ");
+        sb.append("ret:");
+        sb.append(returnType.toString());
+        sb.append(") ");
+        sb.append(body.toString());
+        return sb.toString();
+    }
+
+    @Override
+    public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+        return v.visitMethod(this, t);
+    }
+}
diff --git a/scene-lib/src/annotations/el/AScene.java b/scene-lib/src/annotations/el/AScene.java
new file mode 100644
index 0000000..72bab22
--- /dev/null
+++ b/scene-lib/src/annotations/el/AScene.java
@@ -0,0 +1,371 @@
+package annotations.el;
+
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import annotations.Annotation;
+import annotations.io.IndexFileParser;
+import annotations.util.coll.VivifyingMap;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * An <code>AScene</code> (annotated scene) represents the annotations on a
+ * set of Java classes and packages along with the definitions of some or all of
+ * the annotation types used.
+ *
+ * <p>
+ * Each client of the annotation library may wish to use its own representation
+ * for certain kinds of annotations instead of a simple name-value map; thus, a
+ * layer of abstraction in the storage of annotations was introduced.
+ *
+ * <p>
+ * <code>AScene</code>s and many {@link AElement}s can contain other
+ * {@link AElement}s. When these objects are created, their collections of
+ * subelements are empty. In order to associate an annotation with a particular
+ * Java element in an <code>AScene</code>, one must first ensure that an
+ * appropriate {@link AElement} exists in the <code>AScene</code>. To this
+ * end, the maps of subelements have a <code>vivify</code> method. Calling
+ * <code>vivify</code> to access a particular subelement will return the
+ * subelement if it already exists; otherwise it will create and then return the
+ * subelement. (Compare to vivification in Perl.) For example, the following
+ * code will obtain an {@link AMethod} representing <code>Foo.bar</code> in
+ * the <code>AScene</code> <code>s</code>, creating it if it did not
+ * already exist:
+ *
+ * <pre>
+ * AMethod&lt;A&gt; m = s.classes.vivify("Foo").methods.vivify("bar");
+ * </pre>
+ *
+ * <p>
+ * Then one can add an annotation to the method:
+ *
+ * <pre>
+ * m.annotationsHere.add(new Annotation(
+ *     new AnnotationDef(taintedDef, RetentionPolicy.RUNTIME, true),
+ *     new Annotation(taintedDef, Collections.emptyMap())
+ * ));
+ * </pre>
+ */
+public final class AScene implements Cloneable {
+    private static boolean checkClones = true;
+    public static boolean debugFoundMap = false;
+
+    /** This scene's annotated packages; map key is package name */
+    public final VivifyingMap<String, AElement> packages =
+            AElement.<String>newVivifyingLHMap_AE();
+
+    /**
+     * Contains for each annotation type a set of imports to be added to
+     *  the source if the annotation is inserted with the "abbreviate"
+     *  option on.
+     */
+    public final Map<String, Set<String>> imports =
+        new LinkedHashMap<String, Set<String>>();
+
+    /** This scene's annotated classes; map key is class name */
+    public final VivifyingMap<String, AClass> classes =
+            new VivifyingMap<String, AClass>(
+                    new LinkedHashMap<String, AClass>()) {
+                @Override
+                public  AClass createValueFor(
+                 String k) {
+                    return new AClass(k);
+                }
+
+                @Override
+                public boolean subPrune(AClass v) {
+                    return v.prune();
+                }
+            };
+
+    /**
+     * Creates a new {@link AScene} with no classes or packages.
+     */
+    public AScene() {
+    }
+
+    /**
+     * Copy constructor for {@link AScene}.
+     */
+    public AScene(AScene scene) {
+        for (String key : scene.packages.keySet()) {
+            AElement val = scene.packages.get(key);
+            packages.put(key, val.clone());
+        }
+        for (String key : scene.imports.keySet()) {
+            // copy could in principle have different Set implementation
+            Set<String> value = scene.imports.get(key);
+            Set<String> copy = new LinkedHashSet<String>();
+            copy.addAll(value);
+            imports.put(key, copy);
+        }
+        for (String key : scene.classes.keySet()) {
+            AClass clazz = scene.classes.get(key);
+            classes.put(key, clazz.clone());
+        }
+        if (checkClones) {
+            checkClone(this, scene);
+        }
+    }
+
+    @Override
+    public AScene clone() {
+        return new AScene(this);
+    }
+
+    /**
+     * Returns whether this {@link AScene} equals <code>o</code>; the
+     * commentary and the cautionary remarks on {@link AElement#equals(Object)}
+     * also apply to {@link AScene#equals(Object)}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof AScene
+            && ((AScene) o).equals(this);
+    }
+
+    /**
+     * Returns whether this {@link AScene} equals <code>o</code>; a
+     * slightly faster variant of {@link #equals(Object)} for when the argument
+     * is statically known to be another nonnull {@link AScene}.
+     */
+    public boolean equals(AScene o) {
+        return o.classes.equals(classes) && o.packages.equals(packages);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return classes.hashCode() + packages.hashCode();
+    }
+
+    /**
+     * Removes empty subelements of this {@link AScene} depth-first; returns
+     * whether this {@link AScene} is itself empty after pruning.
+     */
+    public boolean prune() {
+        return classes.prune() & packages.prune();
+    }
+
+    /** Returns a string representation. */
+    public String unparse() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("packages:\n");
+        for (Map.Entry<String, AElement> entry : packages.entrySet()) {
+            sb.append("  " + entry.getKey() + " => " + entry.getValue() + "\n");
+        }
+        sb.append("classes:\n");
+        for (Map.Entry<String, AClass> entry : classes.entrySet()) {
+            sb.append("  " + entry.getKey() + " => " + "\n");
+            sb.append(entry.getValue().unparse("    "));
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        return unparse();
+    }
+
+    /**
+     * Throws exception if the arguments 1) are the same reference;
+     * 2) are not equal() in both directions; or 3) contain
+     * corresponding elements that meet either of the preceding two
+     * conditions.
+     */
+    public static void checkClone(AScene s0, AScene s1) {
+        if (s0 == null) {
+            if (s1 != null) {
+                cloneCheckFail();
+            }
+        } else {
+            if (s1 == null) {
+                cloneCheckFail();
+            }
+            s0.prune();
+            s1.prune();
+            if (s0 == s1) {
+                cloneCheckFail();
+            }
+            checkElems(s0.packages, s1.packages);
+            checkElems(s0.classes, s1.classes);
+        }
+    }
+
+    public static <K, V extends AElement> void
+    checkElems(VivifyingMap<K, V> m0, VivifyingMap<K, V> m1) {
+        if (m0 == null) {
+            if (m1 != null) {
+                cloneCheckFail();
+            }
+        } else if (m1 == null) {
+            cloneCheckFail();
+        } else {
+            for (K k : m0.keySet()) {
+                checkElem(m0.get(k), m1.get(k));
+            }
+        }
+    }
+
+    /**
+     * Throw exception on visit if e0 == e1 or !e0.equals(e1).
+     * (See {@link #checkClone(AScene, AScene)} for explanation.)
+     */
+    public static void checkElem(AElement e0, AElement e1) {
+        checkObject(e0, e1);
+        if (e0 != null) {
+            if (e0 == e1) {
+                cloneCheckFail();
+            }
+            e0.accept(checkVisitor, e1);
+        }
+    }
+
+    /**
+     * Throw exception on visit if !el.equals(arg) or !arg.equals(el).
+     * (See {@link #checkClone(AScene, AScene)} for explanation.)
+     */
+    public static void checkObject(Object o0, Object o1) {
+        if (o0 == null ? o1 != null
+                : !(o0.equals(o1) && o1.equals(o0))) {  // ok if ==
+            throw new RuntimeException("clone check failed");
+        }
+    }
+
+    /**
+     * Throw exception on visit if el == arg or !el.equals(arg).
+     * (See {@link checkClone(AScene, AScene)} for explanation.)
+     */
+    private static ElementVisitor<Void, AElement> checkVisitor =
+        new ElementVisitor<Void, AElement>() {
+            @Override
+            public Void visitAnnotationDef(AnnotationDef el,
+                    AElement arg) {
+                return null;
+            }
+
+            @Override
+            public Void visitBlock(ABlock el, AElement arg) {
+                ABlock b = (ABlock) arg;
+                checkElems(el.locals, b.locals);
+                return null;
+            }
+
+            @Override
+            public Void visitClass(AClass el, AElement arg) {
+                AClass c = (AClass) arg;
+                checkElems(el.bounds, c.bounds);
+                checkElems(el.extendsImplements, c.extendsImplements);
+                checkElems(el.fieldInits, c.fieldInits);
+                checkElems(el.fields, c.fields);
+                checkElems(el.instanceInits, c.instanceInits);
+                checkElems(el.methods, c.methods);
+                checkElems(el.staticInits, c.staticInits);
+                return visitDeclaration(el, arg);
+            }
+
+            @Override
+            public Void visitDeclaration(ADeclaration el, AElement arg) {
+                ADeclaration d = (ADeclaration) arg;
+                checkElems(el.insertAnnotations, d.insertAnnotations);
+                checkElems(el.insertTypecasts, d.insertTypecasts);
+                return visitElement(el, arg);
+            }
+
+            @Override
+            public Void visitExpression(AExpression el, AElement arg) {
+                AExpression e = (AExpression) arg;
+                checkObject(el.id, e.id);
+                checkElems(el.calls, e.calls);
+                checkElems(el.funs, e.funs);
+                checkElems(el.instanceofs, e.instanceofs);
+                checkElems(el.news, e.news);
+                checkElems(el.refs, e.refs);
+                checkElems(el.typecasts, e.typecasts);
+                return visitElement(el, arg);
+            }
+
+            @Override
+            public Void visitField(AField el, AElement arg) {
+                AField f = (AField) arg;
+                checkElem(el.init, f.init);
+                return visitDeclaration(el, arg);
+            }
+
+            @Override
+            public Void visitMethod(AMethod el, AElement arg) {
+                AMethod m = (AMethod) arg;
+                checkObject(el.methodName, m.methodName);
+                checkElem(el.body, m.body);
+                checkElem(el.returnType, m.returnType);
+                checkElems(el.bounds, m.bounds);
+                checkElems(el.parameters, m.parameters);
+                checkElems(el.throwsException, m.throwsException);
+                return null;
+            }
+
+            @Override
+            public Void visitTypeElement(ATypeElement el, AElement arg) {
+                ATypeElement t = (ATypeElement) arg;
+                checkObject(el.description, t.description);
+                checkElems(el.innerTypes, t.innerTypes);
+                return null;
+            }
+
+            @Override
+            public Void visitTypeElementWithType(ATypeElementWithType el,
+                    AElement arg) {
+                ATypeElementWithType t = (ATypeElementWithType) arg;
+                checkObject(el.getType(), t.getType());
+                return visitTypeElement(el, arg);
+            }
+
+            @Override
+            public Void visitElement(AElement el, AElement arg) {
+                checkObject(el.description, arg.description);
+                if (el.tlAnnotationsHere.size() !=
+                        arg.tlAnnotationsHere.size()) {
+                    cloneCheckFail();
+                }
+                for (Annotation a : el.tlAnnotationsHere) {
+                    if (!arg.tlAnnotationsHere.contains(a)) {
+                        cloneCheckFail();
+                    }
+                }
+                checkElem(el.type, arg.type);
+                return null;
+            }
+        };
+
+    private static void cloneCheckFail() {
+        throw new RuntimeException("clone check failed");
+    }
+
+    // temporary main for easy testing on JAIFs
+    public static void main(String[] args) {
+        int status = 0;
+        checkClones = true;
+
+        for (int i = 0; i < args.length; i++) {
+            AScene s0 = new AScene();
+            System.out.print(args[i] + ": ");
+            try {
+                IndexFileParser.parseFile(args[i], s0);
+                s0.clone();
+                System.out.println("ok");
+            } catch (Throwable e) {
+                status = 1;
+                System.out.println("failed");
+                e.printStackTrace();
+            }
+        }
+        System.exit(status);
+    }
+}
diff --git a/scene-lib/src/annotations/el/ATypeElement.java b/scene-lib/src/annotations/el/ATypeElement.java
new file mode 100644
index 0000000..c059906
--- /dev/null
+++ b/scene-lib/src/annotations/el/ATypeElement.java
@@ -0,0 +1,127 @@
+package annotations.el;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import annotations.Annotation;
+import annotations.util.coll.VivifyingMap;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * An {@link AElement} that represents a type might have annotations on inner
+ * types ("generic/array" annotations in the design document). All such elements
+ * extend {@link ATypeElement} so that their annotated inner types can be
+ * accessed in a uniform fashion. An {@link AElement} holds the annotations on
+ * one inner type; {@link #innerTypes} maps locations to inner types.
+ */
+public class ATypeElement extends AElement {
+    static <K extends Object> VivifyingMap<K, ATypeElement> newVivifyingLHMap_ATE() {
+        return new VivifyingMap<K, ATypeElement>(
+                new LinkedHashMap<K, ATypeElement>()) {
+            @Override
+            public  ATypeElement createValueFor(K k) {
+                return new ATypeElement(k);
+            }
+
+            @Override
+            public boolean subPrune(ATypeElement v) {
+                return v.prune();
+            }
+        };
+    }
+
+    /**
+     * The annotated inner types; map key is the inner type location.
+     */
+    public final VivifyingMap<InnerTypeLocation, ATypeElement> innerTypes =
+        ATypeElement.<InnerTypeLocation>newVivifyingLHMap_ATE();
+
+    // general information about the element being annotated
+    public Object description;
+
+    ATypeElement(Object description) {
+        super(description);
+        this.description = description;
+    }
+
+    ATypeElement(ATypeElement elem) {
+      super(elem);
+      description = elem.description;
+      copyMapContents(elem.innerTypes, innerTypes);
+    }
+
+    @Override
+    public ATypeElement clone() {
+        return new ATypeElement(this);
+    }
+
+    void checkRep() {
+        assert type == null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(AElement o) {
+        return o instanceof ATypeElement
+            && ((ATypeElement) o).equalsTypeElement(this);
+    }
+
+    // note:  does not call super.equals, so does not check name
+    final boolean equalsTypeElement(ATypeElement o) {
+        return equalsElement(o) && o.innerTypes.equals(innerTypes)
+            && (type == null ? o.type == null : o.type.equals(type));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        checkRep();
+        return tlAnnotationsHere.hashCode() + innerTypes.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean prune() {
+        checkRep();
+        return super.prune() & innerTypes.prune();
+    }
+
+    private static final String lineSep = System.getProperty("line.separator");
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("ATypeElement: ");
+        sb.append(description);
+        sb.append(" : ");
+        for (Annotation a : tlAnnotationsHere) {
+            sb.append(a.toString());
+            sb.append(" ");
+        }
+        sb.append("{");
+        String linePrefix = "  ";
+        for (Map.Entry<InnerTypeLocation, ATypeElement> entry : innerTypes.entrySet()) {
+            sb.append(linePrefix);
+            sb.append(entry.getKey().toString());
+            sb.append(" => ");
+            sb.append(entry.getValue().toString());
+            sb.append(lineSep);
+        }
+        sb.append("}");
+        return sb.toString();
+    }
+
+    @Override
+    public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+        return v.visitTypeElement(this, t);
+    }
+}
diff --git a/scene-lib/src/annotations/el/ATypeElementWithType.java b/scene-lib/src/annotations/el/ATypeElementWithType.java
new file mode 100644
index 0000000..8f08ae4
--- /dev/null
+++ b/scene-lib/src/annotations/el/ATypeElementWithType.java
@@ -0,0 +1,117 @@
+package annotations.el;
+
+import java.util.LinkedHashMap;
+
+import type.Type;
+import annotations.io.ASTPath;
+import annotations.util.coll.VivifyingMap;
+
+/**
+ * An {@link ATypeElement} that also stores an un-annotated type. This is useful for cast
+ * insertion or receiver insertion.
+ */
+public class ATypeElementWithType extends ATypeElement {
+
+    /**
+     * A map with {@link ATypeElementWithType}s as values. When
+     * {@link VivifyingMap#vivify(Object)} method is called, a new
+     * {@code ATypeElementWithType} is constructed with the parameter to
+     * {@code vivify} passed to {@code ATypeElementWithType}'s constructor. This
+     * parameter is also used as the key into the map. This is used to map
+     * {@link ASTPath}s to their corresponding {@code ATypeElementWithType}.
+     * <p>
+     * {@code ATEWT} stands for {@code ATypeElementWithType}.
+     */
+    /*package-private*/ static <K extends Object> VivifyingMap<K, ATypeElementWithType> newVivifyingLHMap_ATEWT() {
+        return new VivifyingMap<K, ATypeElementWithType>(
+                new LinkedHashMap<K, ATypeElementWithType>()) {
+            @Override
+            public  ATypeElementWithType createValueFor(K k) {
+                return new ATypeElementWithType(k);
+            }
+
+            @Override
+            public boolean subPrune(ATypeElementWithType v) {
+                return v.prune();
+            }
+        };
+    }
+
+    /**
+     * The un-annotated type.
+     */
+    private Type type;
+
+    ATypeElementWithType(Object description) {
+        super(description);
+    }
+
+    ATypeElementWithType(ATypeElementWithType elem) {
+        super(elem);
+        type = elem.type;
+    }
+
+    @Override
+    public ATypeElementWithType clone() {
+        return new ATypeElementWithType(this);
+    }
+
+    /**
+     * Gets the un-annotated type.
+     *
+     * @return the un-annotated type
+     */
+    public Type getType() {
+        return type;
+    }
+
+    /**
+     * Sets the un-annotated type.
+     *
+     * @param type the un-annotated type
+     */
+    public void setType(Type type) {
+        this.type = type;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof ATypeElementWithType
+            && ((ATypeElementWithType) o).equals(this);
+    }
+
+    public boolean equalsTypeElementWithType(ATypeElementWithType o) {
+        return super.equals(o) && o.type.equals(type);
+    }
+
+    @Override
+    public int hashCode() {
+        return super.hashCode() + type.hashCode();
+    }
+
+    @Override
+    public boolean prune() {
+        boolean result = super.prune();
+        if (result && tlAnnotationsHere.isEmpty()) {
+            // If we're about to be pruned because we have no annotations, then
+            // stop the prune to just insert a cast with no annotations.
+            result = false;
+        }
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("AInsertTypecastTypeElement: ");
+        sb.append('\t');
+        sb.append(super.toString());
+        sb.append("type: " + type);
+        return sb.toString();
+    }
+
+    @Override
+    public <R, T> R accept(ElementVisitor<R, T> v, T t) {
+        return v.visitTypeElementWithType(this, t);
+    }
+}
diff --git a/scene-lib/src/annotations/el/AnnotationDef.java b/scene-lib/src/annotations/el/AnnotationDef.java
new file mode 100644
index 0000000..323f862
--- /dev/null
+++ b/scene-lib/src/annotations/el/AnnotationDef.java
@@ -0,0 +1,282 @@
+package annotations.el;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+import java.io.File;
+import java.util.*;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+
+import annotations.el.AElement;
+import annotations.AnnotationBuilder;
+import annotations.field.*;
+import annotations.Annotation;
+import annotations.Annotations;
+
+/**
+ * An annotation type definition, consisting of the annotation name,
+ * its meta-annotations, and its field names and
+ * types. <code>AnnotationDef</code>s are immutable.  An AnnotationDef with
+ * a non-null retention policy is called a "top-level annotation definition".
+ */
+public final class AnnotationDef extends AElement {
+
+    /**
+     * The binary name of the annotation type, such as
+     * "foo.Bar$Baz" for inner class Baz in class Bar in package foo.
+     */
+    public final String name;
+
+    /**
+     * A map of the names of this annotation type's fields to their types. Since
+     * {@link AnnotationDef}s are immutable, attempting to modify this
+     * map will result in an exception.
+     */
+    public Map<String, AnnotationFieldType> fieldTypes;
+
+    /**
+     * Constructs an annotation definition with the given name.
+     * You MUST call setFieldTypes afterward, even if with an empty map.  (Yuck.)
+     */
+    public AnnotationDef(String name) {
+        super("annotation: " + name);
+        assert name != null;
+        this.name = name;
+    }
+
+    @Override
+    public AnnotationDef clone() {
+        throw new UnsupportedOperationException("can't duplicate AnnotationDefs");
+    }
+
+    // Problem:  I am not sure how to handle circularities (annotations meta-annotated with themselves)
+    /**
+     * Look up an AnnotationDefs in adefs.
+     * If not found, read from a class and insert in adefs.
+     */
+    public static AnnotationDef fromClass(Class<? extends java.lang.annotation.Annotation> annoType, Map<String,AnnotationDef> adefs) {
+        String name = annoType.getName();
+        assert name != null;
+
+        if (adefs.containsKey(name)) {
+            return adefs.get(name);
+        }
+
+        Map<String,AnnotationFieldType> fieldTypes = new LinkedHashMap<String,AnnotationFieldType>();
+        for (Method m : annoType.getDeclaredMethods()) {
+            AnnotationFieldType aft = AnnotationFieldType.fromClass(m.getReturnType(), adefs);
+            fieldTypes.put(m.getName(), aft);
+        }
+
+        AnnotationDef result = new AnnotationDef(name, Annotations.noAnnotations, fieldTypes);
+        adefs.put(name, result);
+
+        // An annotation can be meta-annotated with itself, so add
+        // meta-annotations after putting the annotation in the map.
+        java.lang.annotation.Annotation[] jannos;
+        try {
+            jannos = annoType.getDeclaredAnnotations();
+        } catch (Exception e) {
+            printClasspath();
+            throw new Error("Exception in anno.getDeclaredAnnotations() for anno = " + annoType, e);
+        }
+        for (java.lang.annotation.Annotation ja : jannos) {
+            result.tlAnnotationsHere.add(new Annotation(ja, adefs));
+        }
+
+        return result;
+    }
+
+    public AnnotationDef(String name, Set<Annotation> tlAnnotationsHere) {
+        super("annotation: " + name);
+        assert name != null;
+        this.name = name;
+        if (tlAnnotationsHere != null) {
+            this.tlAnnotationsHere.addAll(tlAnnotationsHere);
+        }
+    }
+
+    public AnnotationDef(String name, Set<Annotation> tlAnnotationsHere, Map<String, ? extends AnnotationFieldType> fieldTypes) {
+        this(name, tlAnnotationsHere);
+        setFieldTypes(fieldTypes);
+    }
+
+    /**
+     * Constructs an annotation definition with the given name and field types.
+     * The field type map is copied and then wrapped in an
+     * {@linkplain Collections#unmodifiableMap unmodifiable map} to protect the
+     * immutability of the annotation definition.
+     * You MUST call setFieldTypes afterward, even if with an empty map.  (Yuck.)
+     */
+    public void setFieldTypes(Map<String, ? extends AnnotationFieldType> fieldTypes) {
+        this.fieldTypes = Collections.unmodifiableMap(
+                new LinkedHashMap<String, AnnotationFieldType>(fieldTypes)
+                );
+    }
+
+
+    /**
+     * The retention policy for annotations of this type.
+     * If non-null, this is called a "top-level" annotation definition.
+     * It may be null for annotations that are used only as a field of other
+     * annotations.
+     */
+    public /*@Nullable*/ RetentionPolicy retention() {
+        if (tlAnnotationsHere.contains(Annotations.aRetentionClass)) {
+            return RetentionPolicy.CLASS;
+        } else if (tlAnnotationsHere.contains(Annotations.aRetentionRuntime)) {
+            return RetentionPolicy.RUNTIME;
+        } else if (tlAnnotationsHere.contains(Annotations.aRetentionSource)) {
+            return RetentionPolicy.SOURCE;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * True if this is a type annotation (was meta-annotated
+     * with @Target(ElementType.TYPE_USE) or @TypeQualifier).
+     */
+    public boolean isTypeAnnotation() {
+        return (tlAnnotationsHere.contains(Annotations.aTargetTypeUse)
+                || tlAnnotationsHere.contains(Annotations.aTypeQualifier));
+    }
+
+
+    /**
+     * This {@link AnnotationDef} equals <code>o</code> if and only if
+     * <code>o</code> is another nonnull {@link AnnotationDef} and
+     * <code>this</code> and <code>o</code> define annotation types of the same
+     * name with the same field names and types.
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof AnnotationDef
+            && ((AnnotationDef) o).equals(this);
+    }
+
+    /**
+     * Returns whether this {@link AnnotationDef} equals <code>o</code>; a
+     * slightly faster variant of {@link #equals(Object)} for when the argument
+     * is statically known to be another nonnull {@link AnnotationDef}.
+     */
+    public boolean equals(AnnotationDef o) {
+        boolean sameName = name.equals(o.name);
+        boolean sameMetaAnnotations = equalsElement(o);
+        boolean sameFieldTypes = fieldTypes.equals(o.fieldTypes);
+        // Can be useful for debugging
+        if (false && sameName && (! (sameMetaAnnotations
+                            && sameFieldTypes))) {
+            String message = String.format("Warning: incompatible definitions of annotation %s%n  %s%n  %s%n",
+                                           name, this, o);
+            new Exception(message).printStackTrace(System.out);
+        }
+        return sameName
+            && sameMetaAnnotations
+            && sameFieldTypes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return name.hashCode()
+            // Omit tlAnnotationsHere, becase it should be unique and, more
+            // importantly, including it causes an infinite loop.
+            // + tlAnnotationsHere.hashCode()
+            + fieldTypes.hashCode();
+    }
+
+    /**
+     * Returns an <code>AnnotationDef</code> containing all the information
+     * from both arguments, or <code>null</code> if the two arguments
+     * contradict each other.  Currently this just
+     * {@linkplain AnnotationFieldType#unify unifies the field types}
+     * to handle arrays of unknown element type, which can arise via
+     * {@link AnnotationBuilder#addEmptyArrayField}.
+     */
+    public static AnnotationDef unify(AnnotationDef def1,
+            AnnotationDef def2) {
+        // if (def1.name.equals(def2.name)
+        //     && def1.fieldTypes.keySet().equals(def2.fieldTypes.keySet())
+        //     && ! def1.equalsElement(def2)) {
+        //     throw new Error(String.format("Unifiable except for meta-annotations:%n  %s%n  %s%n",
+        //                                   def1, def2));
+        // }
+        if (def1.equals(def2)) {
+            return def1;
+        } else if (def1.name.equals(def2.name)
+                 && def1.equalsElement(def2)
+                 && def1.fieldTypes.keySet().equals(def2.fieldTypes.keySet())) {
+            Map<String, AnnotationFieldType> newFieldTypes
+                = new LinkedHashMap<String, AnnotationFieldType>();
+            for (String fieldName : def1.fieldTypes.keySet()) {
+                AnnotationFieldType aft1 = def1.fieldTypes.get(fieldName);
+                AnnotationFieldType aft2 = def2.fieldTypes.get(fieldName);
+                AnnotationFieldType uaft = AnnotationFieldType.unify(aft1, aft2);
+                if (uaft == null) {
+                    return null;
+                } else {
+                    newFieldTypes.put(fieldName, uaft);
+                }
+            }
+            return new AnnotationDef(def1.name, def1.tlAnnotationsHere, newFieldTypes);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("[");
+        // Not: sb.append(((AElement) this).toString());
+        // because it causes an infinite loop.
+        boolean first;
+        first = true;
+        for (Annotation a : tlAnnotationsHere) {
+            if (!first) {
+                sb.append(" ");
+            } else {
+                first=false;
+            }
+            sb.append(a);
+        }
+        sb.append("] ");
+        sb.append("@");
+        sb.append(name);
+        sb.append("(");
+        first = true;
+        for (Map.Entry<String, AnnotationFieldType> entry : fieldTypes.entrySet()) {
+            if (!first) {
+                sb.append(",");
+            } else {
+                first = false;
+            }
+            sb.append(entry.getValue().toString());
+            sb.append(" ");
+            sb.append(entry.getKey());
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    public static void printClasspath() {
+        System.out.println();
+        System.out.println("Classpath:");
+        StringTokenizer tokenizer =
+            new StringTokenizer(System.getProperty("java.class.path"), File.pathSeparator);
+        while (tokenizer.hasMoreTokens()) {
+            String cpelt = tokenizer.nextToken();
+            boolean exists = new File(cpelt).exists();
+            if (! exists) {
+                System.out.print(" non-existent:");
+            }
+            System.out.println("  " + cpelt);
+        }
+    }
+
+}
diff --git a/scene-lib/src/annotations/el/BoundLocation.java b/scene-lib/src/annotations/el/BoundLocation.java
new file mode 100644
index 0000000..7d0a47e
--- /dev/null
+++ b/scene-lib/src/annotations/el/BoundLocation.java
@@ -0,0 +1,74 @@
+package annotations.el;
+
+import annotations.util.Hasher;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A {@link BoundLocation} holds location information for a bound of a type
+ * parameter of a class or method: parameter index and bound index.
+ * It also handles type parameters themselves (not just the bound part).
+ * It would be better named "TypeParameterLocation", or the two uses could
+ * be separated out.
+ */
+public final class BoundLocation {
+    /**
+     * The index of the parameter to which the bound applies among all
+     * type parameters of the class or method.
+     */
+    public final int paramIndex;
+
+    /**
+     * The index of the bound among all bounds on the type parameter.
+     * -1 if for the type parameter itself.
+     */
+    public final int boundIndex;
+
+    /**
+     * Constructs a new {@link BoundLocation}; the arguments are assigned to
+     * the fields of the same names.
+     */
+    public BoundLocation(int paramIndex, int boundIndex) {
+        this.paramIndex = paramIndex;
+        this.boundIndex = boundIndex;
+    }
+
+    /**
+     * Returns whether this {@link BoundLocation} equals <code>o</code>; a
+     * slightly faster variant of {@link #equals(Object)} for when the argument
+     * is statically known to be another nonnull {@link BoundLocation}.
+     */
+    public boolean equals(BoundLocation l) {
+        return paramIndex == l.paramIndex && boundIndex == l.boundIndex;
+    }
+
+    /**
+     * This {@link BoundLocation} equals <code>o</code> if and only if
+     * <code>o</code> is another nonnull {@link BoundLocation} and
+     * <code>this</code> and <code>o</code> have equal {@link #paramIndex}
+     * and {@link #boundIndex}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof BoundLocation
+                && equals((BoundLocation) o);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        Hasher h = new Hasher();
+        h.mash(paramIndex);
+        h.mash(boundIndex);
+        return h.hash;
+    }
+
+    @Override
+    public String toString() {
+        return "BoundLocation(" + paramIndex + "," + boundIndex + ")";
+    }
+}
diff --git a/scene-lib/src/annotations/el/DefCollector.java b/scene-lib/src/annotations/el/DefCollector.java
new file mode 100644
index 0000000..2a155fb
--- /dev/null
+++ b/scene-lib/src/annotations/el/DefCollector.java
@@ -0,0 +1,210 @@
+package annotations.el;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import annotations.Annotation;
+import annotations.field.AnnotationAFT;
+import annotations.field.AnnotationFieldType;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A DefCollector supplies a visitor for the annotation definitions in an
+ * AScene.  First, call the DefCollector constructor passing the AScene.
+ * Then, call the visit method.
+ * This class exists primarily for the benefit of
+ * {@link annotations.io.IndexFileWriter#write(AScene, Writer)}.
+ */
+public abstract class DefCollector {
+
+    // The set of all definitions in the Scene, which the visitor iterates
+    // over.
+    private final Set<AnnotationDef> defs;
+
+    /**
+     * Constructs a new {@link DefCollector}, which immediately collects all
+     * the definitions from annotations the given scene.  Next call
+     * {@link #visit} to have the definitions passed back to you in topological
+     * order.  If the scene contains two irreconcilable definitions of the
+     * same annotation type, a {@link DefException} is thrown.
+     */
+    public DefCollector(AScene s)
+            throws DefException {
+        defs = new LinkedHashSet<AnnotationDef>();
+        collect(s);
+    }
+
+    // The name "collect" in the methods below means to insert or add to
+    // the the DefCollector.  "Insert" or "add" would have been better, but
+    // at least the methods are private.
+
+    private AnnotationDef getDef(String name) {
+        for (AnnotationDef def : defs) {
+            if (def.name.equals(name)) {
+                return def;
+            }
+        }
+        return null;
+    }
+
+
+    private void collect(AScene s)
+            throws DefException {
+        for (AElement p : s.packages.values()) {
+            collect(p);
+        }
+        for (AClass c : s.classes.values()) {
+            collect(c);
+        }
+    }
+
+    private void addToDefs(AnnotationDef d) throws DefException {
+        // TODO: this mimics the condition we have in collect, but
+        // i don't know if we need it
+        if (defs.contains(d)) {
+            return;
+        }
+        AnnotationDef oldD = getDef(d.name);
+        if (oldD == null) {
+            defs.add(d);
+        } else {
+            AnnotationDef ud = AnnotationDef.unify(oldD, d);
+            if (ud == null) {
+                throw new DefException(d.name);
+            }
+            defs.remove(oldD);
+            defs.add(ud);
+        }
+    }
+
+    private void collect(AnnotationDef d) throws DefException {
+        if (defs.contains(d)) {
+            return;
+        }
+
+        // define the fields first
+        for (AnnotationFieldType aft : d.fieldTypes.values()) {
+            if (aft instanceof AnnotationAFT) {
+                collect(((AnnotationAFT) aft).annotationDef);
+            }
+        }
+
+        addToDefs(d);
+
+        // TODO: In the future we want to add the defs of meta-annotations
+        // as well.  Enable this option by uncommenting the following line.
+        //
+        // For the time-being, the parser would fail, because of possible
+        // circular references (e.g. Documented and Retention).  When it is
+        // fixed, uncomment it
+        //
+        // collect((AElement)d);
+    }
+
+    private void collect(AElement e)
+            throws DefException {
+        for (Annotation tla : e.tlAnnotationsHere) {
+            AnnotationDef tld = tla.def;
+            if (defs.contains(tld)) {
+                continue;
+            }
+
+            AnnotationDef d = tld;
+            collect(d);
+
+            addToDefs(d);
+        }
+        if (e.type != null) {
+            collect(e.type);
+        }
+
+    }
+
+    private void collect(ATypeElement e)
+            throws DefException {
+        collect((AElement) e);
+        for (AElement it : e.innerTypes.values()) {
+            collect(it);
+        }
+    }
+
+    private void collect(ADeclaration d)
+            throws DefException {
+        collect((AElement) d);
+        for (ATypeElement ia : d.insertAnnotations.values()) {
+            collect(ia);
+        }
+        for (ATypeElementWithType ic : d.insertTypecasts.values()) {
+            collect(ic);
+        }
+    }
+
+    private void collect(AField f)
+            throws DefException {
+        collect((ADeclaration) f);
+    }
+
+    private void collect(AMethod m)
+            throws DefException {
+        for (ATypeElement b : m.bounds.values()) {
+            collect(b);
+        }
+        collect((ADeclaration) m);
+        collect((ATypeElement) m.returnType);
+        collect(m.receiver);
+        for (AElement p : m.parameters.values()) {
+            collect(p);
+        }
+        for (AField l : m.body.locals.values()) {
+            collect(l);
+        }
+        for (ATypeElement tc : m.body.typecasts.values()) {
+            collect(tc);
+        }
+        for (ATypeElement i : m.body.instanceofs.values()) {
+            collect(i);
+        }
+        for (ATypeElement n : m.body.news.values()) {
+            collect(n);
+        }
+    }
+
+    private void collect(AClass c)
+            throws DefException {
+        collect((ADeclaration) c);
+        for (ATypeElement b : c.bounds.values()) {
+            collect(b);
+        }
+        for (ATypeElement ei : c.extendsImplements.values()) {
+            collect(ei);
+        }
+        for (AMethod m : c.methods.values()) {
+            collect(m);
+        }
+        for (AField f : c.fields.values()) {
+            collect(f);
+        }
+    }
+
+    /**
+     * Override this method to perform some sort of subclass-specific
+     * processing on the given {@link AnnotationDef}.
+     */
+    protected abstract void visitAnnotationDef(AnnotationDef d);
+
+    /**
+     * Calls {@link #visitAnnotationDef} on the definitions collected from
+     * the scene that was passed to the constructor.  Visiting is done in
+     * topological order:  if the definition of <code>A</code> contains a
+     * subannotation of type <code>B</code>, then <code>B</code> is
+     * guaranteed to be visited before <code>A</code>.
+     */
+    public final void visit() {
+        for (AnnotationDef d : defs) {
+            visitAnnotationDef(d);
+        }
+    }
+}
diff --git a/scene-lib/src/annotations/el/DefException.java b/scene-lib/src/annotations/el/DefException.java
new file mode 100644
index 0000000..96df426
--- /dev/null
+++ b/scene-lib/src/annotations/el/DefException.java
@@ -0,0 +1,24 @@
+package annotations.el;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * Thrown by {@link DefCollector} if the scene contains two different
+ * definitions of the same annotation type that cannot be
+ * {@linkplain AnnotationDef#unify unified}.
+ */
+public class DefException extends Exception {
+    private static final long serialVersionUID = 1152640422L;
+
+    /**
+     * The name of the annotation type that had two conflicting definitions.
+     */
+    public final String annotationType;
+
+    DefException(String annotationType) {
+        super("Conflicting definition of annotation type " + annotationType);
+        this.annotationType = annotationType;
+    }
+}
diff --git a/scene-lib/src/annotations/el/ElementVisitor.java b/scene-lib/src/annotations/el/ElementVisitor.java
new file mode 100644
index 0000000..ed297a4
--- /dev/null
+++ b/scene-lib/src/annotations/el/ElementVisitor.java
@@ -0,0 +1,17 @@
+package annotations.el;
+
+/**
+ * @author dbro
+ */
+public interface ElementVisitor<R, T> {
+  R visitAnnotationDef(AnnotationDef el, T arg);
+  R visitBlock(ABlock el, T arg);
+  R visitClass(AClass el, T arg);
+  R visitDeclaration(ADeclaration el, T arg);
+  R visitExpression(AExpression el, T arg);
+  R visitField(AField el, T arg);
+  R visitMethod(AMethod el, T arg);
+  R visitTypeElement(ATypeElement el, T arg);
+  R visitTypeElementWithType(ATypeElementWithType el, T arg);
+  R visitElement(AElement el, T arg);
+}
diff --git a/scene-lib/src/annotations/el/InnerTypeLocation.java b/scene-lib/src/annotations/el/InnerTypeLocation.java
new file mode 100644
index 0000000..179b98d
--- /dev/null
+++ b/scene-lib/src/annotations/el/InnerTypeLocation.java
@@ -0,0 +1,87 @@
+package annotations.el;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import annotations.util.Hasher;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * An {@link InnerTypeLocation} holds the location information for an
+ * inner type (namely the location string) inside its {@link ATypeElement}.
+ */
+public final class InnerTypeLocation {
+
+    /**
+     * An {@link InnerTypeLocation} containing no locations.
+     */
+    public static final InnerTypeLocation EMPTY_INNER_TYPE_LOCATION = new InnerTypeLocation(
+            Collections.<TypePathEntry> emptyList());
+
+    /**
+     * The location numbers of the inner type as defined in the extended
+     * annotation specification.  For example, the location numbers of &#064;X
+     * in <code>Foo&lt;Bar&lt;Baz, &#064;X Baz&gt;&gt;</code> are
+     * <code>{0, 1}</code>.
+     */
+    public final List<TypePathEntry> location;
+
+    /**
+     * Constructs an {@link InnerTypeLocation} from the given location string,
+     * which must not be zero-length.  (The "inner type" of an
+     * {@link ATypeElement} with zero-length location string is the
+     * {@link ATypeElement} itself.)
+     */
+    public InnerTypeLocation(List<TypePathEntry> location) {
+        this.location = Collections.unmodifiableList(
+                new ArrayList<TypePathEntry>(location));
+    }
+
+    /**
+     * Returns whether this {@link InnerTypeLocation} equals <code>o</code>; a
+     * slightly faster variant of {@link #equals(Object)} for when the argument
+     * is statically known to be another nonnull {@link InnerTypeLocation}.
+     */
+    public boolean equals(InnerTypeLocation l) {
+        return location.equals(l.location);
+    }
+
+    /**
+     * This {@link InnerTypeLocation} equals <code>o</code> if and only if
+     * <code>o</code> is another nonnull {@link InnerTypeLocation} and
+     * <code>this</code> and <code>o</code> have equal location strings
+     * {@link #location}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof InnerTypeLocation
+                && equals((InnerTypeLocation) o);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        Hasher h = new Hasher();
+        h.mash(location.hashCode());
+        return h.hash;
+    }
+
+    /**
+     * Returns a string representation of this {@link InnerTypeLocation}.
+     * The representation looks like this:
+     *
+     * <pre>InnerTypeLocation([0, 1, 2])</pre>
+     */
+    @Override
+    public  String toString() {
+        return "InnerTypeLocation(" + location.toString() + ")";
+    }
+}
diff --git a/scene-lib/src/annotations/el/LocalLocation.java b/scene-lib/src/annotations/el/LocalLocation.java
new file mode 100644
index 0000000..55bca23
--- /dev/null
+++ b/scene-lib/src/annotations/el/LocalLocation.java
@@ -0,0 +1,103 @@
+package annotations.el;
+
+import annotations.util.Hasher;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A {@link LocalLocation} holds location information for a local
+ * variable: slot index, scope start, and scope length.
+ */
+public final class LocalLocation {
+    /**
+     * The slot index of the local variable.
+     */
+    public final int index;
+
+    /**
+     * The start of the local variable's scope (or live range), as an offset
+     * from the beginning of the method code in bytes.
+     */
+    public final int scopeStart;
+
+    /**
+     * The length of the local variable's scope (or live range), in bytes.
+     */
+    public final int scopeLength;
+
+    /**
+     * Constructs a new {@link LocalLocation}; the arguments are assigned to
+     * the fields of the same names.
+     */
+    public LocalLocation(int index, int scopeStart, int scopeLength) {
+        this.index = index;
+        this.scopeStart = scopeStart;
+        this.scopeLength = scopeLength;
+        this.varName = null;
+        this.varIndex = -1;
+    }
+
+    public final String varName;
+    public final int varIndex;
+
+    public LocalLocation(String varName, int varIndex) {
+        this.index = -1;
+        this.scopeStart = -1;
+        this.scopeLength = -1;
+        this.varName = varName;
+        this.varIndex = varIndex;
+    }
+
+
+    /**
+     * Returns whether this {@link LocalLocation} equals <code>o</code>; a
+     * slightly faster variant of {@link #equals(Object)} for when the argument
+     * is statically known to be another nonnull {@link LocalLocation}.
+     */
+    public boolean equals(LocalLocation l) {
+        return index == l.index && scopeStart == l.scopeStart
+                && scopeLength == l.scopeLength &&
+                (varName==null || varName.equals(l.varName)) &&
+                varIndex==l.varIndex;
+    }
+
+    /**
+     * This {@link LocalLocation} equals <code>o</code> if and only if
+     * <code>o</code> is another nonnull {@link LocalLocation} and
+     * <code>this</code> and <code>o</code> have equal {@link #index},
+     * {@link #scopeStart}, and {@link #scopeLength}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof LocalLocation
+                && equals((LocalLocation) o);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        Hasher h = new Hasher();
+        if (varName==null) {
+            h.mash(index);
+            h.mash(scopeStart);
+            h.mash(scopeLength);
+        } else {
+            h.mash(varName.hashCode());
+            h.mash(varIndex);
+        }
+        return h.hash;
+    }
+
+    @Override
+    public String toString() {
+        if (varName==null) {
+            return "LocalLocation(" + index + ", " + scopeStart + ", " + scopeLength + ")";
+        } else {
+            return "LocalLocation(\"" + varName + "\" #" + varIndex + ")";
+        }
+    }
+}
diff --git a/scene-lib/src/annotations/el/RelativeLocation.java b/scene-lib/src/annotations/el/RelativeLocation.java
new file mode 100644
index 0000000..8f77d9e
--- /dev/null
+++ b/scene-lib/src/annotations/el/RelativeLocation.java
@@ -0,0 +1,109 @@
+package annotations.el;
+
+import annotations.util.Hasher;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A {@link RelativeLocation} holds location information for a
+ * instanceof, cast, or new: either the bytecode offset or the source code index.
+ * I call instanceof, cast, or new "the construct".
+ */
+public final class RelativeLocation implements Comparable<RelativeLocation> {
+    /**
+     * The bytecode offset of the construct.
+     */
+    public final int offset;
+
+    /**
+     * The source code index of the construct.
+     */
+    public final int index;
+
+    /**
+     * The type index used for intersection types in casts.
+     */
+    public final int type_index;
+
+    /**
+     * Constructs a new {@link RelativeLocation}; the arguments are assigned to
+     * the fields of the same names.
+     * Use -1 for the relative location that you're not using
+     */
+    private RelativeLocation(int offset, int index, int type_index) {
+        this.offset = offset;
+        this.index = index;
+        this.type_index = type_index;
+    }
+
+    public static RelativeLocation createOffset(int offset, int type_index) {
+        return new RelativeLocation(offset, -1, type_index);
+    }
+
+    public static RelativeLocation createIndex(int index, int type_index) {
+        return new RelativeLocation(-1, index, type_index);
+    }
+
+    public boolean isBytecodeOffset() {
+        return offset>-1;
+    }
+
+    public String getLocationString() {
+        if (isBytecodeOffset()) {
+            return "#" + offset;
+        } else {
+            return "*" + index;
+        }
+    }
+
+    @Override
+    public int compareTo(RelativeLocation l) {
+        int c = Integer.compare(offset,  l.offset);
+        if (c == 0) {
+            c = Integer.compare(index, l.index);
+            if (c == 0) {
+                c = Integer.compare(type_index, l.type_index);
+            }
+        }
+        return c;
+    }
+
+    /**
+     * Returns whether this {@link RelativeLocation} equals <code>o</code>; a
+     * slightly faster variant of {@link #equals(Object)} for when the argument
+     * is statically known to be another nonnull {@link RelativeLocation}.
+     */
+    public boolean equals(RelativeLocation l) {
+        return compareTo(l) == 0;
+    }
+
+    /**
+     * This {@link RelativeLocation} equals <code>o</code> if and only if
+     * <code>o</code> is another nonnull {@link RelativeLocation} and
+     * <code>this</code> and <code>o</code> have equal {@link #offset} and {@link #index}.
+     */
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof RelativeLocation
+                && equals((RelativeLocation) o);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        Hasher h = new Hasher();
+        h.mash(offset);
+        h.mash(index);
+        h.mash(type_index);
+        return h.hash;
+    }
+
+    @Override
+    public String toString() {
+        return "RelativeLocation(" + getLocationString() + ")";
+    }
+}
diff --git a/scene-lib/src/annotations/el/TypeASTMapper.java b/scene-lib/src/annotations/el/TypeASTMapper.java
new file mode 100644
index 0000000..ac2a7ad
--- /dev/null
+++ b/scene-lib/src/annotations/el/TypeASTMapper.java
@@ -0,0 +1,151 @@
+package annotations.el;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntryKind;
+
+/**
+ * A {@link TypeASTMapper} traverses a client-maintained abstract syntax
+ * tree representing a type in parallel with an {@link ATypeElement} from
+ * the annotation scene library, indicating corresponding pairs of AST nodes and
+ * {@link AElement}s to the client so the client can process them in some
+ * fashion.
+ *
+ * <p>
+ * To use {@link TypeASTMapper}, write a subclass for your particular AST.
+ * Implement {@link #getElementType}, {@link #numTypeArguments}, and
+ * {@link #getTypeArgument} so that the mapper knows how to traverse your
+ * AST; implement {@link #map} to perform whatever processing you desire on
+ * each AST node and its corresponding {@link AElement}.  Then, pass the root
+ * of your AST and the corresponding {@link ATypeElement} from your
+ * annotation scene to {@link #traverse}.
+ *
+ * <p>
+ * {@link TypeASTMapper} itself saves no state, but subclasses may save state
+ * if they wish.
+ *
+ * {@link ATypeElement} objects that will be traversed
+ * @param <N> common supertype of the AST nodes
+ */
+public abstract class TypeASTMapper<N> {
+    /**
+     * Constructs a {@link TypeASTMapper}.  A {@link TypeASTMapper} stores no
+     * state.
+     */
+    protected TypeASTMapper() {
+    }
+
+    private static ATypeElement getInnerType(ATypeElement te,
+                                             List<TypePathEntry> ls) {
+        if (ls.isEmpty()) {
+            return te;
+        } else {
+            return te.innerTypes.vivify(new InnerTypeLocation(ls));
+        }
+    }
+
+    /**
+     * Traverses the type AST rooted at <code>tastRoot</code>, calling
+     * {@link #map} with each AST node and the corresponding {@link AElement}
+     * from <code>aslRoot</code>.
+     *
+     * If a node of the AST has no corresponding inner type in
+     * <code>aslRoot</code>, an inner type {@link AElement} is vivified to hold
+     * any annotations that {@link #map} might wish to store in it.  Thus,
+     * a call to {@link #traverse} may write to <code>aslRoot</code> even if
+     * {@link #map} does not write to its {@link AElement} argument.  You
+     * may wish to {@linkplain AElement#prune prune} <code>aslRoot</code> after
+     * traversal.
+     */
+    public void traverse(N tastRoot, ATypeElement aslRoot) {
+        // Elements are added and removed from the end of this sole mutable
+        // list during the traversal.
+        List<TypePathEntry> ls = new ArrayList<TypePathEntry>();
+        traverse1(tastRoot, aslRoot, ls);
+    }
+
+    // "Sane": top-level or type argument
+    private void traverse1(N n, ATypeElement te, List<TypePathEntry> ls) {
+        N elType = getElementType(n);
+        if (elType == null) {
+            // no array, so the prefix corresponds to the type right here
+            // System.out.printf("non-array: map(%s, getInnerType(%s, %s)=%s)%n",
+            //                   n, te, ls, getInnerType(te, ls));
+            map(n, getInnerType(te, ls));
+            int nta = numTypeArguments(n);
+            for (int tai = 0; tai < nta; tai++) {
+                ls.add(new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, tai));
+                traverse1(getTypeArgument(n, tai), te, ls);
+                ls.remove(ls.size() - 1);
+            }
+        } else {
+            // System.out.printf("array top-level: map(%s, getInnerType(%s, %s)=%s)%n",
+            //                   n, te, ls, getInnerType(te, ls));
+            map(n, getInnerType(te, ls));
+
+            // at least one array layer to confuse us
+            int layers = 0;
+            while ((elType = getElementType(n)) != null) {
+                ls.add(TypePathEntry.ARRAY);
+                // System.out.printf("layers=%d, map(%s, getInnerType(%s, %s)=%s)%n",
+                //                   layers, elType, te, ls, getInnerType(te, ls));
+                map(elType, getInnerType(te, ls));
+                n = elType;
+                layers++;
+            }
+            // // n is now the innermost element type
+            // // map it to the prefix
+            // map(n, getInnerType(te, ls));
+
+            int nta = numTypeArguments(n);
+            for (int tai = 0; tai < nta; tai++) {
+                ls.add(new TypePathEntry(TypePathEntryKind.TYPE_ARGUMENT, tai));
+                traverse1(getTypeArgument(n, tai), te, ls);
+                ls.remove(ls.size() - 1);
+            }
+
+            for (int i = 0; i < layers; i++) {
+                ls.remove(ls.size() - 1);
+            }
+        }
+    }
+
+    /**
+     * If <code>n</code> represents an array type, {@link #getElementType}
+     * returns the node for the element type of the array; otherwise it returns
+     * <code>null</code>.
+     */
+    protected abstract N getElementType(N n);
+
+    /**
+     * If <code>n</code> represents a parameterized type,
+     * {@link #numTypeArguments} returns the number of type arguments;
+     * otherwise it returns 0.
+     */
+    protected abstract int numTypeArguments(N n);
+
+    /**
+     * Returns the node corresponding to the type argument of <code>n</code>
+     * (which must be a parameterized type) at the given index.  The caller
+     * must ensure that
+     * <code>0 &lt;= index &lt; {@link #numTypeArguments}(n)</code>.
+     */
+    protected abstract  N getTypeArgument(N n, int index);
+
+    /**
+     * Signals to the client that <code>n</code> corresponds to <code>e</code>.
+     * The client may, for example, set flags in <code>n</code> based on the
+     * annotations in
+     * <code>e.{@link AElement#tlAnnotationsHere tlAnnotationsHere}</code>.
+     * The {@link TypeASTMapper} calls {@link #map} on <code>n</code> before it
+     * calls {@link #map} on sub-nodes of <code>n</code> but not necessarily
+     * before it explores the structure of <code>n</code>'s subtree.
+     */
+    protected abstract void map(N n, ATypeElement e);
+}
diff --git a/scene-lib/src/annotations/el/TypeIndexLocation.java b/scene-lib/src/annotations/el/TypeIndexLocation.java
new file mode 100644
index 0000000..bce02c5
--- /dev/null
+++ b/scene-lib/src/annotations/el/TypeIndexLocation.java
@@ -0,0 +1,39 @@
+package annotations.el;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import annotations.util.Hasher;
+
+public class TypeIndexLocation {
+  public final int typeIndex;
+
+  public TypeIndexLocation(int typeIndex) {
+      this.typeIndex = typeIndex;
+  }
+
+  public boolean equals(TypeIndexLocation l) {
+      return typeIndex == l.typeIndex;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+      return o instanceof TypeIndexLocation
+              && equals((TypeIndexLocation) o);
+  }
+
+  /** {@inheritDoc} */
+  @Override
+  public int hashCode() {
+      Hasher h = new Hasher();
+      h.mash(typeIndex);
+      return h.hash;
+  }
+
+  @Override
+  public String toString() {
+      return "TypeIndexLocation(" + typeIndex + ")";
+  }
+
+}
diff --git a/scene-lib/src/annotations/el/package-info.java b/scene-lib/src/annotations/el/package-info.java
new file mode 100644
index 0000000..474ac70
--- /dev/null
+++ b/scene-lib/src/annotations/el/package-info.java
@@ -0,0 +1,21 @@
+/**
+ * <code>annotations.el</code> provides classes that associate annotations with
+ * Java elements.  {@link annotations.el.AElement}s represent Java elements
+ * of the scene that can carry annotations. There is a multi-level class
+ * hierarchy for elements that exploits certain commonalities: for example, all
+ * and only {@link annotations.el.ADeclaration declarations} contain an
+ * {@link annotations.el.ADeclaration#insertTypecasts insertTypecasts} field.
+ * A &ldquo;scene&rdquo; ({@link annotations.el.AScene}) contains many elements
+ * and represents all the annotations on a set of classes and packages.
+ *
+ * One related utility class that is important to understand is
+ * {@link annotations.util.coll.VivifyingMap}, a Map implementation that allows
+ * empty entries (for some user-defined meaning of
+ * {@link annotations.util.coll.VivifyingMap#isEmpty() empty}) and provides a
+ * {@link annotations.util.coll.VivifyingMap#prune() prune} method to eliminate
+ * these entries.  The only way to create any element is to invoke
+ * {@link annotations.util.coll.VivifyingMap#vivify(Object) vivify()} on a
+ * {@link annotations.util.coll.VivifyingMap} static member of the appropriate
+ * {@link annotations.el.AElement} superclass.
+ */
+package annotations.el;
diff --git a/scene-lib/src/annotations/field/AFTVisitor.java b/scene-lib/src/annotations/field/AFTVisitor.java
new file mode 100644
index 0000000..7a81669
--- /dev/null
+++ b/scene-lib/src/annotations/field/AFTVisitor.java
@@ -0,0 +1,12 @@
+package annotations.field;
+
+/**
+ * @author dbro
+ */
+public interface AFTVisitor<R, T> {
+  R visitAnnotationAFT(AnnotationAFT aft, T arg);
+  R visitArrayAFT(ArrayAFT aft, T arg);
+  R visitBasicAFT(BasicAFT aft, T arg);
+  R visitClassTokenAFT(ClassTokenAFT aft, T arg);
+  R visitEnumAFT(EnumAFT aft, T arg);
+}
diff --git a/scene-lib/src/annotations/field/AnnotationAFT.java b/scene-lib/src/annotations/field/AnnotationAFT.java
new file mode 100644
index 0000000..08bd6be
--- /dev/null
+++ b/scene-lib/src/annotations/field/AnnotationAFT.java
@@ -0,0 +1,61 @@
+package annotations.field;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import annotations.Annotation;
+import annotations.el.AnnotationDef;
+
+/**
+ * An {@link AnnotationAFT} represents a subannotation as the type of an
+ * annotation field and contains the definition of the subannotation.
+ */
+public final class AnnotationAFT extends ScalarAFT {
+
+    /**
+     * The definition of the subannotation.
+     */
+    public final AnnotationDef annotationDef;
+
+    /**
+     * Constructs a new {@link AnnotationAFT} for a subannotation of the
+     * given definition.
+     */
+    public AnnotationAFT(AnnotationDef annotationDef) {
+        this.annotationDef = annotationDef;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isValidValue(Object o) {
+        return o instanceof Annotation;
+    }
+
+    /**
+     * The string representation of an {@link AnnotationAFT} looks like
+     * <code>&#64;Foo</code> even though the subannotation definition is
+     * logically part of the {@link AnnotationAFT}.  This is because the
+     * subannotation field type appears as <code>&#64;Foo</code> in an
+     * index file and the subannotation definition is written separately.
+     */
+    @Override
+    public  String toString() {
+        return "annotation-field " + annotationDef.name;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String format(Object o) {
+        return o.toString();
+    }
+
+    @Override
+    public <R, T> R accept(AFTVisitor<R, T> v, T arg) {
+        return v.visitAnnotationAFT(this, arg);
+    }
+}
diff --git a/scene-lib/src/annotations/field/AnnotationFieldType.java b/scene-lib/src/annotations/field/AnnotationFieldType.java
new file mode 100644
index 0000000..8fe3215
--- /dev/null
+++ b/scene-lib/src/annotations/field/AnnotationFieldType.java
@@ -0,0 +1,94 @@
+package annotations.field;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import annotations.el.AnnotationDef;
+import annotations.util.EqualByStringRepresentation;
+
+import java.util.Map;
+
+/**
+ * An {@link AnnotationFieldType} represents a type that can be the type
+ * of an annotation field. Each subclass represents one kind of type allowed by
+ * the Java language.
+ */
+public abstract class AnnotationFieldType extends EqualByStringRepresentation {
+
+    /**
+     * Returns the string representation of the type that would appear in an
+     * index file. Used by {@link annotations.io.IndexFileWriter}.
+     */
+    @Override
+    public abstract String toString();
+
+
+    /** Formats an annotation field value. */
+    public abstract String format(Object o);
+
+
+    /** Returns true if this value is valid for this AnnotationFieldType. */
+    public abstract boolean isValidValue(Object o);
+
+    // TODO: add a cache?
+    public static AnnotationFieldType fromClass(Class<?> c, Map<String,AnnotationDef> adefs) {
+        if (c.isAnnotation()) {
+            Class<? extends java.lang.annotation.Annotation> cAnno
+                = (Class<? extends java.lang.annotation.Annotation>) c;
+            return new AnnotationAFT(AnnotationDef.fromClass(cAnno, adefs));
+        } else if (c.isArray()) {
+            return new ArrayAFT((ScalarAFT) fromClass(c.getComponentType(), adefs));
+        } else if (BasicAFT.bafts.containsKey(c)) {
+            return BasicAFT.bafts.get(c);
+        } else if (c == Class.class) {
+            return ClassTokenAFT.ctaft;
+        } else if (c.isEnum()) {
+            return new EnumAFT(c.getName());
+        } else {
+            throw new Error("Unrecognized class: " + c);
+        }
+    }
+
+
+    /**
+     * Returns an {@link AnnotationFieldType} containing all the
+     * information from both arguments, or <code>null</code> if the two
+     * arguments contradict each other.
+     *
+     * <p>
+     * Currently this just merges the {@link ArrayAFT#elementType} field, so
+     * that if both arguments are {@link ArrayAFT}s, one of known element type
+     * and the other of unknown element type, an {@link ArrayAFT} of the known
+     * element type is returned.  Furthermore, if both arguments are
+     * {@link AnnotationAFT}s, the sub-definitions might directly or
+     * indirectly contain {@link ArrayAFT}s, so {@link AnnotationDef#unify} is
+     * called to unify the sub-definitions recursively.
+     */
+    public static final AnnotationFieldType unify(AnnotationFieldType aft1, AnnotationFieldType aft2) {
+        if (aft1.equals(aft2)) {
+            return aft1;
+        } else if (aft1 instanceof ArrayAFT && aft2 instanceof ArrayAFT) {
+            if (((ArrayAFT) aft1).elementType == null) {
+                return aft2;
+            } else if (((ArrayAFT) aft2).elementType == null) {
+                return aft1;
+            } else {
+                return null;
+            }
+        } else if (aft1 instanceof AnnotationAFT && aft2 instanceof AnnotationAFT) {
+            AnnotationDef ud = AnnotationDef.unify(
+                    ((AnnotationAFT) aft1).annotationDef,
+                    ((AnnotationAFT) aft2).annotationDef);
+            if (ud == null) {
+                return null;
+            } else {
+                return new AnnotationAFT(ud);
+            }
+        } else {
+            return null;
+        }
+    }
+
+    public abstract <R, T> R accept(AFTVisitor<R, T> v, T arg);
+}
diff --git a/scene-lib/src/annotations/field/ArrayAFT.java b/scene-lib/src/annotations/field/ArrayAFT.java
new file mode 100644
index 0000000..14fa805
--- /dev/null
+++ b/scene-lib/src/annotations/field/ArrayAFT.java
@@ -0,0 +1,81 @@
+package annotations.field;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.Collection;
+
+/**
+ * An {@link ArrayAFT} represents an annotation field type that is an array.
+ */
+public final class ArrayAFT extends AnnotationFieldType {
+
+    /**
+     * The element type of the array, or <code>null</code> if it is unknown
+     * (see {@link annotations.AnnotationBuilder#addEmptyArrayField}).
+     */
+    public final ScalarAFT elementType;
+
+    /**
+     * Constructs a new {@link ArrayAFT} representing an array type with
+     * the given element type.  <code>elementType</code> may be
+     * <code>null</code> to indicate that the element type is unknown
+     * (see {@link annotations.AnnotationBuilder#addEmptyArrayField}).
+     */
+    public ArrayAFT(ScalarAFT elementType) {
+        this.elementType = elementType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isValidValue(Object o) {
+        if (! (o instanceof Collection)) {
+            return false;
+        }
+        Collection<?> asCollection = (Collection<?>) o;
+        if (elementType == null) {
+            return (asCollection.size() == 0);
+        }
+        for (Object elt : asCollection) {
+            if (! elementType.isValidValue(elt)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public  String toString() {
+        return (elementType == null ? "unknown" :
+            ((ScalarAFT) elementType).toString()) + "[]";
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String format(Object o) {
+        Collection<?> asCollection = (Collection<?>)o;
+        StringBuilder result = new StringBuilder();
+        result.append("{");
+        for (Object elt : asCollection) {
+            if (result.length() > 1) {
+                result.append(",");
+            }
+            result.append(elementType.format(elt));
+        }
+        result.append("}");
+        return result.toString();
+    }
+
+    @Override
+    public <R, T> R accept(AFTVisitor<R, T> v, T arg) {
+        return v.visitArrayAFT(this, arg);
+    }
+}
diff --git a/scene-lib/src/annotations/field/BasicAFT.java b/scene-lib/src/annotations/field/BasicAFT.java
new file mode 100644
index 0000000..cb8939d
--- /dev/null
+++ b/scene-lib/src/annotations/field/BasicAFT.java
@@ -0,0 +1,115 @@
+package annotations.field;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.common.escape.CharEscaperBuilder;
+import com.google.common.escape.Escaper;
+
+/**
+ * A <code>BasicAFT</code> represents a primitive or {@link String} annotation
+ * field type. Get one using {@link #forType(Class)}.
+ */
+// should be an enum except they can't be generic and can't extend a class
+public final class BasicAFT extends ScalarAFT {
+    static final Escaper charEscaper =
+        new CharEscaperBuilder()
+            .addEscape('\b', "\\b")
+            .addEscape('\f', "\\f")
+            .addEscape('\n', "\\n")
+            .addEscape('\r', "\\r")
+            .addEscape('\t', "\\t")
+            .addEscape('\"', "\\\"")
+            .addEscape('\\', "\\\\")
+            .addEscape('\'', "\\'")
+            .toEscaper();
+
+    /**
+     * The Java type backing this annotation field type.
+     */
+    public final Class<?> type;
+
+    private BasicAFT(Class<?> type) {
+        this.type = type;
+    }
+
+    /**
+     * Returns the <code>BasicAFT</code> for <code>type</code>, which
+     * should be primitive (e.g., int.class) or String.  Returns null if
+     * <code>type</code> is not appropriate for a basic annotation field
+     * type.
+     */
+    public static BasicAFT forType(Class<?> type) {
+        return bafts.get(type);
+    }
+
+    /**
+     * Maps from {@link #type} to <code>BasicAFT</code>.
+     * Contains every BasicAFT.
+     */
+    // Disgusting reason for being public; need to fix.
+    public static final Map<Class<?>, BasicAFT> bafts;
+
+    static {
+        Map<Class<?>, BasicAFT> tempBafts =
+            new HashMap<Class<?>, BasicAFT>(9);
+        tempBafts.put(byte.class, new BasicAFT(byte.class));
+        tempBafts.put(short.class, new BasicAFT(short.class));
+        tempBafts.put(int.class, new BasicAFT(int.class));
+        tempBafts.put(long.class, new BasicAFT(long.class));
+        tempBafts.put(float.class, new BasicAFT(float.class));
+        tempBafts.put(double.class, new BasicAFT(double.class));
+        tempBafts.put(char.class, new BasicAFT(char.class));
+        tempBafts.put(boolean.class, new BasicAFT(boolean.class));
+        tempBafts.put(String.class, new BasicAFT(String.class));
+        // bafts = Collections2.<Class<?>, BasicAFT>unmodifiableKeyedSet(tempBafts);
+        // bafts = bafts2;
+        bafts = tempBafts;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isValidValue(Object o) {
+        return (   (type == byte.class && o instanceof Byte)
+                || (type == short.class && o instanceof Short)
+                || (type == int.class && o instanceof Integer)
+                || (type == long.class && o instanceof Long)
+                || (type == float.class && o instanceof Float)
+                || (type == double.class && o instanceof Double)
+                || (type == char.class && o instanceof Character)
+                || (type == boolean.class && o instanceof Boolean)
+                || (type == String.class && o instanceof String));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        if (type == String.class) {
+            return "String";
+        } else {
+            return type.getName();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String format(Object o) {
+      return type != String.class ? o.toString()
+          : "\"" + charEscaper.escape((String) o) + "\"";
+    }
+
+    @Override
+    public <R, T> R accept(AFTVisitor<R, T> v, T arg) {
+        return v.visitBasicAFT(this, arg);
+    }
+}
diff --git a/scene-lib/src/annotations/field/ClassTokenAFT.java b/scene-lib/src/annotations/field/ClassTokenAFT.java
new file mode 100644
index 0000000..0b6916f
--- /dev/null
+++ b/scene-lib/src/annotations/field/ClassTokenAFT.java
@@ -0,0 +1,65 @@
+package annotations.field;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A {@link ClassTokenAFT} is the type of an annotation field that holds a
+ * class token (something like <code>{@link String}.class</code>).
+ * Even if the field type was originally some parameterization
+ * <code>{@link Class}&lt;...&gt;</code>, the annotation scene library
+ * represents it as a plain {@link ClassTokenAFT}.  Use the singleton
+ * {@link #ctaft}.
+ */
+public final class ClassTokenAFT extends ScalarAFT {
+
+    // On 2006.07.07 we decided against parameterizations because
+    // class files that use annotations don't contain them.
+
+    // The type arguments, if any, of the field type
+    // Could be "" or "<HashMap>" (stupid) or "<? extends PrettyPrinter>", etc.,
+    // but not null.
+    /* public final String parameterization; */
+
+    private ClassTokenAFT() {}
+
+    /**
+     * The singleton {@link ClassTokenAFT}.
+     */
+    public static final ClassTokenAFT ctaft = new ClassTokenAFT();
+
+    // public ClassTokenAFT(/* String parameterization */) {
+    //    /* this.parameterization = parameterization; */
+    // }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isValidValue(Object o) {
+        return o instanceof java.lang.Class;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toString() {
+        return "Class"/* + parameterization */;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String format(Object o) {
+        return ((java.lang.Class<?>)o).getName() + ".class";
+    }
+
+
+    @Override
+    public <R, T> R accept(AFTVisitor<R, T> v, T arg) {
+        return v.visitClassTokenAFT(this, arg);
+    }
+}
diff --git a/scene-lib/src/annotations/field/EnumAFT.java b/scene-lib/src/annotations/field/EnumAFT.java
new file mode 100644
index 0000000..195b5cc
--- /dev/null
+++ b/scene-lib/src/annotations/field/EnumAFT.java
@@ -0,0 +1,57 @@
+package annotations.field;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * An {@link EnumAFT} is the type of an annotation field that can hold an
+ * constant from a certain enumeration type.
+ */
+public final class EnumAFT extends ScalarAFT {
+
+    /**
+     * The name of the enumeration type whose constants the annotation field
+     * can hold.
+     */
+    public final String typeName;
+
+    /**
+     * Constructs an {@link EnumAFT} for an annotation field that can hold
+     * constants of the enumeration type with the given name.
+     */
+    public EnumAFT(String typeName) {
+        this.typeName = typeName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isValidValue(Object o) {
+        // return o instanceof Enum;
+        return o instanceof String;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public  String toString() {
+        return "enum " + typeName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String format(Object o) {
+        return typeName + "." + o.toString();
+    }
+
+
+    @Override
+    public <R, T> R accept(AFTVisitor<R, T> v, T arg) {
+        return v.visitEnumAFT(this, arg);
+    }
+}
diff --git a/scene-lib/src/annotations/field/ScalarAFT.java b/scene-lib/src/annotations/field/ScalarAFT.java
new file mode 100644
index 0000000..cf21561
--- /dev/null
+++ b/scene-lib/src/annotations/field/ScalarAFT.java
@@ -0,0 +1,9 @@
+package annotations.field;
+
+/**
+ * Common superclass for non-array {@link AnnotationFieldType}s so that
+ * {@link ArrayAFT} can accept only scalar element types, enforcing the Java
+ * language's prohibition of multidimensional arrays as annotation field types.
+ */
+public abstract class ScalarAFT extends AnnotationFieldType  {
+}
diff --git a/scene-lib/src/annotations/field/package-info.java b/scene-lib/src/annotations/field/package-info.java
new file mode 100644
index 0000000..4805c0a
--- /dev/null
+++ b/scene-lib/src/annotations/field/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * <code>annotations.field</code> provides classes that store the types of
+ * annotation fields.
+ */
+package annotations.field;
+
diff --git a/scene-lib/src/annotations/io/ASTIndex.java b/scene-lib/src/annotations/io/ASTIndex.java
new file mode 100644
index 0000000..049a350
--- /dev/null
+++ b/scene-lib/src/annotations/io/ASTIndex.java
@@ -0,0 +1,700 @@
+package annotations.io;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.lang.model.element.Name;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.AssertTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.CatchTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.CompoundAssignmentTree;
+import com.sun.source.tree.ConditionalExpressionTree;
+import com.sun.source.tree.DoWhileLoopTree;
+import com.sun.source.tree.EnhancedForLoopTree;
+import com.sun.source.tree.ExpressionStatementTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.ForLoopTree;
+import com.sun.source.tree.IfTree;
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.IntersectionTypeTree;
+import com.sun.source.tree.LabeledStatementTree;
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MemberReferenceTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ModifiersTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.ParenthesizedTree;
+import com.sun.source.tree.ReturnTree;
+import com.sun.source.tree.SwitchTree;
+import com.sun.source.tree.SynchronizedTree;
+import com.sun.source.tree.ThrowTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.TryTree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.TypeParameterTree;
+import com.sun.source.tree.UnaryTree;
+import com.sun.source.tree.UnionTypeTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.WhileLoopTree;
+import com.sun.source.tree.WildcardTree;
+import com.sun.source.util.SimpleTreeVisitor;
+import com.sun.source.util.TreePath;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.tree.JCTree;
+
+import annotations.util.JVMNames;
+import annotations.util.coll.WrapperMap;
+
+/**
+ * Cache of {@code ASTPath} data for the nodes of a compilation unit tree.
+ *
+ * @author dbro
+ */
+public class ASTIndex extends WrapperMap<Tree, ASTRecord> {
+  // single-item cache
+  private static Tree cachedRoot = null;
+  private static Map<Tree, ASTRecord> cachedIndex = null;
+  private static final int EXPECTED_SIZE = 128;
+
+  private final CompilationUnitTree cut;
+  private final Map<String, Map<String, List<String>>> formals;
+
+  /**
+   * Maps source trees in compilation unit to corresponding AST paths.
+   *
+   * @param root compilation unit to be indexed
+   * @return map of trees in compilation unit to AST paths
+   */
+  public static Map<Tree, ASTRecord> indexOf(CompilationUnitTree root) {
+    if (cachedRoot == null || !cachedRoot.equals(root)) {
+      cachedRoot = root;
+      cachedIndex = new ASTIndex(root);
+    }
+    return cachedIndex;
+  }
+
+  private ASTIndex(CompilationUnitTree root) {
+    super(HashBiMap.<Tree, ASTRecord>create(EXPECTED_SIZE));
+    cut = root;
+    formals = new HashMap<String, Map<String, List<String>>>();
+
+    // The visitor implementation is slightly complicated by the
+    // inclusion of information from both parent and child nodes in each
+    // ASTEntry.  The pattern for most node types is to call save() and
+    // saveAll() as needed to handle the node's descendants and finally
+    // to invoke defaultAction() to save the entry for the current node.
+    // (If the JVM could take advantage of tail recursion, it would be
+    // better to save the current node's entry first, at a small cost to
+    // the clarity of the code.)
+    cut.accept(new SimpleTreeVisitor<Void, ASTRecord>() {
+      Deque<Integer> counters = new ArrayDeque<Integer>();
+      String inMethod = null;
+
+      private void save(Tree node, ASTRecord rec,
+          Kind kind, String sel) {
+        if (node != null) {
+          node.accept(this, rec.extend(kind, sel));
+        }
+      }
+
+      private void save(Tree node, ASTRecord rec,
+          Kind kind, String sel, int arg) {
+        if (node != null) {
+          node.accept(this, rec.extend(kind, sel, arg));
+        }
+      }
+
+      private void saveAll(Iterable<? extends Tree> nodes,
+          ASTRecord rec, Kind kind, String sel) {
+        if (nodes != null) {
+          int i = 0;
+          for (Tree node : nodes) {
+            save(node, rec, kind, sel, i++);
+          }
+        }
+      }
+
+      private void saveClass(ClassTree node) {
+        String className =
+            ((JCTree.JCClassDecl) node).sym.flatname.toString();
+        ASTRecord rec =
+            new ASTRecord(cut, className, null, null, ASTPath.empty());
+        counters.push(0);
+        node.accept(this, rec);
+        counters.pop();
+      }
+
+      @Override
+      public Void defaultAction(Tree node, ASTRecord rec) {
+        switch (node.getKind()) {
+        case BREAK:
+        case COMPILATION_UNIT:
+        case CONTINUE:
+        case IMPORT:
+        case MODIFIERS:
+          break;  // not handled
+        default:
+          put(node, rec);
+        }
+        return null;
+      }
+
+      @Override
+      public Void visitAnnotatedType(AnnotatedTypeTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        saveAll(node.getAnnotations(), rec, kind, ASTPath.ANNOTATION);
+        save(node.getUnderlyingType(), rec, kind, ASTPath.UNDERLYING_TYPE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitAnnotation(AnnotationTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getAnnotationType(), rec, kind, ASTPath.TYPE);
+        saveAll(node.getArguments(), rec, kind, ASTPath.ARGUMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitMethodInvocation(MethodInvocationTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT);
+        save(node.getMethodSelect(), rec, kind, ASTPath.METHOD_SELECT);
+        saveAll(node.getArguments(), rec, kind, ASTPath.ARGUMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitAssert(AssertTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getCondition(), rec, kind, ASTPath.CONDITION);
+        save(node.getDetail(), rec, kind, ASTPath.DETAIL);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitAssignment(AssignmentTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        save(node.getVariable(), rec, kind, ASTPath.VARIABLE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitCompoundAssignment(CompoundAssignmentTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        save(node.getVariable(), rec, kind, ASTPath.VARIABLE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitBinary(BinaryTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getLeftOperand(), rec, kind, ASTPath.LEFT_OPERAND);
+        save(node.getRightOperand(), rec, kind, ASTPath.RIGHT_OPERAND);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitBlock(BlockTree node, ASTRecord rec) {
+        Iterable<? extends Tree> nodes = node.getStatements();
+        if (nodes != null) {
+          int i = 0;
+          for (Tree stmt : nodes) {
+            if (ASTPath.isClassEquiv(stmt.getKind())) {
+              saveClass((ClassTree) stmt);
+            } else {
+              save(stmt, rec, node.getKind(), ASTPath.STATEMENT, i);
+            }
+            ++i;
+          }
+        }
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitCase(CaseTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        saveAll(node.getStatements(), rec, kind, ASTPath.STATEMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitCatch(CatchTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getBlock(), rec, kind, ASTPath.BLOCK);
+        save(node.getParameter(), rec, kind, ASTPath.PARAMETER);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitClass(ClassTree node, ASTRecord rec) {
+        Kind kind = Tree.Kind.CLASS;  // use for all class-equivalent kinds
+        int i = 0;
+        formals.put(rec.className, new HashMap<String, List<String>>());
+        if (node.getSimpleName().length() > 0) {
+          // don't save exts/impls for anonymous inner class
+          save(node.getExtendsClause(), rec, kind, ASTPath.BOUND, -1);
+          saveAll(node.getImplementsClause(), rec, kind, ASTPath.BOUND);
+        }
+        saveAll(node.getTypeParameters(), rec, kind, ASTPath.TYPE_PARAMETER);
+        for (Tree member : node.getMembers()) {
+          if (member.getKind() == Tree.Kind.BLOCK) {
+            save(member, rec, kind, ASTPath.INITIALIZER, i++);
+          } else if (ASTPath.isClassEquiv(member.getKind())) {
+            String className =
+                ((JCTree.JCClassDecl) member).sym.flatname.toString();
+            member.accept(this,
+                new ASTRecord(cut, className, null, null, ASTPath.empty()));
+          } else {
+            member.accept(this, rec);
+          }
+        }
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitConditionalExpression(ConditionalExpressionTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getCondition(), rec, kind, ASTPath.CONDITION);
+        save(node.getFalseExpression(), rec, kind, ASTPath.FALSE_EXPRESSION);
+        save(node.getTrueExpression(), rec, kind, ASTPath.TRUE_EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitDoWhileLoop(DoWhileLoopTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getCondition(), rec, kind, ASTPath.CONDITION);
+        save(node.getStatement(), rec, kind, ASTPath.STATEMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitExpressionStatement(ExpressionStatementTree node,
+          ASTRecord rec) {
+        save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitEnhancedForLoop(EnhancedForLoopTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getVariable(), rec, kind, ASTPath.VARIABLE);
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        save(node.getStatement(), rec, kind, ASTPath.STATEMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitForLoop(ForLoopTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        saveAll(node.getInitializer(), rec, kind, ASTPath.INITIALIZER);
+        save(node.getCondition(), rec, kind, ASTPath.CONDITION);
+        save(node.getStatement(), rec, kind, ASTPath.STATEMENT);
+        saveAll(node.getUpdate(), rec, kind, ASTPath.UPDATE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitIf(IfTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getCondition(), rec, kind, ASTPath.CONDITION);
+        save(node.getThenStatement(), rec, kind, ASTPath.THEN_STATEMENT);
+        save(node.getElseStatement(), rec, kind, ASTPath.ELSE_STATEMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitArrayAccess(ArrayAccessTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        save(node.getIndex(), rec, kind, ASTPath.INDEX);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitLabeledStatement(LabeledStatementTree node,
+          ASTRecord rec) {
+        save(node.getStatement(), rec, node.getKind(), ASTPath.STATEMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitMethod(MethodTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        Tree rcvr = node.getReceiverParameter();
+        ModifiersTree mods = node.getModifiers();
+        List<? extends Tree> params = node.getParameters();
+        String outMethod = inMethod;
+        inMethod = JVMNames.getJVMMethodName(node);
+        rec = new ASTRecord(cut, rec.className, inMethod, null,
+            ASTPath.empty());
+        if (mods != null) {
+          save(mods, rec, kind, ASTPath.MODIFIERS);
+        }
+        if (rcvr != null) {
+          rcvr.accept(this, rec.extend(kind, ASTPath.PARAMETER, -1));
+        }
+        if (params != null && !params.isEmpty()) {
+          Map<String, List<String>> map = formals.get(rec.className);
+          List<String> names = new ArrayList<String>(params.size());
+          int i = 0;
+          map.put(inMethod, names);
+          for (Tree param : params) {
+            if (param != null) {
+              names.add(((VariableTree) param).getName().toString());
+              param.accept(this,
+                  rec.extend(Tree.Kind.METHOD, ASTPath.PARAMETER, i++));
+            }
+          }
+        }
+        save(node.getReturnType(), rec, kind, ASTPath.TYPE);
+        saveAll(node.getTypeParameters(), rec, kind, ASTPath.TYPE_PARAMETER);
+        // save(node.getReceiverParameter(), rec, kind, ASTPath.PARAMETER, -1);
+        // saveAll(node.getParameters(), rec, kind, ASTPath.PARAMETER);
+        saveAll(node.getThrows(), rec, kind, ASTPath.THROWS);
+        save(node.getBody(), rec, kind, ASTPath.BODY);
+        inMethod = outMethod;
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitModifiers(ModifiersTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        saveAll(node.getAnnotations(), rec, kind, ASTPath.ANNOTATION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitNewArray(NewArrayTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        Tree type = node.getType();
+        int n = node.getDimensions().size();
+        do {
+          save(type, rec, kind, ASTPath.TYPE, n);
+        } while (--n > 0);
+        saveAll(node.getDimensions(), rec, kind, ASTPath.DIMENSION);
+        saveAll(node.getInitializers(), rec, kind, ASTPath.INITIALIZER);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitNewClass(NewClassTree node, ASTRecord rec) {
+        JCTree.JCClassDecl classBody =
+            (JCTree.JCClassDecl) node.getClassBody();
+        Kind kind = node.getKind();
+        save(node.getEnclosingExpression(), rec, kind,
+            ASTPath.ENCLOSING_EXPRESSION);
+        saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT);
+        save(node.getIdentifier(), rec, kind, ASTPath.IDENTIFIER);
+        saveAll(node.getArguments(), rec, kind, ASTPath.ARGUMENT);
+        if (classBody != null) {
+          Name name = classBody.getSimpleName();
+          String className = null;
+          if (name == null || name.toString().isEmpty()) {
+            int i = counters.pop();
+            counters.push(++i);
+            className = rec.className + "$" + i;
+          } else {
+            ClassSymbol sym = ((JCTree.JCClassDecl) classBody).sym;
+            String s = sym == null ? "" : sym.toString();
+            if (s.startsWith("<anonymous ")) {
+              int i = counters.pop();
+              counters.push(++i);
+              className = s.substring(11, s.length()-1);
+            } else {
+              className = rec.className + "$" + name;
+            }
+          }
+          counters.push(0);
+          classBody.accept(this,
+              new ASTRecord(cut, className, null, null, ASTPath.empty()));
+          counters.pop();
+        }
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitLambdaExpression(LambdaExpressionTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        String outMethod = inMethod;
+        Iterable<? extends Tree> nodes = node.getParameters();
+        if (nodes != null) {
+          int i = 0;
+          for (Tree t : nodes) {
+            ASTRecord newRec = rec.extend(kind, ASTPath.PARAMETER, i++);
+            Tree.Kind newKind = t.getKind();
+            if (newKind == Tree.Kind.VARIABLE) {
+              VariableTree vt = (VariableTree) t;
+              save(vt.getType(), newRec, newKind, ASTPath.TYPE);
+              save(vt.getInitializer(), newRec, newKind, ASTPath.INITIALIZER);
+              defaultAction(vt, newRec);
+            } else {
+              t.accept(this, rec.extend(kind, ASTPath.PARAMETER, i++));
+            }
+          }
+        }
+        save(node.getBody(), rec, kind, ASTPath.BODY);
+        inMethod = outMethod;
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitParenthesized(ParenthesizedTree node,
+          ASTRecord rec) {
+        save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitReturn(ReturnTree node, ASTRecord rec) {
+        save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitMemberSelect(MemberSelectTree node,
+          ASTRecord rec) {
+        save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitMemberReference(MemberReferenceTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getQualifierExpression(), rec, kind,
+            ASTPath.QUALIFIER_EXPRESSION);
+        saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitSwitch(SwitchTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        saveAll(node.getCases(), rec, kind, ASTPath.CASE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitSynchronized(SynchronizedTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        save(node.getBlock(), rec, kind, ASTPath.BLOCK);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitThrow(ThrowTree node, ASTRecord rec) {
+        save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitCompilationUnit(CompilationUnitTree node,
+          ASTRecord rec) {
+        for (Tree tree : node.getTypeDecls()) {
+          if (ASTPath.isClassEquiv(tree.getKind())) {
+            saveClass((ClassTree) tree);
+          }
+        }
+        return null;
+      }
+
+      @Override
+      public Void visitTry(TryTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        saveAll(node.getResources(), rec, kind, ASTPath.RESOURCE);
+        save(node.getBlock(), rec, kind, ASTPath.BLOCK);
+        saveAll(node.getCatches(), rec, kind, ASTPath.CATCH);
+        save(node.getFinallyBlock(), rec, kind, ASTPath.FINALLY_BLOCK);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitParameterizedType(ParameterizedTypeTree node,
+          ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getType(), rec, kind, ASTPath.TYPE);
+        saveAll(node.getTypeArguments(), rec, kind, ASTPath.TYPE_ARGUMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitUnionType(UnionTypeTree node, ASTRecord rec) {
+        saveAll(node.getTypeAlternatives(), rec, node.getKind(),
+            ASTPath.TYPE_ALTERNATIVE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitIntersectionType(IntersectionTypeTree node,
+          ASTRecord rec) {
+        saveAll(node.getBounds(), rec, node.getKind(), ASTPath.BOUND);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitArrayType(ArrayTypeTree node, ASTRecord rec) {
+        save(node.getType(), rec, node.getKind(), ASTPath.TYPE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitTypeCast(TypeCastTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getType(), rec, kind, ASTPath.TYPE);
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitTypeParameter(TypeParameterTree node,
+          ASTRecord rec) {
+        saveAll(node.getBounds(), rec, node.getKind(), ASTPath.BOUND);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitInstanceOf(InstanceOfTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getExpression(), rec, kind, ASTPath.EXPRESSION);
+        save(node.getType(), rec, kind, ASTPath.TYPE);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitUnary(UnaryTree node, ASTRecord rec) {
+        save(node.getExpression(), rec, node.getKind(), ASTPath.EXPRESSION);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitVariable(VariableTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        if (rec.methodName == null) {  // member field
+          rec = new ASTRecord(cut, rec.className, rec.methodName,
+              ((VariableTree) node).getName().toString(), rec.astPath);
+        }
+        save(node.getType(), rec, kind, ASTPath.TYPE);
+        save(node.getInitializer(), rec, kind, ASTPath.INITIALIZER);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitWhileLoop(WhileLoopTree node, ASTRecord rec) {
+        Kind kind = node.getKind();
+        save(node.getCondition(), rec, kind, ASTPath.CONDITION);
+        save(node.getStatement(), rec, kind, ASTPath.STATEMENT);
+        return defaultAction(node, rec);
+      }
+
+      @Override
+      public Void visitWildcard(WildcardTree node, ASTRecord rec) {
+        save(node.getBound(), rec, node.getKind(), ASTPath.BOUND);
+        return defaultAction(node, rec);
+      }
+    }, null);
+  }
+
+  public static ASTRecord getASTPath(CompilationUnitTree cut, Tree node) {
+    return indexOf(cut).get(node);
+  }
+
+  public static TreePath getTreePath(CompilationUnitTree cut, ASTRecord rec) {
+    Tree node = getNode(cut, rec);
+    return node == null ? null : TreePath.getPath(cut, node);
+  }
+
+  public static Tree getNode(CompilationUnitTree cut, ASTRecord rec) {
+    Map<Tree, ASTRecord> fwdIndex = ((ASTIndex) indexOf(cut)).back;
+    Map<ASTRecord, Tree> revIndex =
+        ((BiMap<Tree, ASTRecord>) fwdIndex).inverse();
+    ExpressionTree et = cut.getPackageName();
+    String pkg = et == null ? "" : et.toString();
+    if (!pkg.isEmpty() && rec.className.indexOf('.') < 0) {
+      rec = new ASTRecord(cut, pkg + "." + rec.className,
+          rec.methodName, rec.varName, rec.astPath);
+    }
+    return revIndex.get(rec);
+  }
+
+  public static String getParameterName(CompilationUnitTree cut,
+      String className, String methodName, int index) {
+    try {
+      ASTIndex ai = (ASTIndex) ASTIndex.indexOf(cut);
+      return ai.formals.get(className).get(methodName).get(index);
+    } catch (NullPointerException ex) {
+      return null;
+    }
+  }
+
+  public static Integer getParameterIndex(CompilationUnitTree cut,
+      String className, String methodName, String varName) {
+    if (cut != null && className != null
+        && methodName != null && varName != null) {
+      // if it's already a number, return it
+      try {
+        return Integer.valueOf(varName);
+      } catch (NumberFormatException ex) {}
+      // otherwise, look through parameter list for string
+      try {
+        ASTIndex ai = (ASTIndex) ASTIndex.indexOf(cut);
+        List<String> names =
+            ai.formals.get(className).get(methodName);
+        int i = 0;
+        for (String name : names) {
+          if (varName.equals(name)) { return i; }
+          ++i;
+        }
+      } catch (NullPointerException ex) {}
+    }
+    // not found
+    return null;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    for (Map.Entry<Tree, ASTRecord> entry : entrySet()) {
+      sb.append(entry.getKey().toString().replaceAll("\\s+", " "))
+        .append(" # ").append(entry.getValue()).append("\n");
+    }
+    return sb.toString();
+  }
+}
diff --git a/scene-lib/src/annotations/io/ASTPath.java b/scene-lib/src/annotations/io/ASTPath.java
new file mode 100644
index 0000000..ec88dd2
--- /dev/null
+++ b/scene-lib/src/annotations/io/ASTPath.java
@@ -0,0 +1,1692 @@
+package annotations.io;
+
+import java.io.IOException;
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import plume.ArraysMDE;
+import annotations.util.PersistentStack;
+
+import com.sun.source.tree.AnnotatedTypeTree;
+import com.sun.source.tree.AnnotationTree;
+import com.sun.source.tree.ArrayAccessTree;
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.AssertTree;
+import com.sun.source.tree.AssignmentTree;
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.CaseTree;
+import com.sun.source.tree.CatchTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.CompoundAssignmentTree;
+import com.sun.source.tree.ConditionalExpressionTree;
+import com.sun.source.tree.DoWhileLoopTree;
+import com.sun.source.tree.EnhancedForLoopTree;
+import com.sun.source.tree.ExpressionStatementTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.ForLoopTree;
+import com.sun.source.tree.IfTree;
+import com.sun.source.tree.InstanceOfTree;
+import com.sun.source.tree.LabeledStatementTree;
+import com.sun.source.tree.LambdaExpressionTree;
+import com.sun.source.tree.MemberReferenceTree;
+import com.sun.source.tree.MemberSelectTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.NewArrayTree;
+import com.sun.source.tree.NewClassTree;
+import com.sun.source.tree.ParameterizedTypeTree;
+import com.sun.source.tree.ParenthesizedTree;
+import com.sun.source.tree.ReturnTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.SwitchTree;
+import com.sun.source.tree.SynchronizedTree;
+import com.sun.source.tree.ThrowTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.TryTree;
+import com.sun.source.tree.TypeCastTree;
+import com.sun.source.tree.UnaryTree;
+import com.sun.source.tree.UnionTypeTree;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.WhileLoopTree;
+import com.sun.source.tree.WildcardTree;
+import com.sun.source.util.TreePath;
+
+/**
+ * A path through the AST.
+ */
+public class ASTPath extends ConsStack<ASTPath.ASTEntry>
+implements Comparable<ASTPath>, Iterable<ASTPath.ASTEntry> {
+  private static final ASTPath EMPTY = new ASTPath();
+  private static final String[] typeSelectors =
+    { "bound", "identifier", "type", "typeAlternative", "typeArgument",
+    "typeParameter", "underlyingType" };
+
+  // Constants for the various child selectors.
+  public static final String ANNOTATION = "annotation";
+  public static final String ARGUMENT = "argument";
+  public static final String BLOCK = "block";
+  public static final String BODY = "body";
+  public static final String BOUND = "bound";
+  public static final String CASE = "case";
+  public static final String CATCH = "catch";
+  public static final String CLASS_BODY = "classBody";
+  public static final String CONDITION = "condition";
+  public static final String DETAIL = "detail";
+  public static final String DIMENSION = "dimension";
+  public static final String ELSE_STATEMENT = "elseStatement";
+  public static final String ENCLOSING_EXPRESSION = "enclosingExpression";
+  public static final String EXPRESSION = "expression";
+  public static final String FALSE_EXPRESSION = "falseExpression";
+  public static final String FINALLY_BLOCK = "finallyBlock";
+  public static final String IDENTIFIER = "identifier";
+  public static final String INDEX = "index";
+  public static final String INITIALIZER = "initializer";
+  public static final String LEFT_OPERAND = "leftOperand";
+  public static final String METHOD_SELECT = "methodSelect";
+  public static final String MODIFIERS = "modifiers";
+  public static final String PARAMETER = "parameter";
+  public static final String QUALIFIER_EXPRESSION = "qualifierExpression";
+  public static final String RESOURCE = "resource";
+  public static final String RIGHT_OPERAND = "rightOperand";
+  public static final String STATEMENT = "statement";
+  public static final String THEN_STATEMENT = "thenStatement";
+  public static final String THROWS = "throws";
+  public static final String TRUE_EXPRESSION = "trueExpression";
+  public static final String TYPE = "type";
+  public static final String TYPE_ALTERNATIVE = "typeAlternative";
+  public static final String TYPE_ARGUMENT = "typeArgument";
+  public static final String TYPE_PARAMETER = "typeParameter";
+  public static final String UNDERLYING_TYPE = "underlyingType";
+  public static final String UPDATE = "update";
+  public static final String VARIABLE = "variable";
+
+  /**
+   * A single entry in an AST path.
+   */
+  public static class ASTEntry implements Comparable<ASTEntry> {
+    private Tree.Kind treeKind;
+    private String childSelector;
+    private Integer argument;
+
+    /**
+     * Constructs a new AST entry. For example, in the entry:
+     * <pre>
+     * {@code
+     * Block.statement 3
+     * }</pre>
+     * the tree kind is "Block", the child selector is "statement", and the
+     * argument is "3".
+     *
+     * @param treeKind the kind of this AST entry
+     * @param childSelector the child selector to this AST entry
+     * @param argument the argument
+     */
+    public ASTEntry(Tree.Kind treeKind, String childSelector, Integer argument) {
+      this.treeKind = treeKind;
+      this.childSelector = childSelector;
+      this.argument = argument;
+    }
+
+    /**
+     * Constructs a new AST entry, without an argument.
+     *
+     * See {@link #ASTEntry(Tree.Kind, String, Integer)} for an example of the parameters.
+     *
+     * @param treeKind the kind of this AST entry
+     * @param childSelector the child selector to this AST entry
+     */
+    public ASTEntry(Tree.Kind treeKind, String childSelector) {
+      this(treeKind, childSelector, null);
+    }
+
+    /**
+     * Gets the tree node equivalent kind of this AST entry. For example, in
+     * <pre>
+     * {@code
+     * Block.statement 3
+     * }</pre>
+     * "Block" is the tree kind.
+     * @return the tree kind
+     */
+    public Tree.Kind getTreeKind() {
+      return treeKind;
+    }
+
+    /**
+     * Gets the child selector of this AST entry. For example, in
+     * <pre>
+     * {@code
+     * Block.statement 3
+     * }</pre>
+     * "statement" is the child selector.
+     * @return the child selector
+     */
+    public String getChildSelector() {
+      return childSelector;
+    }
+
+    /**
+     * Determines if the given string is equal to this AST path entry's
+     * child selector.
+     *
+     * @param s the string to compare to
+     * @return {@code true} if the string matches the child selector,
+     *         {@code false} otherwise.
+     */
+    public boolean childSelectorIs(String s) {
+      return childSelector.equals(s);
+    }
+
+    /**
+     * Gets the argument of this AST entry. For example, in
+     * <pre>
+     * {@code
+     * Block.statement 3
+     * }</pre>
+     * "3" is the argument.
+     * @return the argument
+     * @throws IllegalStateException if this AST entry does not have an argument
+     */
+    public int getArgument() {
+      if (argument >= (negativeAllowed() ? -1 : 0)) {
+        return argument;
+      }
+      throw new IllegalStateException("Value not set.");
+    }
+
+    /**
+     * Checks that this Entry has an argument.
+     *
+     * @return if this entry has an argument
+     */
+    public boolean hasArgument() {
+      return argument == null ? false
+          : argument >= 0 ? true
+          : negativeAllowed();
+    }
+
+    // argument < 0 valid for two cases
+    private boolean negativeAllowed() {
+      switch (treeKind) {
+      case CLASS:
+        return childSelectorIs(ASTPath.BOUND);
+      case METHOD:
+        return childSelectorIs(ASTPath.PARAMETER);
+      default:
+        return false;
+      }
+    }
+
+    @Override
+    public int compareTo(ASTEntry o) {
+      if (o == null) {
+        return 1;
+      } else if (o.childSelector == null) {
+        if (childSelector != null) { return 1; }
+      } else if (childSelector == null) {
+        return -1;
+      }
+      int c = treeKind.compareTo(o.treeKind);
+      if (c != 0) { return c; }
+      c = childSelector.compareTo(o.childSelector);
+      if (c != 0) { return c; }
+      return o.argument == null
+          ? argument == null ? 0 : 1
+          : argument == null ? -1 : argument.compareTo(o.argument);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      return o instanceof ASTEntry && compareTo((ASTEntry) o) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+      int base = treeKind.hashCode() ^ childSelector.hashCode();
+      int shift = argument == null ? 0 : 2 + argument;
+      return Integer.rotateRight(base, shift);
+    }
+
+    @Override
+    public String toString() {
+      StringBuilder b = new StringBuilder();
+      switch (treeKind) {
+      case CLASS:
+      case ENUM:
+      case INTERFACE:
+        b.append("Class");
+        break;
+      case AND:
+      case CONDITIONAL_AND:
+      case CONDITIONAL_OR:
+      case DIVIDE:
+      case EQUAL_TO:
+      case GREATER_THAN:
+      case GREATER_THAN_EQUAL:
+      case LEFT_SHIFT:
+      case LESS_THAN:
+      case LESS_THAN_EQUAL:
+      case MINUS:
+      case MULTIPLY:
+      case NOT_EQUAL_TO:
+      case OR:
+      case PLUS:
+      case REMAINDER:
+      case RIGHT_SHIFT:
+      case XOR:
+        b.append("Binary");
+        break;
+      case LOGICAL_COMPLEMENT:
+      case POSTFIX_DECREMENT:
+      case POSTFIX_INCREMENT:
+      case PREFIX_DECREMENT:
+      case PREFIX_INCREMENT:
+      case UNARY_MINUS:
+      case UNARY_PLUS:
+      case UNSIGNED_RIGHT_SHIFT:
+        b.append("Unary");
+        break;
+      case AND_ASSIGNMENT:
+      case DIVIDE_ASSIGNMENT:
+      case LEFT_SHIFT_ASSIGNMENT:
+      case MINUS_ASSIGNMENT:
+      case MULTIPLY_ASSIGNMENT:
+      case OR_ASSIGNMENT:
+      case PLUS_ASSIGNMENT:
+      case REMAINDER_ASSIGNMENT:
+      case RIGHT_SHIFT_ASSIGNMENT:
+      case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+      case XOR_ASSIGNMENT:
+        b.append("CompoundAssignment");
+        break;
+      case EXTENDS_WILDCARD:
+      case SUPER_WILDCARD:
+      case UNBOUNDED_WILDCARD:
+        b.append("Wildcard");
+        break;
+      case ANNOTATION:
+      case TYPE_ANNOTATION:
+        b.append("Annotation");
+        break;
+      default:
+        String s = treeKind.toString();
+        int n = s.length();
+        boolean cap = true;  // capitalize next character
+        for (int i = 0; i < n; i++) {
+          char c = s.charAt(i);
+          if (c == '_') {
+            cap = true;
+          } else {
+            b.append(cap ? Character.toUpperCase(c)
+                : Character.toLowerCase(c));
+            cap = false;
+          }
+        }
+      }
+      b.append(".").append(childSelector);
+      if (argument != null) { b.append(" ").append(argument); }
+      return b.toString();
+    }
+  }
+
+  private static Comparator<ASTPath> comparator = new Comparator<ASTPath>() {
+    @Override
+    public int compare(ASTPath p1, ASTPath p2) {
+      return p1 == null ? (p2 == null ? 0 : -1) : p1.compareTo(p2);
+    }
+  };
+
+  ASTPath() {}
+
+  public static ASTPath empty() { return EMPTY; }
+
+  public static Comparator<ASTPath> getComparator() {
+    return comparator;
+  }
+
+  // TODO: replace w/ skip list?
+  @Override
+  public Iterator<ASTEntry> iterator() {
+    PersistentStack<ASTEntry> s = this;
+    int n = size();
+    ASTEntry[] a = new ASTEntry[n];
+    while (--n >= 0) {
+      a[n] = s.peek();
+      s = s.pop();
+    }
+    return Arrays.asList(a).iterator();
+  }
+
+  public ASTPath extendNewArray(int depth) {
+    return extend(new ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, depth));
+  }
+
+  public ASTPath newArrayLevel(int depth) {
+    return add(new ASTEntry(Tree.Kind.NEW_ARRAY, ASTPath.TYPE, depth));
+  }
+
+  public ASTPath add(ASTEntry entry) {
+    ASTPath path = EMPTY;
+    for (ASTEntry e : this) { path = path.extend(e); }
+    return path.extend(entry);
+  }
+
+  public ASTPath extend(ASTEntry entry) {
+    return (ASTPath) push(entry);
+  }
+
+  public ASTPath getParentPath() {
+    return (ASTPath) pop();
+  }
+
+  public ASTEntry get(int index) {
+    PersistentStack<ASTEntry> s = this;
+    int n = size();
+    if (index >= n) {
+      throw new NoSuchElementException(Integer.toString(index));
+    }
+    if (index < 0) {
+      index += n;
+      if (index < 0) {
+        throw new IllegalArgumentException("negative index " + index);
+      }
+    }
+    while (--n > index) { s = s.pop(); }
+    return s.peek();
+  }
+
+  @Override
+  public int hashCode() {
+    // hacky fix: remove {Method,Class}.body for comparison
+    PersistentStack<ASTEntry> s = canonical(this);
+    int hash = 0;
+    while (!s.isEmpty()) {
+      hash = Integer.rotateRight(hash ^ s.peek().hashCode(), 1);
+      s = s.pop();
+    }
+    return hash;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return o instanceof ASTPath && equals((ASTPath) o);
+  }
+
+  public boolean equals(ASTPath astPath) {
+    return compareTo(astPath) == 0;
+  }
+
+  @Override
+  public int compareTo(ASTPath o) {
+    // hacky fix: remove {Method,Class}.body for comparison
+    PersistentStack<ASTEntry> s0 = canonical(this);
+    PersistentStack<ASTEntry> s1 = canonical(o);
+    Deque<ASTEntry> d0 = new LinkedList<ASTEntry>();
+    Deque<ASTEntry> d1 = new LinkedList<ASTEntry>();
+    int c = 0;
+    while (!s0.isEmpty()) {
+      d0.push(s0.peek());
+      s0 = s0.pop();
+    }
+    while (!s1.isEmpty()) {
+      d1.push(s1.peek());
+      s1 = s1.pop();
+    }
+    int n0 = d0.size();
+    int n1 = d1.size();
+    c = Integer.compare(n0, n1);
+    if (c == 0) {
+      Iterator<ASTEntry> i0 = d0.iterator();
+      Iterator<ASTEntry> i1 = d1.iterator();
+      while (i0.hasNext()) {
+        c = i0.next().compareTo(i1.next());
+        if (c != 0) { return c; }
+      }
+    }
+    return c;
+  }
+
+  private static ASTPath canonical(ASTPath astPath) {
+    // TODO
+    return astPath;
+  }
+
+  @Override
+  public String toString() {
+    if (isEmpty()) { return ""; }
+    Iterator<ASTEntry> iter = iterator();
+    StringBuilder sb = new StringBuilder().append(iter.next());
+    while (iter.hasNext()) {
+      sb = sb.append(", ").append(iter.next());
+    }
+    return sb.toString();
+  }
+
+  /**
+   * Create a new {@code ASTPath} from a formatted string description.
+   *
+   * @param s formatted string as in JAIF {@code insert-\{cast,annotation\}}
+   * @return the corresponding {@code ASTPath}
+   * @throws ParseException
+   */
+  public static ASTPath parse(final String s) throws ParseException {
+    return new Parser(s).parseASTPath();
+  }
+
+  /**
+   * Determine whether this {@code ASTPath} matches a given {@code TreePath}.
+   */
+  public boolean matches(TreePath treePath) {
+    CompilationUnitTree cut = treePath.getCompilationUnit();
+    Tree leaf = treePath.getLeaf();
+    ASTPath astPath = ASTIndex.indexOf(cut).get(leaf).astPath;  // FIXME
+    return this.equals(astPath);
+  }
+
+  static class Parser {
+    // adapted from annotations.io.IndexFileParser
+    // TODO: refactor IndexFileParser to use this class
+
+    StreamTokenizer st;
+
+    Parser(String s) {
+      st = new StreamTokenizer(new StringReader(s));
+    }
+
+    private void getTok() {
+      try {
+        st.nextToken();
+      } catch (IOException e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    private boolean gotType(int t) {
+      return st.ttype == t;
+    }
+
+    private int intVal() throws ParseException {
+      if (gotType(StreamTokenizer.TT_NUMBER)) {
+        int n = (int) st.nval;
+        if (n == st.nval) {
+          return n;
+        }
+      }
+      throw new ParseException("expected integer, got " + st);
+    }
+
+    private String strVal() throws ParseException {
+      if (gotType(StreamTokenizer.TT_WORD)) {
+        return st.sval;
+      }
+      throw new ParseException("expected string, got " + st);
+    }
+
+    /**
+     * Parses an AST path.
+     * @return the AST path
+     */
+    ASTPath parseASTPath() throws ParseException {
+      ASTPath astPath = new ASTPath().extend(parseASTEntry());
+      while (gotType(',')) {
+        getTok();
+        astPath = astPath.extend(parseASTEntry());
+      }
+      return astPath;
+    }
+
+    /**
+     * Parses and returns the next AST entry.
+     * @return a new AST entry
+     * @throws ParseException if the next entry type is invalid
+     */
+    ASTEntry parseASTEntry() throws ParseException {
+      String s = strVal();
+
+      if (s.equals("AnnotatedType")) {
+        return newASTEntry(Tree.Kind.ANNOTATED_TYPE,
+            new String[] {ASTPath.ANNOTATION, ASTPath.UNDERLYING_TYPE},
+            new String[] {ASTPath.ANNOTATION});
+      } else if (s.equals("ArrayAccess")) {
+        return newASTEntry(Tree.Kind.ARRAY_ACCESS,
+            new String[] {ASTPath.EXPRESSION, ASTPath.INDEX});
+      } else if (s.equals("ArrayType")) {
+        return newASTEntry(Tree.Kind.ARRAY_TYPE,
+            new String[] {ASTPath.TYPE});
+      } else if (s.equals("Assert")) {
+        return newASTEntry(Tree.Kind.ASSERT,
+            new String[] {ASTPath.CONDITION, ASTPath.DETAIL});
+      } else if (s.equals("Assignment")) {
+        return newASTEntry(Tree.Kind.ASSIGNMENT,
+            new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION});
+      } else if (s.equals("Binary")) {
+        // Always use Tree.Kind.PLUS for Binary
+        return newASTEntry(Tree.Kind.PLUS,
+            new String[] {ASTPath.LEFT_OPERAND, ASTPath.RIGHT_OPERAND});
+      } else if (s.equals("Block")) {
+        return newASTEntry(Tree.Kind.BLOCK,
+            new String[] {ASTPath.STATEMENT},
+            new String[] {ASTPath.STATEMENT});
+      } else if (s.equals("Case")) {
+        return newASTEntry(Tree.Kind.CASE,
+            new String[] {ASTPath.EXPRESSION, ASTPath.STATEMENT},
+            new String[] {ASTPath.STATEMENT});
+      } else if (s.equals("Catch")) {
+        return newASTEntry(Tree.Kind.CATCH,
+            new String[] {ASTPath.PARAMETER, ASTPath.BLOCK});
+      } else if (s.equals("Class")) {
+        return newASTEntry(Tree.Kind.CLASS,
+            new String[] {ASTPath.BOUND, ASTPath.INITIALIZER,
+                ASTPath.TYPE_PARAMETER},
+            new String[] {ASTPath.BOUND, ASTPath.INITIALIZER,
+                ASTPath.TYPE_PARAMETER});
+      } else if (s.equals("CompoundAssignment")) {
+        // Always use Tree.Kind.PLUS_ASSIGNMENT for CompoundAssignment
+        return newASTEntry(Tree.Kind.PLUS_ASSIGNMENT,
+            new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION});
+      } else if (s.equals("ConditionalExpression")) {
+        return newASTEntry(Tree.Kind.CONDITIONAL_EXPRESSION,
+            new String[] {ASTPath.CONDITION,
+                ASTPath.TRUE_EXPRESSION,
+                ASTPath.FALSE_EXPRESSION});
+      } else if (s.equals("DoWhileLoop")) {
+        return newASTEntry(Tree.Kind.DO_WHILE_LOOP,
+            new String[] {ASTPath.CONDITION, ASTPath.STATEMENT});
+      } else if (s.equals("EnhancedForLoop")) {
+        return newASTEntry(Tree.Kind.ENHANCED_FOR_LOOP,
+            new String[] {ASTPath.VARIABLE,
+                ASTPath.EXPRESSION,
+                ASTPath.STATEMENT});
+      } else if (s.equals("ExpressionStatement")) {
+        return newASTEntry(Tree.Kind.EXPRESSION_STATEMENT,
+            new String[] {ASTPath.EXPRESSION});
+      } else if (s.equals("ForLoop")) {
+        return newASTEntry(Tree.Kind.FOR_LOOP,
+            new String[] {ASTPath.INITIALIZER, ASTPath.CONDITION,
+                ASTPath.UPDATE, ASTPath.STATEMENT},
+            new String[] {ASTPath.INITIALIZER, ASTPath.UPDATE});
+      } else if (s.equals("If")) {
+        return newASTEntry(Tree.Kind.IF,
+            new String[] {ASTPath.CONDITION,
+                ASTPath.THEN_STATEMENT, ASTPath.ELSE_STATEMENT});
+      } else if (s.equals("InstanceOf")) {
+        return newASTEntry(Tree.Kind.INSTANCE_OF,
+            new String[] {ASTPath.EXPRESSION, ASTPath.TYPE});
+      } else if (s.equals("LabeledStatement")) {
+        return newASTEntry(Tree.Kind.LABELED_STATEMENT,
+            new String[] {ASTPath.STATEMENT});
+      } else if (s.equals("LambdaExpression")) {
+        return newASTEntry(Tree.Kind.LAMBDA_EXPRESSION,
+            new String[] {ASTPath.PARAMETER, ASTPath.BODY},
+            new String[] {ASTPath.PARAMETER});
+      } else if (s.equals("MemberReference")) {
+        return newASTEntry(Tree.Kind.MEMBER_REFERENCE,
+            new String[] {ASTPath.QUALIFIER_EXPRESSION, ASTPath.TYPE_ARGUMENT},
+            new String[] {ASTPath.TYPE_ARGUMENT});
+      } else if (s.equals("MemberSelect")) {
+        return newASTEntry(Tree.Kind.MEMBER_SELECT,
+            new String[] {ASTPath.EXPRESSION});
+      } else if (s.equals("Method")) {
+        return newASTEntry(Tree.Kind.METHOD,
+            new String[] {ASTPath.BODY, ASTPath.PARAMETER,
+                ASTPath.TYPE, ASTPath.TYPE_PARAMETER},
+            new String[] {ASTPath.PARAMETER, ASTPath.TYPE_PARAMETER});
+      } else if (s.equals("MethodInvocation")) {
+        return newASTEntry(Tree.Kind.METHOD_INVOCATION,
+            new String[] {ASTPath.TYPE_ARGUMENT,
+                ASTPath.METHOD_SELECT, ASTPath.ARGUMENT},
+            new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT});
+      } else if (s.equals("NewArray")) {
+        return newASTEntry(Tree.Kind.NEW_ARRAY,
+            new String[] {ASTPath.TYPE, ASTPath.DIMENSION,
+                ASTPath.INITIALIZER},
+            new String[] {ASTPath.TYPE, ASTPath.DIMENSION,
+                ASTPath.INITIALIZER});
+      } else if (s.equals("NewClass")) {
+        return newASTEntry(Tree.Kind.NEW_CLASS,
+            new String[] {ASTPath.ENCLOSING_EXPRESSION,
+                ASTPath.TYPE_ARGUMENT, ASTPath.IDENTIFIER,
+                ASTPath.ARGUMENT, ASTPath.CLASS_BODY},
+            new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT});
+      } else if (s.equals("ParameterizedType")) {
+        return newASTEntry(Tree.Kind.PARAMETERIZED_TYPE,
+            new String[] {ASTPath.TYPE, ASTPath.TYPE_ARGUMENT},
+            new String[] {ASTPath.TYPE_ARGUMENT});
+      } else if (s.equals("Parenthesized")) {
+        return newASTEntry(Tree.Kind.PARENTHESIZED,
+            new String[] {ASTPath.EXPRESSION});
+      } else if (s.equals("Return")) {
+        return newASTEntry(Tree.Kind.RETURN,
+            new String[] {ASTPath.EXPRESSION});
+      } else if (s.equals("Switch")) {
+        return newASTEntry(Tree.Kind.SWITCH,
+            new String[] {ASTPath.EXPRESSION, ASTPath.CASE},
+            new String[] {ASTPath.CASE});
+      } else if (s.equals("Synchronized")) {
+        return newASTEntry(Tree.Kind.SYNCHRONIZED,
+            new String[] {ASTPath.EXPRESSION, ASTPath.BLOCK});
+      } else if (s.equals("Throw")) {
+        return newASTEntry(Tree.Kind.THROW,
+            new String[] {ASTPath.EXPRESSION});
+      } else if (s.equals("Try")) {
+        return newASTEntry(Tree.Kind.TRY,
+            new String[] {ASTPath.BLOCK, ASTPath.CATCH, ASTPath.FINALLY_BLOCK},
+            new String[] {ASTPath.CATCH});
+      } else if (s.equals("TypeCast")) {
+        return newASTEntry(Tree.Kind.TYPE_CAST,
+            new String[] {ASTPath.TYPE, ASTPath.EXPRESSION});
+      } else if (s.equals("Unary")) {
+        // Always use Tree.Kind.UNARY_PLUS for Unary
+        return newASTEntry(Tree.Kind.UNARY_PLUS,
+            new String[] {ASTPath.EXPRESSION});
+      } else if (s.equals("UnionType")) {
+        return newASTEntry(Tree.Kind.UNION_TYPE,
+            new String[] {ASTPath.TYPE_ALTERNATIVE},
+            new String[] {ASTPath.TYPE_ALTERNATIVE});
+      } else if (s.equals("Variable")) {
+        return newASTEntry(Tree.Kind.VARIABLE,
+            new String[] {ASTPath.TYPE, ASTPath.INITIALIZER});
+      } else if (s.equals("WhileLoop")) {
+        return newASTEntry(Tree.Kind.WHILE_LOOP,
+            new String[] {ASTPath.CONDITION, ASTPath.STATEMENT});
+      } else if (s.equals("Wildcard")) {
+        // Always use Tree.Kind.UNBOUNDED_WILDCARD for Wildcard
+        return newASTEntry(Tree.Kind.UNBOUNDED_WILDCARD,
+            new String[] {ASTPath.BOUND});
+      }
+
+      throw new ParseException("Invalid AST path type: " + s);
+    }
+
+    /**
+     * Parses and constructs a new AST entry, where none of the child selections require
+     * arguments. For example, the call:
+     *
+     * <pre>
+     * {@code newASTEntry(Tree.Kind.WHILE_LOOP, new String[] {"condition", "statement"});</pre>
+     *
+     * constructs a while loop AST entry, where the valid child selectors are "condition" or
+     * "statement".
+     *
+     * @param kind the kind of this AST entry
+     * @param legalChildSelectors a list of the legal child selectors for this AST entry
+     * @return a new {@link ASTEntry}
+     * @throws ParseException if an illegal argument is found
+     */
+    private ASTEntry newASTEntry(Tree.Kind kind, String[] legalChildSelectors)
+        throws ParseException {
+      return newASTEntry(kind, legalChildSelectors, null);
+    }
+
+    /**
+     * Parses and constructs a new AST entry. For example, the call:
+     *
+     * <pre>
+     * {@code newASTEntry(Tree.Kind.CASE, new String[] {"expression", "statement"}, new String[] {"statement"});
+     * </pre>
+     *
+     * constructs a case AST entry, where the valid child selectors are
+     * "expression" or "statement" and the "statement" child selector requires
+     * an argument.
+     *
+     * @param kind the kind of this AST entry
+     * @param legalChildSelectors a list of the legal child selectors for this AST entry
+     * @param argumentChildSelectors a list of the child selectors that also require an argument.
+     *                               Entries here should also be in the legalChildSelectors list.
+     * @return a new {@link ASTEntry}
+     * @throws ParseException if an illegal argument is found
+     */
+    private ASTEntry newASTEntry(Tree.Kind kind, String[] legalChildSelectors,
+        String[] argumentChildSelectors) throws ParseException {
+      if (gotType('.')) {
+        getTok();
+      } else {
+        throw new ParseException("expected '.', got " + st);
+      }
+
+      String s = strVal();
+      for (String arg : legalChildSelectors) {
+        if (s.equals(arg)) {
+          if (argumentChildSelectors != null
+              && ArraysMDE.indexOf(argumentChildSelectors, arg) >= 0) {
+            getTok();
+            return new ASTEntry(kind, arg, intVal());
+          } else {
+            return new ASTEntry(kind, arg);
+          }
+        }
+      }
+
+      throw new ParseException("Invalid argument for " + kind
+          + " (legal arguments - " + Arrays.toString(legalChildSelectors)
+          + "): " + s);
+    }
+  }
+
+  static class Matcher {
+    // adapted from IndexFileParser.parseASTPath et al.
+    // TODO: refactor switch statement into TreeVisitor?
+    public static final DebugWriter dbug = new DebugWriter();
+    private ASTPath astPath;
+
+    Matcher(ASTPath astPath) {
+      this.astPath = astPath;
+    }
+
+    private boolean nonDecl(TreePath path) {
+      switch (path.getLeaf().getKind()) {
+      case CLASS:
+      case METHOD:
+        return false;
+      case VARIABLE:
+        TreePath parentPath = path.getParentPath();
+        return parentPath != null
+            && parentPath.getLeaf().getKind() != Tree.Kind.CLASS;
+      default:
+        return true;
+      }
+    }
+
+    public boolean matches(TreePath path) {
+      return matches(path, -1);
+    }
+
+    public boolean matches(TreePath path, int depth) {
+      if (path == null) {
+        return false;
+      }
+
+      // actualPath stores the path through the source code AST to this
+      // location (specified by the "path" parameter to this method). It is
+      // computed by traversing from this location up the source code AST
+      // until it reaches a method node (this gets only the part of the path
+      // within a method) or class node (this gets only the part of the path
+      // within a field).
+      List<Tree> actualPath = new ArrayList<Tree>();
+      while (path != null && nonDecl(path)) {
+        actualPath.add(0, path.getLeaf());
+        path = path.getParentPath();
+      }
+
+      if (dbug.isEnabled()) {
+        dbug.debug("AST [%s]%n", astPath);
+        for (Tree t : actualPath) {
+          dbug.debug("  %s: %s%n", t.getKind(),
+              t.toString().replace('\n', ' '));
+        }
+      }
+
+      if (astPath.isEmpty() || actualPath.isEmpty()
+          || actualPath.size() != astPath.size() + 1) {
+        return false;
+      }
+
+      for (int i = 0; i < astPath.size() && i < actualPath.size(); i++) {
+        ASTPath.ASTEntry astNode = astPath.get(i);
+        Tree actualNode = actualPath.get(i);
+
+        // Based on the child selector and (optional) argument in "astNode",
+        // "next" will get set to the next source node below "actualNode".
+        // Then "next" will be compared with the node following "astNode"
+        // in "actualPath". If it's not a match, this is not the correct
+        // location. If it is a match, keep going.
+        Tree next = null;
+        dbug.debug("astNode: %s%n", astNode);
+        dbug.debug("actualNode: %s%n", actualNode.getKind());
+        if (!kindsMatch(astNode.getTreeKind(), actualNode.getKind())) {
+          return false;
+        }
+
+        switch (actualNode.getKind()) {
+        case ANNOTATED_TYPE: {
+          AnnotatedTypeTree annotatedType = (AnnotatedTypeTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.ANNOTATION)) {
+            int arg = astNode.getArgument();
+            List<? extends AnnotationTree> annos = annotatedType.getAnnotations();
+            if (arg >= annos.size()) {
+              return false;
+            }
+            next = annos.get(arg);
+          } else {
+            next = annotatedType.getUnderlyingType();
+          }
+          break;
+        }
+        case ARRAY_ACCESS: {
+          ArrayAccessTree arrayAccess = (ArrayAccessTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+            next = arrayAccess.getExpression();
+          } else {
+            next = arrayAccess.getIndex();
+          }
+          break;
+        }
+        case ARRAY_TYPE: {
+          ArrayTypeTree arrayType = (ArrayTypeTree) actualNode;
+          next = arrayType.getType();
+          break;
+        }
+        case ASSERT: {
+          AssertTree azzert = (AssertTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+            next = azzert.getCondition();
+          } else {
+            next = azzert.getDetail();
+          }
+          break;
+        }
+        case ASSIGNMENT: {
+          AssignmentTree assignment = (AssignmentTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
+            next = assignment.getVariable();
+          } else {
+            next = assignment.getExpression();
+          }
+          break;
+        }
+        case BLOCK: {
+          BlockTree block = (BlockTree) actualNode;
+          int arg = astNode.getArgument();
+          List<? extends StatementTree> statements = block.getStatements();
+          if (arg >= block.getStatements().size()) {
+            return false;
+          }
+          next = statements.get(arg);
+          break;
+        }
+        case CASE: {
+          CaseTree caze = (CaseTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+            next = caze.getExpression();
+          } else {
+            int arg = astNode.getArgument();
+            List<? extends StatementTree> statements = caze.getStatements();
+            if (arg >= statements.size()) {
+              return false;
+            }
+            next = statements.get(arg);
+          }
+          break;
+        }
+        case CATCH: {
+          CatchTree cach = (CatchTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
+            next = cach.getParameter();
+          } else {
+            next = cach.getBlock();
+          }
+          break;
+        }
+        case CLASS: {
+          ClassTree clazz = (ClassTree) actualNode;
+          int arg = astNode.getArgument();
+          if (astNode.childSelectorIs(ASTPath.BOUND)) {
+            next = arg == -1 ? clazz.getExtendsClause()
+                : clazz.getImplementsClause().get(arg);
+          } else {
+            next = clazz.getTypeParameters().get(arg);
+          }
+          break;
+        }
+        case CONDITIONAL_EXPRESSION: {
+          ConditionalExpressionTree conditionalExpression =
+              (ConditionalExpressionTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+            next = conditionalExpression.getCondition();
+          } else if (astNode.childSelectorIs(ASTPath.TRUE_EXPRESSION)) {
+            next = conditionalExpression.getTrueExpression();
+          } else {
+            next = conditionalExpression.getFalseExpression();
+          }
+          break;
+        }
+        case DO_WHILE_LOOP: {
+          DoWhileLoopTree doWhileLoop =(DoWhileLoopTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+            next = doWhileLoop.getCondition();
+          } else {
+            next = doWhileLoop.getStatement();
+          }
+          break;
+        }
+        case ENHANCED_FOR_LOOP: {
+          EnhancedForLoopTree enhancedForLoop = (EnhancedForLoopTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
+            next = enhancedForLoop.getVariable();
+          } else if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+            next = enhancedForLoop.getExpression();
+          } else {
+            next = enhancedForLoop.getStatement();
+          }
+          break;
+        }
+        case EXPRESSION_STATEMENT: {
+          ExpressionStatementTree expressionStatement =
+              (ExpressionStatementTree) actualNode;
+          next = expressionStatement.getExpression();
+          break;
+        }
+        case FOR_LOOP: {
+          ForLoopTree forLoop = (ForLoopTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
+            int arg = astNode.getArgument();
+            List<? extends StatementTree> inits = forLoop.getInitializer();
+            if (arg >= inits.size()) {
+              return false;
+            }
+            next = inits.get(arg);
+          } else if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+            next = forLoop.getCondition();
+          } else if (astNode.childSelectorIs(ASTPath.UPDATE)) {
+            int arg = astNode.getArgument();
+            List<? extends ExpressionStatementTree> updates = forLoop.getUpdate();
+            if (arg >= updates.size()) {
+              return false;
+            }
+            next = updates.get(arg);
+          } else {
+            next = forLoop.getStatement();
+          }
+          break;
+        }
+        case IF: {
+          IfTree iff = (IfTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+            next = iff.getCondition();
+          } else if (astNode.childSelectorIs(ASTPath.THEN_STATEMENT)) {
+            next = iff.getThenStatement();
+          } else {
+            next = iff.getElseStatement();
+          }
+          break;
+        }
+        case INSTANCE_OF: {
+          InstanceOfTree instanceOf = (InstanceOfTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+            next = instanceOf.getExpression();
+          } else {
+            next = instanceOf.getType();
+          }
+          break;
+        }
+        case LABELED_STATEMENT: {
+          LabeledStatementTree labeledStatement =
+              (LabeledStatementTree) actualNode;
+          next = labeledStatement.getStatement();
+          break;
+        }
+        case LAMBDA_EXPRESSION: {
+          LambdaExpressionTree lambdaExpression =
+              (LambdaExpressionTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
+            int arg = astNode.getArgument();
+            List<? extends VariableTree> params =
+                lambdaExpression.getParameters();
+            if (arg >= params.size()) {
+              return false;
+            }
+            next = params.get(arg);
+          } else {
+            next = lambdaExpression.getBody();
+          }
+          break;
+        }
+        case MEMBER_REFERENCE: {
+          MemberReferenceTree memberReference = (MemberReferenceTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.QUALIFIER_EXPRESSION)) {
+            next = memberReference.getQualifierExpression();
+          } else {
+            int arg = astNode.getArgument();
+            List<? extends ExpressionTree> typeArgs =
+                memberReference.getTypeArguments();
+            if (arg >= typeArgs.size()) {
+              return false;
+            }
+            next = typeArgs.get(arg);
+          }
+          break;
+        }
+        case MEMBER_SELECT: {
+          MemberSelectTree memberSelect = (MemberSelectTree) actualNode;
+          next = memberSelect.getExpression();
+          break;
+        }
+        case METHOD: {
+          MethodTree method = (MethodTree) actualNode;
+          int arg = astNode.getArgument();
+          if (astNode.childSelectorIs(ASTPath.TYPE)) {
+            next = method.getReturnType();
+          } else if (astNode.childSelectorIs(ASTPath.PARAMETER)) {
+            next = arg == -1 ? method.getReceiverParameter()
+                : method.getParameters().get(arg);
+          } else if (astNode.childSelectorIs(ASTPath.TYPE_PARAMETER)) {
+            next = method.getTypeParameters().get(arg);
+          } else if (astNode.childSelectorIs(ASTPath.BODY)) {
+            next = method.getBody();
+          } else {  // THROWS?
+            return false;
+          }
+          break;
+        }
+        case METHOD_INVOCATION: {
+          MethodInvocationTree methodInvocation =
+              (MethodInvocationTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
+            int arg = astNode.getArgument();
+            List<? extends Tree> typeArgs = methodInvocation.getTypeArguments();
+            if (arg >= typeArgs.size()) {
+              return false;
+            }
+            next = typeArgs.get(arg);
+          } else if (astNode.childSelectorIs(ASTPath.METHOD_SELECT)) {
+            next = methodInvocation.getMethodSelect();
+          } else {
+            int arg = astNode.getArgument();
+            List<? extends ExpressionTree> args = methodInvocation.getArguments();
+            if (arg >= args.size()) {
+              return false;
+            }
+            next = args.get(arg);
+          }
+          break;
+        }
+        case NEW_ARRAY: {
+          NewArrayTree newArray = (NewArrayTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.TYPE)) {
+            int arg = astNode.getArgument();
+            if (arg < 0) {
+              next = newArray.getType();
+            } else {
+              return arg == depth;
+            }
+          } else if (astNode.childSelectorIs(ASTPath.DIMENSION)) {
+            int arg = astNode.getArgument();
+            List<? extends ExpressionTree> dims = newArray.getDimensions();
+            if (arg >= dims.size()) {
+              return false;
+            }
+            next = dims.get(arg);
+          } else {
+            int arg = astNode.getArgument();
+            List<? extends ExpressionTree> inits = newArray.getInitializers();
+            if (arg >= inits.size()) {
+              return false;
+            }
+            next = inits.get(arg);
+          }
+          break;
+        }
+        case NEW_CLASS: {
+          NewClassTree newClass = (NewClassTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.ENCLOSING_EXPRESSION)) {
+            next = newClass.getEnclosingExpression();
+          } else if (astNode.childSelectorIs(ASTPath.TYPE_ARGUMENT)) {
+            int arg = astNode.getArgument();
+            List<? extends Tree> typeArgs = newClass.getTypeArguments();
+            if (arg >= typeArgs.size()) {
+              return false;
+            }
+            next = typeArgs.get(arg);
+          } else if (astNode.childSelectorIs(ASTPath.IDENTIFIER)) {
+            next = newClass.getIdentifier();
+          } else if (astNode.childSelectorIs(ASTPath.ARGUMENT)) {
+            int arg = astNode.getArgument();
+            List<? extends ExpressionTree> args = newClass.getArguments();
+            if (arg >= args.size()) {
+              return false;
+            }
+            next = args.get(arg);
+          } else {
+            next = newClass.getClassBody();
+          }
+          break;
+        }
+        case PARAMETERIZED_TYPE: {
+          ParameterizedTypeTree parameterizedType =
+              (ParameterizedTypeTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.TYPE)) {
+            next = parameterizedType.getType();
+          } else {
+            int arg = astNode.getArgument();
+            List<? extends Tree> typeArgs = parameterizedType.getTypeArguments();
+            if (arg >= typeArgs.size()) {
+              return false;
+            }
+            next = typeArgs.get(arg);
+          }
+          break;
+        }
+        case PARENTHESIZED: {
+          ParenthesizedTree parenthesized = (ParenthesizedTree) actualNode;
+          next = parenthesized.getExpression();
+          break;
+        }
+        case RETURN: {
+          ReturnTree returnn = (ReturnTree) actualNode;
+          next = returnn.getExpression();
+          break;
+        }
+        case SWITCH: {
+          SwitchTree zwitch = (SwitchTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+            next = zwitch.getExpression();
+          } else {
+            int arg = astNode.getArgument();
+            List<? extends CaseTree> cases = zwitch.getCases();
+            if (arg >= cases.size()) {
+              return false;
+            }
+            next = cases.get(arg);
+          }
+          break;
+        }
+        case SYNCHRONIZED: {
+          SynchronizedTree synchronizzed = (SynchronizedTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.EXPRESSION)) {
+            next = synchronizzed.getExpression();
+          } else {
+            next = synchronizzed.getBlock();
+          }
+          break;
+        }
+        case THROW: {
+          ThrowTree throww = (ThrowTree) actualNode;
+          next = throww.getExpression();
+          break;
+        }
+        case TRY: {
+          TryTree tryy = (TryTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.BLOCK)) {
+            next = tryy.getBlock();
+          } else if (astNode.childSelectorIs(ASTPath.CATCH)) {
+            int arg = astNode.getArgument();
+            List<? extends CatchTree> catches = tryy.getCatches();
+            if (arg >= catches.size()) {
+              return false;
+            }
+            next = catches.get(arg);
+          } else if (astNode.childSelectorIs(ASTPath.FINALLY_BLOCK)) {
+            next = tryy.getFinallyBlock();
+          } else {
+            int arg = astNode.getArgument();
+            List<? extends Tree> resources = tryy.getResources();
+            if (arg >= resources.size()) {
+              return false;
+            }
+            next = resources.get(arg);
+          }
+          break;
+        }
+        case TYPE_CAST: {
+          TypeCastTree typeCast = (TypeCastTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.TYPE)) {
+            next = typeCast.getType();
+          } else {
+            next = typeCast.getExpression();
+          }
+          break;
+        }
+        case UNION_TYPE: {
+          UnionTypeTree unionType = (UnionTypeTree) actualNode;
+          int arg = astNode.getArgument();
+          List<? extends Tree> typeAlts = unionType.getTypeAlternatives();
+          if (arg >= typeAlts.size()) {
+            return false;
+          }
+          next = typeAlts.get(arg);
+          break;
+        }
+        case VARIABLE: {
+          VariableTree var = (VariableTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.INITIALIZER)) {
+            next = var.getInitializer();
+          } else {
+            next = var.getType();
+          }
+          break;
+        }
+        case WHILE_LOOP: {
+          WhileLoopTree whileLoop = (WhileLoopTree) actualNode;
+          if (astNode.childSelectorIs(ASTPath.CONDITION)) {
+            next = whileLoop.getCondition();
+          } else {
+            next = whileLoop.getStatement();
+          }
+          break;
+        }
+        default: {
+          if (isBinaryOperator(actualNode.getKind())) {
+            BinaryTree binary = (BinaryTree) actualNode;
+            if (astNode.childSelectorIs(ASTPath.LEFT_OPERAND)) {
+              next = binary.getLeftOperand();
+            } else {
+              next = binary.getRightOperand();
+            }
+          } else if (isCompoundAssignment(actualNode.getKind())) {
+            CompoundAssignmentTree compoundAssignment =
+                (CompoundAssignmentTree) actualNode;
+            if (astNode.childSelectorIs(ASTPath.VARIABLE)) {
+              next = compoundAssignment.getVariable();
+            } else {
+              next = compoundAssignment.getExpression();
+            }
+          } else if (isUnaryOperator(actualNode.getKind())) {
+            UnaryTree unary = (UnaryTree) actualNode;
+            next = unary.getExpression();
+          } else if (isWildcard(actualNode.getKind())) {
+            WildcardTree wildcard = (WildcardTree) actualNode;
+            // The following check is necessary because Oracle has decided that
+            //   x instanceof Class<? extends Object>
+            // will remain illegal even though it means the same thing as
+            //   x instanceof Class<?>.
+            if (i > 0) {  // TODO: refactor GenericArrayLoc to use same code?
+              Tree ancestor = actualPath.get(i-1);
+              if (ancestor.getKind() == Tree.Kind.INSTANCE_OF) {
+                System.err.println("WARNING: wildcard bounds not allowed"
+                    + " in 'instanceof' expression; skipping insertion");
+                return false;
+              } else if (i > 1 && ancestor.getKind() ==
+                  Tree.Kind.PARAMETERIZED_TYPE) {
+                ancestor = actualPath.get(i-2);
+                if (ancestor.getKind() == Tree.Kind.ARRAY_TYPE) {
+                  System.err.println("WARNING: wildcard bounds not allowed"
+                      + " in generic array type; skipping insertion");
+                  return false;
+                }
+              }
+            }
+            next = wildcard.getBound();
+          } else {
+            throw new IllegalArgumentException("Illegal kind: "
+                + actualNode.getKind());
+          }
+          break;
+        }
+        }
+
+        dbug.debug("next: %s%n", next);
+        if (next != actualPath.get(i + 1)) {
+          dbug.debug("no next match%n");
+          return false;
+        }
+      }
+      return true;
+    }
+
+    /**
+     * Determines if the given kinds match, false otherwise. Two kinds match if
+     * they're exactly the same or if the two kinds are both compound
+     * assignments, unary operators, binary operators or wildcards.
+     * <p>
+     * This is necessary because in the JAIF file these kinds are represented by
+     * their general types (i.e. BinaryOperator, CompoundOperator, etc.) rather
+     * than their kind (i.e. PLUS, MINUS, PLUS_ASSIGNMENT, XOR_ASSIGNMENT,
+     * etc.). Internally, a single kind is used to represent each general type
+     * (i.e. PLUS is used for BinaryOperator, PLUS_ASSIGNMENT is used for
+     * CompoundAssignment, etc.). Yet, the actual source nodes have the correct
+     * kind. So if an AST path entry has a PLUS kind, that really means it could
+     * be any BinaryOperator, resulting in PLUS matching any other
+     * BinaryOperator.
+     *
+     * @param kind1
+     *            the first kind to match
+     * @param kind2
+     *            the second kind to match
+     * @return {@code true} if the kinds match as described above, {@code false}
+     *         otherwise.
+     */
+    private static boolean kindsMatch(Tree.Kind kind1, Tree.Kind kind2) {
+      return kind1 == kind2
+          || (isCompoundAssignment(kind1) && isCompoundAssignment(kind2))
+          || (isUnaryOperator(kind1) && isUnaryOperator(kind2))
+          || (isBinaryOperator(kind1) && isBinaryOperator(kind2))
+          || (isWildcard(kind1) && isWildcard(kind2));
+    }
+
+    /**
+     * Determines if the given kind is a compound assignment.
+     *
+     * @param kind
+     *            the kind to test
+     * @return true if the given kind is a compound assignment
+     */
+    private static boolean isCompoundAssignment(Tree.Kind kind) {
+      return kind == Tree.Kind.PLUS_ASSIGNMENT
+          || kind == Tree.Kind.MINUS_ASSIGNMENT
+          || kind == Tree.Kind.MULTIPLY_ASSIGNMENT
+          || kind == Tree.Kind.DIVIDE_ASSIGNMENT
+          || kind == Tree.Kind.OR_ASSIGNMENT
+          || kind == Tree.Kind.AND_ASSIGNMENT
+          || kind == Tree.Kind.REMAINDER_ASSIGNMENT
+          || kind == Tree.Kind.LEFT_SHIFT_ASSIGNMENT
+          || kind == Tree.Kind.RIGHT_SHIFT
+          || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT
+          || kind == Tree.Kind.XOR_ASSIGNMENT;
+    }
+
+    /**
+     * Determines if the given kind is a unary operator.
+     *
+     * @param kind
+     *            the kind to test
+     * @return true if the given kind is a unary operator
+     */
+    private static boolean isUnaryOperator(Tree.Kind kind) {
+      return kind == Tree.Kind.POSTFIX_INCREMENT
+          || kind == Tree.Kind.POSTFIX_DECREMENT
+          || kind == Tree.Kind.PREFIX_INCREMENT
+          || kind == Tree.Kind.PREFIX_DECREMENT
+          || kind == Tree.Kind.UNARY_PLUS
+          || kind == Tree.Kind.UNARY_MINUS
+          || kind == Tree.Kind.BITWISE_COMPLEMENT
+          || kind == Tree.Kind.LOGICAL_COMPLEMENT;
+    }
+
+    /**
+     * Determines if the given kind is a binary operator.
+     *
+     * @param kind
+     *            the kind to test
+     * @return true if the given kind is a binary operator
+     */
+    private static boolean isBinaryOperator(Tree.Kind kind) {
+      return kind == Tree.Kind.MULTIPLY
+          || kind == Tree.Kind.DIVIDE
+          || kind == Tree.Kind.REMAINDER
+          || kind == Tree.Kind.PLUS
+          || kind == Tree.Kind.MINUS
+          || kind == Tree.Kind.LEFT_SHIFT
+          || kind == Tree.Kind.RIGHT_SHIFT
+          || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT
+          || kind == Tree.Kind.LESS_THAN
+          || kind == Tree.Kind.GREATER_THAN
+          || kind == Tree.Kind.LESS_THAN_EQUAL
+          || kind == Tree.Kind.GREATER_THAN_EQUAL
+          || kind == Tree.Kind.EQUAL_TO
+          || kind == Tree.Kind.NOT_EQUAL_TO
+          || kind == Tree.Kind.AND
+          || kind == Tree.Kind.XOR
+          || kind == Tree.Kind.OR
+          || kind == Tree.Kind.CONDITIONAL_AND
+          || kind == Tree.Kind.CONDITIONAL_OR;
+    }
+
+    /**
+     * Determines if the given kind is a wildcard.
+     *
+     * @param kind
+     *            the kind to test
+     * @return true if the given kind is a wildcard
+     */
+    private static boolean isWildcard(Tree.Kind kind) {
+      return kind == Tree.Kind.UNBOUNDED_WILDCARD
+          || kind == Tree.Kind.EXTENDS_WILDCARD
+          || kind == Tree.Kind.SUPER_WILDCARD;
+    }
+  }
+
+  public static boolean isTypeSelector(String selector) {
+    return Arrays.<String>binarySearch(typeSelectors,
+        selector, Collator.getInstance()) >= 0;
+  }
+
+  public static boolean isClassEquiv(Tree.Kind kind) {
+    switch (kind) {
+      case CLASS:
+      case INTERFACE:
+      case ENUM:
+      case ANNOTATION_TYPE:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  /**
+   * Determines if the given kind is a compound assignment.
+   *
+   * @param kind
+   *            the kind to test
+   * @return true if the given kind is a compound assignment
+   */
+  public static boolean isCompoundAssignment(Tree.Kind kind) {
+    return kind == Tree.Kind.PLUS_ASSIGNMENT
+        || kind == Tree.Kind.MINUS_ASSIGNMENT
+        || kind == Tree.Kind.MULTIPLY_ASSIGNMENT
+        || kind == Tree.Kind.DIVIDE_ASSIGNMENT
+        || kind == Tree.Kind.OR_ASSIGNMENT
+        || kind == Tree.Kind.AND_ASSIGNMENT
+        || kind == Tree.Kind.REMAINDER_ASSIGNMENT
+        || kind == Tree.Kind.LEFT_SHIFT_ASSIGNMENT
+        || kind == Tree.Kind.RIGHT_SHIFT_ASSIGNMENT
+        || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT
+        || kind == Tree.Kind.XOR_ASSIGNMENT;
+  }
+
+  /**
+   * Determines if the given kind is a unary operator.
+   *
+   * @param kind
+   *            the kind to test
+   * @return true if the given kind is a unary operator
+   */
+  public static boolean isUnaryOperator(Tree.Kind kind) {
+    return kind == Tree.Kind.POSTFIX_INCREMENT
+        || kind == Tree.Kind.POSTFIX_DECREMENT
+        || kind == Tree.Kind.PREFIX_INCREMENT
+        || kind == Tree.Kind.PREFIX_DECREMENT
+        || kind == Tree.Kind.UNARY_PLUS
+        || kind == Tree.Kind.UNARY_MINUS
+        || kind == Tree.Kind.BITWISE_COMPLEMENT
+        || kind == Tree.Kind.LOGICAL_COMPLEMENT;
+  }
+
+  /**
+   * Determines if the given kind is a binary operator.
+   *
+   * @param kind
+   *            the kind to test
+   * @return true if the given kind is a binary operator
+   */
+  public static boolean isBinaryOperator(Tree.Kind kind) {
+    return kind == Tree.Kind.MULTIPLY || kind == Tree.Kind.DIVIDE
+        || kind == Tree.Kind.REMAINDER || kind == Tree.Kind.PLUS
+        || kind == Tree.Kind.MINUS || kind == Tree.Kind.LEFT_SHIFT
+        || kind == Tree.Kind.RIGHT_SHIFT
+        || kind == Tree.Kind.UNSIGNED_RIGHT_SHIFT
+        || kind == Tree.Kind.LESS_THAN
+        || kind == Tree.Kind.GREATER_THAN
+        || kind == Tree.Kind.LESS_THAN_EQUAL
+        || kind == Tree.Kind.GREATER_THAN_EQUAL
+        || kind == Tree.Kind.EQUAL_TO || kind == Tree.Kind.NOT_EQUAL_TO
+        || kind == Tree.Kind.AND || kind == Tree.Kind.XOR
+        || kind == Tree.Kind.OR || kind == Tree.Kind.CONDITIONAL_AND
+        || kind == Tree.Kind.CONDITIONAL_OR;
+  }
+
+  public static boolean isLiteral(Tree.Kind kind) {
+    switch (kind) {
+    case INT_LITERAL:
+    case LONG_LITERAL:
+    case FLOAT_LITERAL:
+    case DOUBLE_LITERAL:
+    case BOOLEAN_LITERAL:
+    case CHAR_LITERAL:
+    case STRING_LITERAL:
+    case NULL_LITERAL:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  public static boolean isExpression(Tree.Kind kind) {
+    switch (kind) {
+    case ARRAY_ACCESS:
+    case ASSIGNMENT:
+    case CONDITIONAL_EXPRESSION:
+    case EXPRESSION_STATEMENT:
+    case MEMBER_SELECT:
+    case MEMBER_REFERENCE:
+    case IDENTIFIER:
+    case INSTANCE_OF:
+    case METHOD_INVOCATION:
+    case NEW_ARRAY:
+    case NEW_CLASS:
+    case LAMBDA_EXPRESSION:
+    case PARENTHESIZED:
+    case TYPE_CAST:
+      return true;
+    default:
+      return isUnaryOperator(kind) || isBinaryOperator(kind)
+          || isCompoundAssignment(kind) || isLiteral(kind);
+    }
+  }
+
+  public static boolean isTypeKind(Tree.Kind kind) {
+    switch (kind) {
+    case ANNOTATED_TYPE:
+    case ARRAY_TYPE:
+    case IDENTIFIER:
+    case INTERSECTION_TYPE:
+    // case MEMBER_SELECT:
+    case PARAMETERIZED_TYPE:
+    case PRIMITIVE_TYPE:
+    case UNION_TYPE:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  /**
+   * Determines if the given kind is a wildcard.
+   *
+   * @param kind
+   *            the kind to test
+   * @return true if the given kind is a wildcard
+   */
+  public static boolean isWildcard(Tree.Kind kind) {
+    return kind == Tree.Kind.UNBOUNDED_WILDCARD
+        || kind == Tree.Kind.EXTENDS_WILDCARD
+        || kind == Tree.Kind.SUPER_WILDCARD;
+  }
+
+  /**
+   * Determines if the given kind is a declaration.
+   *
+   * @param kind
+   *            the kind to test
+   * @return true if the given kind is a declaration
+   */
+  public static boolean isDeclaration(Tree.Kind kind) {
+    return kind == Tree.Kind.ANNOTATION
+        || kind == Tree.Kind.CLASS
+        || kind == Tree.Kind.ENUM
+        || kind == Tree.Kind.INTERFACE
+        || kind == Tree.Kind.METHOD
+        || kind == Tree.Kind.VARIABLE;
+  }
+
+  /**
+   * Determines whether an {@code ASTPath} can identify nodes of the
+   *  given kind.
+   *
+   * @param kind
+   *            the kind to test
+   * @return true if the given kind can be identified by an {@code ASTPath}.
+   */
+  public static boolean isHandled(Tree.Kind kind) {
+    switch (kind) {
+    case BREAK:
+    case COMPILATION_UNIT:
+    case CONTINUE:
+    case IMPORT:
+    case MODIFIERS:
+      return false;
+    default:
+      return !isDeclaration(kind);
+    }
+  }  // TODO: need "isType"?
+}
+
+/**
+ *
+ * @author dbro
+ *
+ * @param <E> type of stack elements
+ */
+class ConsStack<E> implements PersistentStack<E> {
+  private int size;
+  private E elem;
+  private PersistentStack<E> rest;
+
+  public ConsStack() {
+    size = 0;
+    elem = null;
+    rest = null;
+  }
+
+  private static <T, S extends ConsStack<T>> S extend(T el, S s0) {
+    try {
+      @SuppressWarnings("unchecked")
+      S s1 = (S) s0.getClass().newInstance();
+      ConsStack<T> cs = (ConsStack<T>) s1;
+      cs.size = 1 + s0.size();
+      cs.elem = el;
+      cs.rest = s0;
+      return s1;
+    } catch (InstantiationException e) {
+      throw new RuntimeException(e);
+    } catch (IllegalAccessException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public boolean isEmpty() { return size == 0; }
+
+  @Override
+  public E peek() {
+    if (size > 0) { return elem; }
+    throw new IllegalStateException("peek() on empty stack");
+  }
+
+  @Override
+  public PersistentStack<E> pop() {
+    if (size > 0) { return rest; }
+    throw new IllegalStateException("pop() on empty stack");
+  }
+
+  @Override
+  public PersistentStack<E> push(E elem) {
+    return extend(elem, this);
+  }
+
+  @Override
+  public int size() { return size; }
+
+  @Override
+  public String toString() {
+    if (size > 0) {
+      StringBuilder sb = new StringBuilder("]").insert(0, peek());
+      for (PersistentStack<E> stack = pop(); !stack.isEmpty();
+          stack = stack.pop()) {
+        sb = sb.insert(0, ", ").insert(0, stack.peek());
+      }
+      return sb.insert(0, "[").toString();
+    }
+    return "[]";
+  }
+}
diff --git a/scene-lib/src/annotations/io/ASTRecord.java b/scene-lib/src/annotations/io/ASTRecord.java
new file mode 100644
index 0000000..648238e
--- /dev/null
+++ b/scene-lib/src/annotations/io/ASTRecord.java
@@ -0,0 +1,195 @@
+package annotations.io;
+
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.util.TreePath;
+
+/**
+ * Structure bundling an {@link ASTPath} with information about its
+ *  starting point. Necessary because the {@link ASTPath} structure
+ *  does not include the declaration from which it originates.
+ *
+ * @author dbro
+ */
+public class ASTRecord implements Comparable<ASTRecord> {
+  /**
+   * The AST to which this {@code ASTRecord} pertains.
+   */
+  public final CompilationUnitTree ast;
+
+  /**
+   * Name of the enclosing class declaration.
+   */
+  public final String className;
+
+  /**
+   * Name of the enclosing method declaration, or null if there is none.
+   */
+  public final String methodName;
+
+  /**
+   * Name of the enclosing variable declaration, or null if there is none.
+   */
+  public final String varName;
+
+  /**
+   * Path through AST, from specified declaration to descendant node.
+   */
+  public final ASTPath astPath;
+
+  public ASTRecord(CompilationUnitTree ast, String className,
+      String methodName, String varName, ASTPath astPath) {
+    this.ast = ast;
+    this.className = className;
+    this.methodName = methodName;
+    this.varName = varName;
+    // FIXME: ensure path is canonical
+    if (varName != null) {
+      // TODO?
+    } else if (methodName != null) {
+      int n = astPath.size();
+      if (n > 0 && astPath.get(0).getTreeKind() != Tree.Kind.METHOD
+          && astPath.get(0).getTreeKind() != Tree.Kind.VARIABLE) {
+        ASTPath bodyPath = ASTPath.empty().add(
+            new ASTPath.ASTEntry(Tree.Kind.METHOD, ASTPath.BODY));
+        for (int i = 0; i < n; i++) { bodyPath = bodyPath.add(astPath.get(i)); }
+        astPath = bodyPath;
+      }
+    }
+    this.astPath = astPath;
+  }
+
+  public ASTRecord newArrayLevel(int depth) {
+    return new ASTRecord(ast, className, methodName, varName,
+        astPath.extendNewArray(depth));
+  }
+
+  public ASTRecord replacePath(ASTPath newPath) {
+    return new ASTRecord(ast, className, methodName, varName, newPath);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return o instanceof ASTRecord && equals((ASTRecord) o);
+  }
+
+  public boolean equals(ASTRecord astRecord) {
+    return compareTo(astRecord) == 0;
+  }
+
+  @Override
+  public int compareTo(ASTRecord rec) {
+    int d = ast == null
+        ? rec.ast == null ? 0 : -1
+        : rec.ast == null ? 1 : Integer
+            .compare(ast.hashCode(), rec.ast.hashCode());
+    if (d == 0) {
+      d = className == null
+          ? rec.className == null ? 0 : -1
+          : rec.className == null ? 1 : className.compareTo(rec.className);
+      if (d == 0) {
+        d = methodName == null
+            ? rec.methodName == null ? 0 : -1
+            : rec.methodName == null ? 1 : methodName.compareTo(rec.methodName);
+        if (d == 0) {
+          d = varName == null
+              ? rec.varName == null ? 0 : -1
+              : rec.varName == null ? 1 : varName.compareTo(rec.varName);
+          if (d == 0) {
+            d = astPath == null
+                ? rec.astPath == null ? 0 : -1
+                : rec.astPath == null ? 1 : astPath.compareTo(rec.astPath);
+          }
+        }
+      }
+    }
+    return d;
+  }
+
+  @Override
+  public int hashCode() {
+    return ast.hashCode()
+        ^ (className == null ? 0
+            : Integer.rotateRight(className.hashCode(), 3))
+        ^ (methodName == null ? 0
+            : Integer.rotateRight(methodName.hashCode(), 6))
+        ^ (varName == null ? 0
+            : Integer.rotateRight(varName.hashCode(), 9))
+        ^ (astPath == null ? 0
+            : Integer.rotateRight(astPath.hashCode(), 12));
+  }
+
+  /**
+   * Indicates whether this record identifies the given {@link TreePath}.
+   */
+  public boolean matches(TreePath treePath) {
+    String clazz = null;
+    String meth = null;
+    String var = null;
+    boolean matchVars = false;  // members only!
+    Deque<Tree> stack = new ArrayDeque<Tree>();
+    for (Tree tree : treePath) { stack.push(tree); }
+    while (!stack.isEmpty()) {
+      Tree tree = stack.pop();
+      switch (tree.getKind()) {
+      case CLASS:
+      case INTERFACE:
+      case ENUM:
+      case ANNOTATION_TYPE:
+        clazz = ((ClassTree) tree).getSimpleName().toString();
+        meth = null;
+        var = null;
+        matchVars = true;
+        break;
+      case METHOD:
+        assert meth == null;
+        meth = ((MethodTree) tree).getName().toString();
+        matchVars = false;
+        break;
+      case VARIABLE:
+        if (matchVars) {
+          assert var == null;
+          var = ((VariableTree) tree).getName().toString();
+          matchVars = false;
+        }
+        break;
+      default:
+        matchVars = false;
+        continue;
+      }
+    }
+    return className.equals(clazz)
+        && (methodName == null ? meth == null : methodName.equals(meth))
+        && (varName == null ? var == null : varName.equals(var))
+        && astPath.matches(treePath);
+  }
+
+  @Override
+  public String toString() {
+    return new StringBuilder()
+        .append(className == null ? "" : className).append(":")
+        .append(methodName == null ? "" : methodName).append(":")
+        .append(varName == null ? "" : varName).append(":")
+        .append(astPath).toString();
+  }
+
+  public ASTRecord extend(ASTPath.ASTEntry entry) {
+    return new ASTRecord(ast, className, methodName, varName,
+        astPath.extend(entry));
+  }
+
+  public ASTRecord extend(Kind kind, String sel) {
+    return extend(new ASTPath.ASTEntry(kind, sel));
+  }
+
+  public ASTRecord extend(Kind kind, String sel, int arg) {
+    return extend(new ASTPath.ASTEntry(kind, sel, arg));
+  }
+}
diff --git a/scene-lib/src/annotations/io/DebugWriter.java b/scene-lib/src/annotations/io/DebugWriter.java
new file mode 100644
index 0000000..372ad42
--- /dev/null
+++ b/scene-lib/src/annotations/io/DebugWriter.java
@@ -0,0 +1,43 @@
+package annotations.io;
+
+import java.io.PrintWriter;
+import java.util.logging.Level;
+
+/**
+ * @author dbro
+ */
+public class DebugWriter {
+  private PrintWriter out = new PrintWriter(System.out);
+  private Level level = Level.WARNING;
+
+  public DebugWriter or(final DebugWriter other) {
+    return new DebugWriter() {
+      @Override
+      public boolean isEnabled() {
+        return super.isEnabled() || other.isEnabled();
+      }
+    };
+  }
+
+  public static boolean anyEnabled(DebugWriter... debugs) {
+    for (DebugWriter debug : debugs) {
+      if (debug.isEnabled()) { return true; }
+    }
+    return false;
+  }
+
+  public boolean isEnabled() {
+    return level == Level.INFO;
+  }
+
+  public void setEnabled(boolean enabled) {
+    level = enabled ? Level.INFO : Level.WARNING;
+  }
+
+  public void debug(String format, Object... args) {
+    if (isEnabled()) {
+      out.print(String.format(format, args));
+      out.flush();
+    }
+  }
+}
diff --git a/scene-lib/src/annotations/io/IOUtils.java b/scene-lib/src/annotations/io/IOUtils.java
new file mode 100644
index 0000000..cc72257
--- /dev/null
+++ b/scene-lib/src/annotations/io/IOUtils.java
@@ -0,0 +1,23 @@
+package annotations.io;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * <code>IOUtils</code> has some static methods useful to scene I/O code.
+ */
+class IOUtils {
+    private IOUtils() {
+    }
+
+    static String packagePart(String className) {
+        int lastdot = className.lastIndexOf('.');
+        return (lastdot == -1) ? "" : className.substring(0, lastdot);
+    }
+
+    static String basenamePart(String className) {
+        int lastdot = className.lastIndexOf('.');
+        return (lastdot == -1) ? className : className.substring(lastdot + 1);
+    }
+}
diff --git a/scene-lib/src/annotations/io/IndexFileParser.java b/scene-lib/src/annotations/io/IndexFileParser.java
new file mode 100644
index 0000000..c6b83bb
--- /dev/null
+++ b/scene-lib/src/annotations/io/IndexFileParser.java
@@ -0,0 +1,1726 @@
+package annotations.io;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import static java.io.StreamTokenizer.TT_EOF;
+import static java.io.StreamTokenizer.TT_NUMBER;
+import static java.io.StreamTokenizer.TT_WORD;
+
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.Reader;
+import java.io.StreamTokenizer;
+import java.io.StringReader;
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+import com.sun.source.tree.Tree.Kind;
+import com.sun.tools.javac.code.TypeAnnotationPosition;
+
+import annotations.Annotation;
+import annotations.AnnotationBuilder;
+import annotations.AnnotationFactory;
+import annotations.Annotations;
+import annotations.ArrayBuilder;
+import annotations.el.ABlock;
+import annotations.el.AClass;
+import annotations.el.ADeclaration;
+import annotations.el.AElement;
+import annotations.el.AExpression;
+import annotations.el.AField;
+import annotations.el.AMethod;
+import annotations.el.AScene;
+import annotations.el.ATypeElement;
+import annotations.el.ATypeElementWithType;
+import annotations.el.AnnotationDef;
+import annotations.el.BoundLocation;
+import annotations.el.InnerTypeLocation;
+import annotations.el.LocalLocation;
+import annotations.el.RelativeLocation;
+import annotations.el.TypeIndexLocation;
+import annotations.field.AnnotationAFT;
+import annotations.field.AnnotationFieldType;
+import annotations.field.ArrayAFT;
+import annotations.field.BasicAFT;
+import annotations.field.ClassTokenAFT;
+import annotations.field.EnumAFT;
+import annotations.field.ScalarAFT;
+import annotations.util.coll.VivifyingMap;
+
+import plume.ArraysMDE;
+import plume.FileIOException;
+import plume.Pair;
+
+import type.ArrayType;
+import type.BoundedType;
+import type.BoundedType.BoundKind;
+import type.DeclaredType;
+import type.Type;
+
+/**
+ * IndexFileParser provides static methods
+ * {@link #parse(LineNumberReader, AScene)},
+ * {@link #parseFile(String, AScene)}, and
+ * {@link #parseString(String, AScene)}.
+ * Each of these parses an index file into a {@link AScene}.
+ * <p>
+ *
+ * If there are any problems, it throws a ParseException internally, or a
+ * FileIOException externally.
+ */
+public final class IndexFileParser {
+
+    private static final String[] typeSelectors = { "bound", "identifier",
+        "type", "typeAlternative", "typeArgument", "typeParameter",
+        "underlyingType" };
+
+    private static boolean abbreviate = true;
+
+    // The input
+    private final StreamTokenizer st;
+
+    // The output
+    private final AScene scene;
+
+    private String curPkgPrefix;
+
+    /**
+     * Holds definitions we've seen so far.  Maps from annotation name to
+     * the definition itself.  Maps from both the qualified name and the
+     * unqualified name.  If the unqualified name is not unique, it maps
+     * to null and the qualified name should be used instead. */
+    private final HashMap<String, AnnotationDef> defs;
+
+    public static void setAbbreviate(boolean b) {
+      abbreviate = b;
+    }
+
+    private int expectNonNegative(int i) throws ParseException {
+        if (i >= 0) {
+            return i;
+        } else {
+            throw new ParseException("Expected a nonnegative integer, got " + st);
+        }
+    }
+
+    /** True if the next thing from st is the given character. */
+    private boolean checkChar(char c) {
+        return st.ttype == c;
+    }
+
+    /** True if the next thing from st is the given string token. */
+    private boolean checkKeyword(String s) {
+        return st.ttype == TT_WORD && st.sval.equals(s);
+    }
+
+    /**
+     * Return true if the next thing to be read from st is the given string.
+     * In that case, also read past the given string.
+     * If the result is false, reads nothing from st.
+     */
+    private boolean matchChar(char c) throws IOException {
+        if (checkChar(c)) {
+            st.nextToken();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Return true if the next thing to be read from st is the given string.
+     * In that case, also read past the given string.
+     * If the result is false, reads nothing from st.
+     */
+    private boolean matchKeyword(String s) throws IOException {
+        if (checkKeyword(s)) {
+            st.nextToken();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /** Reads from st.  If the result is not c, throws an exception. */
+    private void expectChar(char c) throws IOException, ParseException {
+        if (! matchChar(c)) {
+            // Alternately, could use st.toString().
+            String found;
+            switch (st.ttype) {
+            case StreamTokenizer.TT_WORD: found = st.sval; break;
+            case StreamTokenizer.TT_NUMBER: found = "" + st.nval; break;
+            case StreamTokenizer.TT_EOL: found = "end of line"; break;
+            case StreamTokenizer.TT_EOF: found = "end of file"; break;
+            default: found = "'" + ((char) st.ttype) + "'"; break;
+            }
+            throw new ParseException("Expected '" + c + "', found " + found);
+        }
+    }
+
+    /** Reads from st.  If the result is not s, throws an exception. */
+    private void expectKeyword(String s) throws IOException,
+            ParseException {
+        if (! matchKeyword(s)) {
+            throw new ParseException("Expected `" + s + "'");
+        }
+    }
+
+    private static final Set<String> knownKeywords;
+    static {
+        String[] knownKeywords_array =
+                { "abstract", "assert", "boolean", "break", "byte", "case",
+                        "catch", "char", "class", "const", "continue",
+                        "default", "do", "double", "else", "enum", "extends",
+                        "false", "final", "finally", "float", "for", "if",
+                        "goto", "implements", "import", "instanceof", "int",
+                        "interface", "long", "native", "new", "null",
+                        "package", "private", "protected", "public", "return",
+                        "short", "static", "strictfp", "super", "switch",
+                        "synchronized", "this", "throw", "throws", "transient",
+                        "true", "try", "void", "volatile", "while", };
+        knownKeywords = new LinkedHashSet<String>();
+        Collections.addAll(knownKeywords, knownKeywords_array);
+    }
+
+    private boolean isValidIdentifier(String x) {
+        if (x.length() == 0 || !Character.isJavaIdentifierStart(x.charAt(0))
+                || knownKeywords.contains(x))
+            return false;
+        for (int i = 1; i < x.length(); i++) {
+            if (!Character.isJavaIdentifierPart(x.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private String checkIdentifier() {
+        if (st.sval == null) {
+            return null;
+        } else {
+            String val = st.sval;
+            if (st.ttype == TT_WORD && isValidIdentifier(val)) {
+                return st.sval;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    private String matchIdentifier() throws IOException {
+        String x = checkIdentifier();
+        if (x != null) {
+            st.nextToken();
+            return x;
+        } else {
+            return null;
+        }
+    }
+
+    private String expectIdentifier() throws IOException, ParseException {
+        String id = matchIdentifier();
+        if (id == null) { throw new ParseException("Expected an identifier"); }
+        return id;
+    }
+
+    private String checkPrimitiveType() {
+        if (st.sval == null) {
+            return null;
+        } else {
+            String val = st.sval;
+            if (st.ttype == TT_WORD && primitiveTypes.containsKey(val)) {
+                return st.sval;
+            } else {
+                return null;
+            }
+        }
+    }
+
+    private String matchPrimitiveType() throws IOException {
+        String x = checkPrimitiveType();
+        if (x != null) {
+            st.nextToken();
+            return x;
+        } else {
+            return null;
+        }
+    }
+
+    // an identifier, or a sequence of dot-separated identifiers
+    private String expectQualifiedName() throws IOException, ParseException {
+        String name = expectIdentifier();
+        while (matchChar('.')) {
+            name += '.' + expectIdentifier();
+        }
+        return name;
+    }
+
+    private int checkNNInteger() {
+        if (st.ttype == TT_NUMBER) {
+            int x = (int) st.nval;
+            if (x == st.nval && x >= -1) // shouldn't give us a huge number
+                return x;
+        }
+        return -1;
+    }
+
+    private int matchNNInteger() throws IOException {
+        int x = checkNNInteger();
+        if (x >= -1) {
+            st.nextToken();
+            return x;
+        } else {
+            return -1;
+        }
+    }
+
+    // Mapping from primitive types and void to their corresponding
+    // class objects. Class.forName doesn't directly support these.
+    // Using this map we can go from "void.class" to the correct
+    // Class object.
+    private static final Map<String, Class<?>> primitiveTypes;
+    static {
+        Map<String, Class<?>> pt = new LinkedHashMap<String, Class<?>>();
+        pt.put("byte", byte.class);
+        pt.put("short", short.class);
+        pt.put("int", int.class);
+        pt.put("long", long.class);
+        pt.put("float", float.class);
+        pt.put("double", double.class);
+        pt.put("char", char.class);
+        pt.put("boolean", boolean.class);
+        pt.put("void", void.class);
+        primitiveTypes = pt;
+    }
+
+    /** Parse scalar annotation value. */
+    // HMMM can a (readonly) Integer be casted to a writable Object?
+    private Object parseScalarAFV(ScalarAFT aft) throws IOException, ParseException {
+        if (aft instanceof BasicAFT) {
+            Object val;
+            BasicAFT baft = (BasicAFT) aft;
+            Class<?> type = baft.type;
+            if (type == boolean.class) {
+                if (matchKeyword("true")) {
+                    val = true;
+                } else if (matchKeyword("false")) {
+                    val = false;
+                } else {
+                    throw new ParseException("Expected `true' or `false'");
+                }
+            } else if (type == char.class) {
+                if (st.ttype == '\'' && st.sval.length() == 1) {
+                    val = st.sval.charAt(0);
+                } else {
+                    throw new ParseException("Expected a character literal");
+                }
+                st.nextToken();
+            } else if (type == String.class) {
+                if (st.ttype == '"') {
+                    val = st.sval;
+                } else {
+                    throw new ParseException("Expected a string literal");
+                }
+                st.nextToken();
+            } else {
+                if (st.ttype == TT_NUMBER) {
+                    double n = st.nval;
+                    // TODO validate the literal better
+                    // HMMM StreamTokenizer can't handle all floating point
+                    // numbers; in particular, scientific notation is a problem
+                    if (type == byte.class) {
+                        val = (byte) n;
+                    } else if (type == short.class) {
+                        val = (short) n;
+                    } else if (type == int.class) {
+                        val = (int) n;
+                    } else if (type == long.class) {
+                        val = (long) n;
+                    } else if (type == float.class) {
+                        val = (float) n;
+                    } else if (type == double.class) {
+                        val = n;
+                    } else {
+                        throw new AssertionError();
+                    }
+                    st.nextToken();
+                } else {
+                    throw new ParseException(
+                            "Expected a number literal");
+                }
+            }
+            assert aft.isValidValue(val);
+            return val;
+        } else if (aft instanceof ClassTokenAFT) {
+            // Expect the class name in the format that Class.forName accepts,
+            // which is some very strange format.
+            // Example inputs followed by their Java source ".class" equivalent:
+            //   [[I.class      for int[][].class
+            //   [java.util.Map for Map[].class
+            //   java.util.Map  for Map.class
+            // Have to use fully-qualified names, i.e. "Object" alone won't work.
+            // Also note use of primitiveTypes map for primitives and void.
+            int arrays = 0;
+            StringBuilder type = new StringBuilder();
+            while (matchChar('[')) {
+                // Array dimensions as prefix
+                ++arrays;
+            }
+            while (!matchKeyword("class")) {
+                if (st.ttype >= 0) {
+                    type.append((char) st.ttype);
+                } else if (st.ttype == TT_WORD) {
+                    type.append(st.sval);
+                } else {
+                    throw new ParseException("Found something that doesn't belong in a signature");
+                }
+                st.nextToken();
+            }
+
+            // Drop the '.' before the "class"
+            type.deleteCharAt(type.length()-1);
+            // expectKeyword("class");
+
+            // Add arrays as prefix in the type.
+            while (arrays-->0) {
+                type.insert(0, '[');
+            }
+
+            try {
+                String sttype = type.toString();
+                Class<?> tktype;
+                if (primitiveTypes.containsKey(sttype)) {
+                    tktype = primitiveTypes.get(sttype);
+                } else {
+                    tktype = Class.forName(sttype);
+                }
+                assert aft.isValidValue(tktype);
+                return tktype;
+            } catch (ClassNotFoundException e) {
+                throw new ParseException("Could not load class: " + type, e);
+            }
+        } else if (aft instanceof EnumAFT) {
+            String name = expectQualifiedName();
+            assert aft.isValidValue(name);
+            return name;
+        } else if (aft instanceof AnnotationAFT) {
+            AnnotationAFT aaft = (AnnotationAFT) aft;
+            AnnotationDef d = parseAnnotationHead();
+            if (! d.name.equals(aaft.annotationDef.name)) {
+                throw new ParseException("Got an " + d.name
+                    + " subannotation where an " + aaft.annotationDef.name
+                    + " was expected");
+            }
+            AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(d);
+            // interested in this annotation,
+            // so should be interested in subannotations
+            assert ab != null;
+            AnnotationBuilder ab2 = (AnnotationBuilder) ab;
+            Annotation suba = parseAnnotationBody(d, ab2);
+            assert aft.isValidValue(suba);
+            return suba;
+        } else {
+            throw new AssertionError("IndexFileParser.parseScalarAFV: unreachable code.");
+        }
+    }
+
+    private void parseAndAddArrayAFV(ArrayAFT aaft, ArrayBuilder arrb) throws IOException, ParseException {
+        ScalarAFT comp;
+        if (aaft.elementType != null) {
+            comp = aaft.elementType;
+        } else {
+            throw new IllegalArgumentException("array AFT has null elementType");
+        }
+        if (matchChar('{')) {
+            // read an array
+            while (!matchChar('}')) {
+                arrb.appendElement(parseScalarAFV(comp));
+                if (!checkChar('}')) {
+                    expectChar(',');
+                }
+            }
+        } else {
+            // not an array, so try reading just one value as an array
+            arrb.appendElement(parseScalarAFV(comp));
+        }
+        arrb.finish();
+    }
+
+    // parses a field such as "f1=5" in "@A(f1=5, f2=10)".
+    private void parseAnnotationField(AnnotationDef d, AnnotationBuilder ab) throws IOException, ParseException {
+        String fieldName;
+        if (d.fieldTypes.size() == 1
+            && d.fieldTypes.containsKey("value")) {
+            fieldName = "value";
+            if (matchKeyword("value")) {
+                expectChar('=');
+            }
+        } else {
+            fieldName = expectIdentifier();
+            expectChar('=');
+        }
+        // HMMM let's hope the builder checks for duplicate fields
+        // because we can't do it any more
+        AnnotationFieldType aft1 = d.fieldTypes.get(fieldName);
+        if (aft1 == null) {
+            throw new ParseException("The annotation type " + d.name
+                + " has no field called " + fieldName);
+        }
+        AnnotationFieldType aft = (AnnotationFieldType) aft1;
+        if (aft instanceof ArrayAFT) {
+            ArrayAFT aaft = (ArrayAFT) aft;
+            if (aaft.elementType == null) {
+                // Array of unknown element type--must be zero-length
+                expectChar('{');
+                expectChar('}');
+                ab.addEmptyArrayField(fieldName);
+            } else {
+                parseAndAddArrayAFV(aaft, ab.beginArrayField(fieldName, aaft));
+            }
+        } else if (aft instanceof ScalarAFT) {
+            ScalarAFT saft = (ScalarAFT) aft;
+            Object value = parseScalarAFV(saft);
+            ab.addScalarField(fieldName, saft, value);
+        } else {
+            throw new AssertionError();
+        }
+    }
+
+    // reads the "@A" part of an annotation such as "@A(f1=5, f2=10)".
+    private AnnotationDef parseAnnotationHead() throws IOException,
+            ParseException {
+        expectChar('@');
+        String name = expectQualifiedName();
+        AnnotationDef d = defs.get(name);
+        if (d == null) {
+            // System.err.println("No definition for annotation type " + name);
+            // System.err.printf("  defs contains %d entries%n", defs.size());
+            // for (Map.Entry<String,AnnotationDef> entry : defs.entrySet()) {
+            //    System.err.printf("    defs entry: %s => %s%n", entry.getKey(), entry.getValue());
+            // }
+            throw new ParseException("No definition for annotation type " + name);
+        }
+        return d;
+    }
+
+    private Annotation parseAnnotationBody(AnnotationDef d, AnnotationBuilder ab) throws IOException, ParseException {
+        if (matchChar('(')) {
+            parseAnnotationField(d, ab);
+            while (matchChar(',')) {
+                parseAnnotationField(d, ab);
+            }
+            expectChar(')');
+        }
+        Annotation ann = ab.finish();
+        if (! ann.def.equals(d)) {
+            throw new ParseException(
+                "parseAnnotationBody: Annotation def isn't as it should be.\n" + d + "\n" + ann.def);
+        }
+        if (ann.def().fieldTypes.size() != d.fieldTypes.size()) {
+            throw new ParseException(
+                "At least one annotation field is missing");
+        }
+        return ann;
+    }
+
+    private void parseAnnotations(AElement e)
+            throws IOException, ParseException {
+        while (checkChar('@')) {
+            AnnotationDef d = parseAnnotationHead();
+            AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(d);
+            if (ab == null) {
+                // don't care about the result
+                // but need to skip over it anyway
+                @SuppressWarnings("unused")
+                Object trash = parseAnnotationBody(d, AnnotationFactory.saf
+                        .beginAnnotation(d));
+            } else {
+                Annotation a = parseAnnotationBody(d, ab);
+                for (Annotation other : e.tlAnnotationsHere) {
+                    if (a.def.name.equals(other.def.name)) {
+                        System.err.println(
+                                "WARNING: duplicate annotation of type "
+                                        + a.def().name);
+                        continue;
+                    }
+                }
+                Annotation tla = a;
+                if (! tla.def.equals(d)) {
+                    throw new ParseException("Bad def");
+                }
+                e.tlAnnotationsHere.add(tla);
+            }
+        }
+    }
+
+    private ScalarAFT parseScalarAFT() throws IOException, ParseException {
+        for (BasicAFT baft : BasicAFT.bafts.values()) {
+            if (matchKeyword(baft.toString())) {
+                return baft;
+            }
+        }
+        // wasn't a BasicAFT
+        if (matchKeyword("Class")) {
+            return ClassTokenAFT.ctaft/* dumpParameterization() */;
+        } else if (matchKeyword("enum")) {
+            String name = expectQualifiedName();
+            if (abbreviate) {
+              int i = name.lastIndexOf('.');
+              if (i >= 0) {
+                String baseName = name.substring(i+1);
+                Set<String> set1 = scene.imports.get(name);
+                Set<String> set2 = scene.imports.get(baseName);
+                if (set1 == null) {
+                  set1 = new TreeSet<String>();
+                  scene.imports.put(name, set1);
+                }
+                if (set2 == null) {
+                  set2 = new TreeSet<String>();
+                  scene.imports.put(name, set2);
+                }
+                set1.add(name);
+                set2.add(name);
+                name = baseName;
+              }
+            }
+            return new EnumAFT(name);
+        } else if (matchKeyword("annotation-field")) {
+            String name = expectQualifiedName();
+            AnnotationDef ad = defs.get(name);
+            if (ad == null) {
+                throw new ParseException("Annotation type " + name + " used as a field before it is defined");
+            }
+            return new AnnotationAFT((AnnotationDef) ad);
+        } else {
+            throw new ParseException(
+                    "Expected the beginning of an annotation field type: "
+                    + "a primitive type, `String', `Class', `enum', or `annotation-field'. Got '"
+                    + st.sval + "'.");
+        }
+    }
+
+    private AnnotationFieldType parseAFT() throws IOException,
+            ParseException {
+        if (matchKeyword("unknown")) {
+            // Handle unknown[]; see AnnotationBuilder#addEmptyArrayField
+            expectChar('[');
+            expectChar(']');
+            return new ArrayAFT(null);
+        }
+        ScalarAFT baseAFT = parseScalarAFT();
+        // only one level of array is permitted
+        if (matchChar('[')) {
+            expectChar(']');
+            return new ArrayAFT(baseAFT);
+        } else {
+            return baseAFT;
+        }
+    }
+
+    private void parseAnnotationDef() throws IOException, ParseException {
+        expectKeyword("annotation");
+
+        expectChar('@');
+        String basename = expectIdentifier();
+        String fullName = curPkgPrefix + basename;
+
+        AnnotationDef ad = new AnnotationDef(fullName);
+        expectChar(':');
+        parseAnnotations(ad);
+
+        Map<String, AnnotationFieldType> fields =
+                new LinkedHashMap<String, AnnotationFieldType>();
+
+        // yuck; it would be nicer to do a positive match
+        while (st.ttype != TT_EOF && !checkKeyword("annotation")
+               && !checkKeyword("class") && !checkKeyword("package")) {
+            AnnotationFieldType type = parseAFT();
+            String name = expectIdentifier();
+            if (fields.containsKey(name)) {
+                throw new ParseException("Duplicate definition of field "
+                                         + name);
+            }
+            fields.put(name, type);
+        }
+
+        ad.setFieldTypes(fields);
+
+        // Now add the definition to the map of all definitions.
+        addDef(ad, basename);
+
+    }
+
+    // Add the definition to the map of all definitions.
+    // also see addDef(AnnotationDef, String).
+    public void addDef(AnnotationDef ad) throws ParseException {
+        String basename = ad.name;
+        int dotPos = basename.lastIndexOf('.');
+        if (dotPos != -1) {
+            basename = basename.substring(dotPos + 1);
+        }
+        addDef(ad, basename);
+    }
+
+    // Add the definition to the map of all definitions.
+    public void addDef(AnnotationDef ad, String basename) throws ParseException {
+        // System.out.println("addDef:" + ad);
+
+        if (defs.containsKey(ad.name)) {
+            // TODO:  permit identical re-definition
+            System.err.println("Duplicate definition of annotation type " + ad.name);
+        }
+        defs.put(ad.name, ad);
+        // Add short name; but if it's already there, remove it to avoid ambiguity.
+        if (! basename.equals(ad.name)) {
+            if (defs.containsKey(basename)) {
+                // not "defs.remove(basename)" because then a subsequent
+                // one could get added, which would be wrong.
+                defs.put(basename, null);
+            } else {
+                defs.put(basename, ad);
+            }
+        }
+    }
+
+
+    private void parseInnerTypes(ATypeElement e)
+            throws IOException, ParseException {
+        parseInnerTypes(e, 0);
+    }
+
+    private void parseInnerTypes(ATypeElement e, int offset)
+            throws IOException, ParseException {
+        while (matchKeyword("inner-type")) {
+            ArrayList<Integer> locNumbers =
+                    new ArrayList<Integer>();
+            locNumbers.add(offset + expectNonNegative(matchNNInteger()));
+            // TODO: currently, we simply read the binary representation.
+            // Should we read a higher-level format?
+            while (matchChar(',')) {
+                locNumbers.add(expectNonNegative(matchNNInteger()));
+            }
+            InnerTypeLocation loc;
+            try {
+                loc = new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(locNumbers));
+            } catch (AssertionError ex) {
+                throw new ParseException(ex.getMessage(), ex);
+            }
+            AElement it = e.innerTypes.vivify(loc);
+            expectChar(':');
+            parseAnnotations(it);
+        }
+    }
+
+    private void parseBounds(VivifyingMap<BoundLocation, ATypeElement> bounds)
+        throws IOException, ParseException {
+        while (checkKeyword("typeparam") || checkKeyword("bound")) {
+            if (matchKeyword("typeparam")) {
+                int paramIndex = expectNonNegative(matchNNInteger());
+                BoundLocation bl = new BoundLocation(paramIndex, -1);
+                ATypeElement b = bounds.vivify(bl);
+                expectChar(':');
+                parseAnnotations(b);
+                // does this make sense?
+                parseInnerTypes(b);
+            } else if (matchKeyword("bound")) {
+                // expectChar(',');
+                int paramIndex = expectNonNegative(matchNNInteger());
+                expectChar('&');
+                int boundIndex = expectNonNegative(matchNNInteger());
+                BoundLocation bl = new BoundLocation(paramIndex, boundIndex);
+                ATypeElement b = bounds.vivify(bl);
+                expectChar(':');
+                parseAnnotations(b);
+                // does this make sense?
+                parseInnerTypes(b);
+            } else {
+                throw new Error("impossible");
+            }
+        }
+    }
+
+    private void parseExtends(AClass cls) throws IOException, ParseException {
+        expectKeyword("extends");
+        TypeIndexLocation idx = new TypeIndexLocation(-1);
+        ATypeElement ext = cls.extendsImplements.vivify(idx);
+        expectChar(':');
+        parseAnnotations(ext);
+        parseInnerTypes(ext);
+    }
+
+    private void parseImplements(AClass cls) throws IOException, ParseException {
+        expectKeyword("implements");
+        int implIndex = expectNonNegative(matchNNInteger());
+        TypeIndexLocation idx = new TypeIndexLocation(implIndex);
+        ATypeElement impl = cls.extendsImplements.vivify(idx);
+        expectChar(':');
+        parseAnnotations(impl);
+        parseInnerTypes(impl);
+    }
+
+    private void parseField(AClass c) throws IOException,
+            ParseException {
+        expectKeyword("field");
+        String name = expectIdentifier();
+        AField f = c.fields.vivify(name);
+
+        expectChar(':');
+        parseAnnotations(f);
+        if (checkKeyword("type") && matchKeyword("type")) {
+            expectChar(':');
+            parseAnnotations(f.type);
+            parseInnerTypes(f.type);
+        }
+
+        f.init = c.fieldInits.vivify(name);
+        parseExpression(f.init);
+        parseASTInsertions(f);
+    }
+
+    private void parseStaticInit(AClass c) throws IOException,
+            ParseException {
+        expectKeyword("staticinit");
+        expectChar('*');
+        int blockIndex = expectNonNegative(matchNNInteger());
+        expectChar(':');
+
+        ABlock staticinit = c.staticInits.vivify(blockIndex);
+        parseBlock(staticinit);
+    }
+
+    private void parseInstanceInit(AClass c) throws IOException,
+            ParseException {
+        expectKeyword("instanceinit");
+        expectChar('*');
+        int blockIndex = expectNonNegative(matchNNInteger());
+        expectChar(':');
+
+        ABlock instanceinit = c.instanceInits.vivify(blockIndex);
+        parseBlock(instanceinit);
+    }
+
+    private void parseMethod(AClass c) throws IOException,
+            ParseException {
+        expectKeyword("method");
+        // special case: method could be <init> or <clinit>
+        String key;
+        if (matchChar('<')) {
+            String basename = expectIdentifier();
+            if (!(basename.equals("init") || basename.equals("clinit"))) {
+                throw new ParseException(
+                    "The only special methods allowed are <init> and <clinit>");
+            }
+            expectChar('>');
+            key = "<" + basename + ">";
+        } else {
+            key = expectIdentifier();
+            // too bad className is private in AClass and thus must be
+            // extracted from what toString() returns
+            if (Pattern.matches("AClass: (?:[^. ]+\\.)*" + key,
+                    c.toString())) {  // ugh
+                key = "<init>";
+            }
+        }
+
+        expectChar('(');
+        key += '(';
+        while (!matchChar(':')) {
+            if (st.ttype >= 0) {
+                key += st.ttype == 46 ? '/' :(char) st.ttype;
+            } else if (st.ttype == TT_WORD) {
+                key += st.sval;
+            } else {
+                throw new ParseException("Found something that doesn't belong in a signature");
+            }
+            st.nextToken();
+        }
+
+        AMethod m = c.methods.vivify(key);
+        parseAnnotations(m);
+        parseMethod(m);
+    }
+
+    private void parseMethod(AMethod m) throws IOException, ParseException {
+        parseBounds(m.bounds);
+
+        // Permit return value, receiver, and parameters in any order.
+        while (checkKeyword("return") || checkKeyword("receiver") || checkKeyword("parameter")) {
+            if (matchKeyword("return")) {
+                expectChar(':');
+                parseAnnotations(m.returnType);
+                parseInnerTypes(m.returnType);
+            } else if (matchKeyword("parameter")) {
+                // make "#" optional
+                if (checkChar('#')) {
+                    matchChar('#');
+                }
+                int idx = expectNonNegative(matchNNInteger());
+                AField p = m.parameters.vivify(idx);
+                expectChar(':');
+                parseAnnotations(p);
+                if (checkKeyword("type") && matchKeyword("type")) {
+                    expectChar(':');
+                    parseAnnotations(p.type);
+                    parseInnerTypes(p.type);
+                }
+            } else if (matchKeyword("receiver")) {
+                expectChar(':');
+                parseAnnotations(m.receiver.type);
+                parseInnerTypes(m.receiver.type);
+            } else {
+                throw new Error("This can't happen");
+            }
+        }
+
+        parseBlock(m.body);
+        parseASTInsertions(m);
+    }
+
+    private void parseLambda(AMethod m) throws IOException, ParseException {
+        while (checkKeyword("parameter")) {
+            matchKeyword("parameter");
+            // make "#" optional
+            if (checkChar('#')) {
+                matchChar('#');
+            }
+            int idx = expectNonNegative(matchNNInteger());
+            AField p = m.parameters.vivify(idx);
+            expectChar(':');
+            parseAnnotations(p);
+            if (checkKeyword("type") && matchKeyword("type")) {
+                expectChar(':');
+                parseAnnotations(p.type);
+                parseInnerTypes(p.type);
+            }
+        }
+
+        // parseBlock(m.body, true);
+        parseASTInsertions(m);
+    }
+
+    private void parseBlock(ABlock bl) throws IOException,
+            ParseException {
+        boolean matched = true;
+
+        while (matched) {
+            matched = false;
+
+            while (checkKeyword("local")) {
+                matchKeyword("local");
+                matched = true;
+                LocalLocation loc;
+                if (checkNNInteger() != -1) {
+                    // the local variable is specified by bytecode index/range
+                    int index = expectNonNegative(matchNNInteger());
+                    expectChar('#');
+                    int scopeStart = expectNonNegative(matchNNInteger());
+                    expectChar('+');
+                    int scopeLength = expectNonNegative(matchNNInteger());
+                    loc = new LocalLocation(index, scopeStart, scopeLength);
+                } else {
+                    // look for a valid identifier for the local variable
+                    String lvar = expectIdentifier();
+                    int varIndex;
+                    if (checkChar('*')) {
+                        expectChar('*');
+                        varIndex = expectNonNegative(matchNNInteger());
+                    } else {
+                        // default the variable index to 0, the most common case
+                        varIndex = 0;
+                    }
+                    loc = new LocalLocation(lvar, varIndex);
+                }
+                AField l = bl.locals.vivify(loc);
+                expectChar(':');
+                parseAnnotations(l);
+                if (checkKeyword("type") && matchKeyword("type")) {
+                    expectChar(':');
+                    parseAnnotations(l.type);
+                    parseInnerTypes(l.type);
+                }
+            }
+            matched = parseExpression(bl) || matched;
+        }
+    }
+
+    private boolean parseExpression(AExpression exp) throws IOException,
+            ParseException {
+        boolean matched = true;
+        boolean evermatched = false;
+
+        while (matched) {
+            matched = false;
+
+            while (checkKeyword("typecast")) {
+                matchKeyword("typecast");
+                matched = true;
+                evermatched = true;
+                RelativeLocation loc;
+                if (checkChar('#')) {
+                    expectChar('#');
+                    int offset = expectNonNegative(matchNNInteger());
+                    int type_index = 0;
+                    if (checkChar(',')) {
+                        expectChar(',');
+                        type_index = expectNonNegative(matchNNInteger());
+                    }
+                    loc = RelativeLocation.createOffset(offset, type_index);
+                } else {
+                    expectChar('*');
+                    int index = expectNonNegative(matchNNInteger());
+                    int type_index = 0;
+                    if (checkChar(',')) {
+                        expectChar(',');
+                        type_index = expectNonNegative(matchNNInteger());
+                    }
+                    loc = RelativeLocation.createIndex(index, type_index);
+                }
+                ATypeElement t = exp.typecasts.vivify(loc);
+                expectChar(':');
+                parseAnnotations(t);
+                parseInnerTypes(t);
+            }
+            while (checkKeyword("instanceof")) {
+                matchKeyword("instanceof");
+                matched = true;
+                evermatched = true;
+                RelativeLocation loc;
+                if (checkChar('#')) {
+                    expectChar('#');
+                    int offset = expectNonNegative(matchNNInteger());
+                    loc = RelativeLocation.createOffset(offset, 0);
+                } else {
+                    expectChar('*');
+                    int index = expectNonNegative(matchNNInteger());
+                    loc = RelativeLocation.createIndex(index, 0);
+                }
+                ATypeElement i = exp.instanceofs.vivify(loc);
+                expectChar(':');
+                parseAnnotations(i);
+                parseInnerTypes(i);
+            }
+            while (checkKeyword("new")) {
+                matchKeyword("new");
+                matched = true;
+                evermatched = true;
+                RelativeLocation loc;
+                if (checkChar('#')) {
+                    expectChar('#');
+                    int offset = expectNonNegative(matchNNInteger());
+                    loc = RelativeLocation.createOffset(offset, 0);
+                } else {
+                    expectChar('*');
+                    int index = expectNonNegative(matchNNInteger());
+                    loc = RelativeLocation.createIndex(index, 0);
+                }
+                ATypeElement n = exp.news.vivify(loc);
+                expectChar(':');
+                parseAnnotations(n);
+                parseInnerTypes(n);
+            }
+            while (checkKeyword("call")) {
+                matchKeyword("call");
+                matched = true;
+                evermatched = true;
+                int i;
+                boolean isOffset = checkChar('#');
+                expectChar(isOffset ? '#' : '*');
+                i = expectNonNegative(matchNNInteger());
+                expectChar(':');
+                while (checkKeyword("typearg")) {
+                    matchKeyword("typearg");
+                    if (checkChar('#')) { matchChar('#'); }
+                    int type_index = expectNonNegative(matchNNInteger());
+                    RelativeLocation loc = isOffset
+                            ? RelativeLocation.createOffset(i, type_index)
+                            : RelativeLocation.createIndex(i, type_index);
+                    ATypeElement t = exp.calls.vivify(loc);
+                    expectChar(':');
+                    parseAnnotations(t);
+                    parseInnerTypes(t);
+                }
+            }
+            while (checkKeyword("reference")) {
+                matchKeyword("reference");
+                matched = true;
+                evermatched = true;
+                ATypeElement t;
+                RelativeLocation loc;
+                int i;
+                boolean isOffset = checkChar('#');
+                if (isOffset) {
+                    expectChar('#');
+                    i = expectNonNegative(matchNNInteger());
+                    loc = RelativeLocation.createOffset(i, 0);
+                } else {
+                    expectChar('*');
+                    i = expectNonNegative(matchNNInteger());
+                    loc = RelativeLocation.createIndex(i, 0);
+                }
+                expectChar(':');
+                t = exp.refs.vivify(loc);
+                parseAnnotations(t);
+                parseInnerTypes(t);
+                while (checkKeyword("typearg")) {
+                    matchKeyword("typearg");
+                    if (checkChar('#')) { matchChar('#'); }
+                    int type_index = expectNonNegative(matchNNInteger());
+                    loc = isOffset
+                        ? RelativeLocation.createOffset(i, type_index)
+                        : RelativeLocation.createIndex(i, type_index);
+                    t = exp.refs.vivify(loc);
+                    expectChar(':');
+                    parseAnnotations(t);
+                    parseInnerTypes(t);
+                }
+            }
+            while (checkKeyword("lambda")) {
+                matchKeyword("lambda");
+                matched = true;
+                evermatched = true;
+                RelativeLocation loc;
+                if (checkChar('#')) {
+                    expectChar('#');
+                    int offset = expectNonNegative(matchNNInteger());
+                    int type_index = 0;
+                    if (checkChar(',')) {
+                        expectChar(',');
+                        type_index = expectNonNegative(matchNNInteger());
+                    }
+                    loc = RelativeLocation.createOffset(offset, type_index);
+                } else {
+                    expectChar('*');
+                    int index = expectNonNegative(matchNNInteger());
+                    int type_index = 0;
+                    if (checkChar(',')) {
+                        expectChar(',');
+                        type_index = expectNonNegative(matchNNInteger());
+                    }
+                    loc = RelativeLocation.createIndex(index, type_index);
+                }
+                AMethod m = exp.funs.vivify(loc);
+                expectChar(':');
+                // parseAnnotations(m);
+                parseLambda(m);
+                // parseMethod(m);
+            }
+        }
+        return evermatched;
+    }
+
+    private static boolean isTypeSelector(String selector) {
+      return Arrays.<String>binarySearch(typeSelectors, selector, Collator.getInstance()) >= 0;
+    }
+
+    private static boolean selectsExpression(ASTPath astPath) {
+        int n = astPath.size();
+        if (--n >= 0) {
+            ASTPath.ASTEntry entry = astPath.get(n);
+            while (--n >= 0 && entry.getTreeKind() == Kind.MEMBER_SELECT
+                    && entry.childSelectorIs(ASTPath.EXPRESSION)) {
+              entry = astPath.get(n);
+            }
+            return !isTypeSelector(entry.getChildSelector());
+        }
+        return false;
+    }
+
+    private boolean parseASTInsertions(ADeclaration decl)
+            throws IOException, ParseException {
+        boolean matched = false;
+        while (checkKeyword("insert-annotation")) {
+            matched = true;
+            matchKeyword("insert-annotation");
+            ASTPath astPath = parseASTPath();
+            expectChar(':');
+            // if path doesn't indicate a type, a cast must be generated
+            if (selectsExpression(astPath)) {
+                ATypeElementWithType i = decl.insertTypecasts.vivify(astPath);
+                parseAnnotations(i);
+                i.setType(new DeclaredType());
+                parseInnerTypes(i);
+            } else {
+                // astPath = fixNewArrayType(astPath);  // handle special case
+                // ATypeElement i = decl.insertAnnotations.vivify(astPath);
+                // parseAnnotations(i);
+                // parseInnerTypes(i);
+                int offset = 0;
+                Pair<ASTPath, InnerTypeLocation> pair =
+                        splitNewArrayType(astPath);  // handle special case
+                ATypeElement i;
+                if (pair == null) {
+                    i = decl.insertAnnotations.vivify(astPath);
+                } else {
+                    i = decl.insertAnnotations.vivify(pair.a);
+                    if (pair.b != null) {
+                        i = i.innerTypes.vivify(pair.b);
+                        offset = pair.b.location.size();
+                    }
+                }
+                parseAnnotations(i);
+                parseInnerTypes(i, offset);
+            }
+        }
+        while (checkKeyword("insert-typecast")) {
+            matched = true;
+            matchKeyword("insert-typecast");
+            ASTPath astPath = parseASTPath();
+            expectChar(':');
+            ATypeElementWithType i = decl.insertTypecasts.vivify(astPath);
+            parseAnnotations(i);
+            Type type = parseType();
+            i.setType(type);
+            parseInnerTypes(i);
+        }
+        return matched;
+    }
+
+    // Due to the unfortunate representation of new array expressions,
+    // ASTPaths to their inner array types break the usual rule that
+    // an ASTPath corresponds to an AST node.  This method restores the
+    // invariant by separating out the inner type information.
+    private Pair<ASTPath, InnerTypeLocation> splitNewArrayType(ASTPath astPath) {
+        ASTPath outerPath = astPath;
+        InnerTypeLocation loc = null;
+        int last = astPath.size() - 1;
+
+        if (last > 0) {
+            ASTPath.ASTEntry entry = astPath.get(last);
+            if (entry.getTreeKind() == Kind.NEW_ARRAY && entry.childSelectorIs(ASTPath.TYPE)) {
+                int a = entry.getArgument();
+                if (a > 0) {
+                    outerPath = astPath.getParentPath().extend(new ASTPath.ASTEntry(Kind.NEW_ARRAY, ASTPath.TYPE, 0));
+            loc = new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Collections.nCopies(2 * a, 0)));
+                }
+            }
+        }
+
+        return Pair.of(outerPath, loc);
+    }
+
+    private ASTPath fixNewArrayType(ASTPath astPath) {
+        ASTPath outerPath = astPath;
+        int last = astPath.size() - 1;
+
+        if (last > 0) {
+            ASTPath.ASTEntry entry = astPath.get(last);
+            if (entry.getTreeKind() == Kind.NEW_ARRAY && entry.childSelectorIs(ASTPath.TYPE)) {
+                int a = entry.getArgument();
+                outerPath = astPath.getParentPath().extend(new ASTPath.ASTEntry(Kind.NEW_ARRAY, ASTPath.TYPE, 0));
+                while (--a >= 0) {
+                    outerPath = outerPath.extend(new ASTPath.ASTEntry(Kind.ARRAY_TYPE, ASTPath.TYPE));
+                }
+            }
+        }
+
+        return outerPath;
+    }
+
+    /**
+     * Parses an AST path.
+     * @return the AST path
+     */
+    private ASTPath parseASTPath() throws IOException, ParseException {
+        ASTPath astPath = ASTPath.empty().extend(parseASTEntry());
+        while (matchChar(',')) {
+            astPath = astPath.extend(parseASTEntry());
+        }
+        return astPath;
+    }
+
+    /**
+     * Parses and returns the next AST entry.
+     * @return a new AST entry
+     * @throws ParseException if the next entry type is invalid
+     */
+    private ASTPath.ASTEntry parseASTEntry() throws IOException, ParseException {
+        ASTPath.ASTEntry entry;
+        if (matchKeyword("AnnotatedType")) {
+            entry = newASTEntry(Kind.ANNOTATED_TYPE, new String[] {ASTPath.ANNOTATION, ASTPath.UNDERLYING_TYPE},
+                    new String[] {ASTPath.ANNOTATION});
+        } else if (matchKeyword("ArrayAccess")) {
+            entry = newASTEntry(Kind.ARRAY_ACCESS, new String[] {ASTPath.EXPRESSION, ASTPath.INDEX});
+        } else if (matchKeyword("ArrayType")) {
+            entry = newASTEntry(Kind.ARRAY_TYPE, new String[] {ASTPath.TYPE});
+        } else if (matchKeyword("Assert")) {
+            entry = newASTEntry(Kind.ASSERT, new String[] {ASTPath.CONDITION, ASTPath.DETAIL});
+        } else if (matchKeyword("Assignment")) {
+            entry = newASTEntry(Kind.ASSIGNMENT, new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION});
+        } else if (matchKeyword("Binary")) {
+            // Always use Kind.PLUS for Binary
+            entry = newASTEntry(Kind.PLUS, new String[] {ASTPath.LEFT_OPERAND, ASTPath.RIGHT_OPERAND});
+        } else if (matchKeyword("Block")) {
+            entry = newASTEntry(Kind.BLOCK, new String[] {ASTPath.STATEMENT}, new String[] {ASTPath.STATEMENT});
+        } else if (matchKeyword("Case")) {
+            entry = newASTEntry(Kind.CASE, new String[] {ASTPath.EXPRESSION, ASTPath.STATEMENT},
+                    new String[] {ASTPath.STATEMENT});
+        } else if (matchKeyword("Catch")) {
+            entry = newASTEntry(Kind.CATCH, new String[] {ASTPath.PARAMETER, ASTPath.BLOCK});
+        } else if (matchKeyword("Class")) {
+            entry = newASTEntry(Kind.CLASS,
+                    new String[] {ASTPath.BOUND, ASTPath.INITIALIZER, ASTPath.TYPE_PARAMETER},
+                    new String[] {ASTPath.BOUND, ASTPath.INITIALIZER, ASTPath.TYPE_PARAMETER});
+        } else if (matchKeyword("CompoundAssignment")) {
+            // Always use Kind.PLUS_ASSIGNMENT for CompoundAssignment
+            entry = newASTEntry(Kind.PLUS_ASSIGNMENT, new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION});
+        } else if (matchKeyword("ConditionalExpression")) {
+            entry = newASTEntry(Kind.CONDITIONAL_EXPRESSION,
+                    new String[] {ASTPath.CONDITION, ASTPath.TRUE_EXPRESSION, ASTPath.FALSE_EXPRESSION});
+        } else if (matchKeyword("DoWhileLoop")) {
+            entry = newASTEntry(Kind.DO_WHILE_LOOP, new String[] {ASTPath.CONDITION, ASTPath.STATEMENT});
+        } else if (matchKeyword("EnhancedForLoop")) {
+            entry = newASTEntry(Kind.ENHANCED_FOR_LOOP, new String[] {ASTPath.VARIABLE, ASTPath.EXPRESSION, ASTPath.STATEMENT});
+        } else if (matchKeyword("ExpressionStatement")) {
+            entry = newASTEntry(Kind.EXPRESSION_STATEMENT, new String[] {ASTPath.EXPRESSION});
+        } else if (matchKeyword("ForLoop")) {
+            entry = newASTEntry(Kind.FOR_LOOP, new String[] {ASTPath.INITIALIZER, ASTPath.CONDITION, ASTPath.UPDATE, ASTPath.STATEMENT},
+                    new String[] {ASTPath.INITIALIZER, ASTPath.UPDATE});
+        } else if (matchKeyword("If")) {
+            entry = newASTEntry(Kind.IF, new String[] {ASTPath.CONDITION, ASTPath.THEN_STATEMENT, ASTPath.ELSE_STATEMENT});
+        } else if (matchKeyword("InstanceOf")) {
+            entry = newASTEntry(Kind.INSTANCE_OF, new String[] {ASTPath.EXPRESSION, ASTPath.TYPE});
+        } else if (matchKeyword("LabeledStatement")) {
+            entry = newASTEntry(Kind.LABELED_STATEMENT, new String[] {ASTPath.STATEMENT});
+        } else if (matchKeyword("LambdaExpression")) {
+            entry = newASTEntry(Kind.LAMBDA_EXPRESSION, new String[] {ASTPath.PARAMETER, ASTPath.BODY},
+                    new String[] {ASTPath.PARAMETER});
+        } else if (matchKeyword("MemberReference")) {
+            entry = newASTEntry(Kind.MEMBER_REFERENCE, new String[] {ASTPath.QUALIFIER_EXPRESSION, ASTPath.TYPE_ARGUMENT},
+                    new String[] {ASTPath.TYPE_ARGUMENT});
+        } else if (matchKeyword("MemberSelect")) {
+            entry = newASTEntry(Kind.MEMBER_SELECT, new String[] {ASTPath.EXPRESSION});
+        } else if (matchKeyword("Method")) {
+            entry = newASTEntry(Kind.METHOD, new String[] {ASTPath.BODY, ASTPath.TYPE, ASTPath.PARAMETER, ASTPath.TYPE_PARAMETER},
+                    new String[] {ASTPath.PARAMETER, ASTPath.TYPE_PARAMETER});
+        } else if (matchKeyword("MethodInvocation")) {
+            entry = newASTEntry(Kind.METHOD_INVOCATION, new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.METHOD_SELECT, ASTPath.ARGUMENT},
+                    new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT});
+        } else if (matchKeyword("NewArray")) {
+            entry = newASTEntry(Kind.NEW_ARRAY, new String[] {ASTPath.TYPE, ASTPath.DIMENSION, ASTPath.INITIALIZER},
+                    new String[] {ASTPath.TYPE, ASTPath.DIMENSION, ASTPath.INITIALIZER});
+        } else if (matchKeyword("NewClass")) {
+            entry = newASTEntry(Kind.NEW_CLASS, new String[] {ASTPath.ENCLOSING_EXPRESSION, ASTPath.TYPE_ARGUMENT, ASTPath.IDENTIFIER, ASTPath.ARGUMENT, ASTPath.CLASS_BODY},
+                    new String[] {ASTPath.TYPE_ARGUMENT, ASTPath.ARGUMENT});
+        } else if (matchKeyword("ParameterizedType")) {
+            entry = newASTEntry(Kind.PARAMETERIZED_TYPE, new String[] {ASTPath.TYPE, ASTPath.TYPE_ARGUMENT},
+                    new String[] {ASTPath.TYPE_ARGUMENT});
+        } else if (matchKeyword("Parenthesized")) {
+            entry = newASTEntry(Kind.PARENTHESIZED, new String[] {ASTPath.EXPRESSION});
+        } else if (matchKeyword("Return")) {
+            entry = newASTEntry(Kind.RETURN, new String[] {ASTPath.EXPRESSION});
+        } else if (matchKeyword("Switch")) {
+            entry = newASTEntry(Kind.SWITCH, new String[] {ASTPath.EXPRESSION, ASTPath.CASE},
+                    new String[] {ASTPath.CASE});
+        } else if (matchKeyword("Synchronized")) {
+            entry = newASTEntry(Kind.SYNCHRONIZED, new String[] {ASTPath.EXPRESSION, ASTPath.BLOCK});
+        } else if (matchKeyword("Throw")) {
+            entry = newASTEntry(Kind.THROW, new String[] {ASTPath.EXPRESSION});
+        } else if (matchKeyword("Try")) {
+            entry = newASTEntry(Kind.TRY, new String[] {ASTPath.BLOCK, ASTPath.CATCH, ASTPath.FINALLY_BLOCK},
+                    new String[] {ASTPath.CATCH});
+        } else if (matchKeyword("TypeCast")) {
+            entry = newASTEntry(Kind.TYPE_CAST, new String[] {ASTPath.TYPE, ASTPath.EXPRESSION});
+        } else if (matchKeyword("TypeParameter")) {
+            entry = newASTEntry(Kind.TYPE_PARAMETER, new String[] {ASTPath.BOUND},
+                    new String[] {ASTPath.BOUND});
+        } else if (matchKeyword("Unary")) {
+            // Always use Kind.UNARY_PLUS for Unary
+            entry = newASTEntry(Kind.UNARY_PLUS, new String[] {ASTPath.EXPRESSION});
+        } else if (matchKeyword("UnionType")) {
+            entry = newASTEntry(Kind.UNION_TYPE, new String[] {ASTPath.TYPE_ALTERNATIVE},
+                    new String[] {ASTPath.TYPE_ALTERNATIVE});
+        } else if (matchKeyword("Variable")) {
+            entry = newASTEntry(Kind.VARIABLE, new String[] {ASTPath.TYPE, ASTPath.INITIALIZER});
+        } else if (matchKeyword("WhileLoop")) {
+            entry = newASTEntry(Kind.WHILE_LOOP, new String[] {ASTPath.CONDITION, ASTPath.STATEMENT});
+        } else if (matchKeyword("Wildcard")) {
+            // Always use Kind.UNBOUNDED_WILDCARD for Wildcard
+            entry = newASTEntry(Kind.UNBOUNDED_WILDCARD, new String[] {ASTPath.BOUND});
+        } else {
+            throw new ParseException("Invalid AST path type: " + st.sval);
+        }
+        return entry;
+    }
+
+    /**
+     * Parses and constructs a new AST entry, where none of the child selections require
+     * arguments. For example, the call:
+     *
+     * <pre>
+     * {@code newASTEntry(Kind.WHILE_LOOP, new String[] {"condition", "statement"});</pre>
+     *
+     * constructs a while loop AST entry, where the valid child selectors are "condition" or
+     * "statement".
+     *
+     * @param kind the kind of this AST entry
+     * @param legalChildSelectors a list of the legal child selectors for this AST entry
+     * @return a new {@link ASTPath.ASTEntry}
+     * @throws ParseException if an illegal argument is found
+     */
+    private ASTPath.ASTEntry newASTEntry(Kind kind, String[] legalChildSelectors) throws IOException, ParseException {
+        return newASTEntry(kind, legalChildSelectors, null);
+    }
+
+    /**
+     * Parses and constructs a new AST entry. For example, the call:
+     *
+     * <pre>
+     * {@code newASTEntry(Kind.CASE, new String[] {"expression", "statement"}, new String[] {"statement"});
+     * </pre>
+     *
+     * constructs a case AST entry, where the valid child selectors are
+     * "expression" or "statement" and the "statement" child selector requires
+     * an argument.
+     *
+     * @param kind the kind of this AST entry
+     * @param legalChildSelectors a list of the legal child selectors for this AST entry
+     * @param argumentChildSelectors a list of the child selectors that also require an argument.
+     *                               Entries here should also be in the legalChildSelectors list.
+     * @return a new {@link ASTPath.ASTEntry}
+     * @throws ParseException if an illegal argument is found
+     */
+    private ASTPath.ASTEntry newASTEntry(Kind kind, String[] legalChildSelectors, String[] argumentChildSelectors) throws IOException, ParseException {
+        expectChar('.');
+        for (String arg : legalChildSelectors) {
+            if (matchKeyword(arg)) {
+                if (argumentChildSelectors != null && ArraysMDE.indexOf(argumentChildSelectors, arg) >= 0) {
+                    int index = matchNNInteger();
+                    return new ASTPath.ASTEntry(kind, arg, index);
+                } else {
+                    return new ASTPath.ASTEntry(kind, arg);
+                }
+            }
+        }
+        throw new ParseException("Invalid argument for " + kind + " (legal arguments - " + Arrays.toString(legalChildSelectors) + "): " + st.sval);
+    }
+
+    /**
+     * Parses the next tokens as a Java type.
+     */
+    private Type parseType() throws IOException, ParseException {
+        DeclaredType declaredType;
+        if (matchChar('?')) {
+            declaredType = new DeclaredType(DeclaredType.WILDCARD);
+        } else {
+            declaredType = parseDeclaredType();
+        }
+        if (checkKeyword("extends") || checkKeyword("super")) {
+            return parseBoundedType(declaredType);
+        } else {
+            Type type = declaredType;
+            while (matchChar('[')) {
+                expectChar(']');
+                type = new ArrayType(type);
+            }
+            return type;
+        }
+    }
+
+    /**
+     * Parses the next tokens as a declared type.
+     */
+    private DeclaredType parseDeclaredType() throws IOException, ParseException {
+        String type = matchIdentifier();
+        if (type == null) {
+            type = matchPrimitiveType();
+            if (type == null) {
+                throw new ParseException("Expected identifier or primitive type");
+            }
+        }
+        return parseDeclaredType(type);
+    }
+
+    /**
+     * Parses the next tokens as a declared type.
+     * @param name the name of the initial identifier
+     */
+    private DeclaredType parseDeclaredType(String name) throws IOException, ParseException {
+        DeclaredType type = new DeclaredType(name);
+        if (matchChar('<')) {
+            type.addTypeParameter(parseType());
+            while (matchChar(',')) {
+                type.addTypeParameter(parseType());
+            }
+            expectChar('>');
+        }
+        if (matchChar('.')) {
+            type.setInnerType(parseDeclaredType());
+        }
+        return type;
+    }
+
+    /**
+     * Parses the next tokens as a bounded type.
+     * @param type the name, which precedes "extends" or "super"
+     */
+    private BoundedType parseBoundedType(DeclaredType type) throws IOException, ParseException {
+        BoundKind kind;
+        if (matchKeyword("extends")) {
+            kind = BoundKind.EXTENDS;
+        } else if (matchKeyword("super")) {
+            kind = BoundKind.SUPER;
+        } else {
+            throw new ParseException("Illegal bound kind: " + st.sval);
+        }
+        return new BoundedType(type, kind, parseDeclaredType());
+    }
+
+    private void parseClass() throws IOException, ParseException {
+        expectKeyword("class");
+        String basename = expectIdentifier();
+        String fullName = curPkgPrefix + basename;
+
+        AClass c = scene.classes.vivify(fullName);
+        expectChar(':');
+
+        parseAnnotations(c);
+        parseBounds(c.bounds);
+
+        while (checkKeyword("extends")) {
+            parseExtends(c);
+        }
+        while (checkKeyword("implements")) {
+            parseImplements(c);
+        }
+        parseASTInsertions(c);
+
+        while (checkKeyword("field")) {
+            parseField(c);
+        }
+        while (checkKeyword("staticinit")) {
+            parseStaticInit(c);
+        }
+        while (checkKeyword("instanceinit")) {
+            parseInstanceInit(c);
+        }
+        while (checkKeyword("method")) {
+            parseMethod(c);
+        }
+        c.methods.prune();
+    }
+
+    // Reads the index file in this.st and puts the information in this.scene.
+    private void parse() throws ParseException, IOException {
+        st.nextToken();
+
+        while (st.ttype != TT_EOF) {
+            expectKeyword("package");
+
+            String pkg;
+            if (checkIdentifier() == null) {
+                pkg = null;
+                // the default package cannot be annotated
+                matchChar(':');
+            } else {
+                pkg = expectQualifiedName();
+                // AElement p = scene.packages.vivify(pkg);
+                AClass p = scene.classes.vivify(pkg + ".package-info");
+                expectChar(':');
+                p = scene.classes.vivify(pkg + ".package-info");
+                parseAnnotations(p);
+            }
+
+            if (pkg != null) {
+                curPkgPrefix = pkg + ".";
+            } else {
+                curPkgPrefix = "";
+            }
+
+            for (;;) {
+                if (checkKeyword("annotation")) {
+                    parseAnnotationDef();
+                } else if (checkKeyword("class")) {
+                    parseClass();
+                } else if (checkKeyword("package") || st.ttype == TT_EOF) {
+                    break;
+                } else {
+                    throw new ParseException("Expected: `annotation', `class', or `package'. Found: `"
+                            + st.sval + "', ttype:" + st.ttype);
+                }
+            }
+        }
+
+/*
+        for (Map.Entry<String, AnnotationDef> entry : defs.entrySet()) {
+            final String annotationType = entry.getKey();
+            AnnotationDef def = entry.getValue();
+            for (AnnotationFieldType aft : def.fieldTypes.values()) {
+                aft.accept(new AFTVisitor<Void, Void>() {
+                    @Override
+                    public Void visitAnnotationAFT(AnnotationAFT aft,
+                            Void arg) {
+                        for (AnnotationFieldType t : aft.annotationDef.fieldTypes.values()) {
+                            t.accept(this, arg);
+                        }
+                        return null;
+                    }
+
+                    @Override
+                    public Void visitArrayAFT(ArrayAFT aft, Void arg) {
+                      return aft.elementType == null ? null
+                          : aft.elementType.accept(this, arg);
+                    }
+
+                    @Override
+                    public Void visitBasicAFT(BasicAFT aft, Void arg) {
+                      return null;
+                    }
+
+                    @Override
+                    public Void visitClassTokenAFT(ClassTokenAFT aft, Void arg) {
+                      return null;
+                    }
+
+                    @Override
+                    public Void visitEnumAFT(EnumAFT aft, Void arg) {
+                        importSet(annotationType, aft).add(aft.typeName);
+                        return null;
+                    }
+
+                    private Set<String> importSet(final String annotationType,
+                        AnnotationFieldType aft) {
+                      Set<String> imps = scene.imports.get(annotationType);
+                      if (imps == null) {
+                          imps = new TreeSet<String>();
+                          scene.imports.put(annotationType, imps);
+                      }
+                      return imps;
+                    }
+                }, null);
+            }
+        }
+*/
+    }
+
+    private IndexFileParser(Reader in, AScene scene) {
+        defs = new LinkedHashMap<String, AnnotationDef>();
+        for (AnnotationDef ad : Annotations.standardDefs) {
+            try {
+                addDef(ad);
+            } catch (ParseException e) {
+                throw new Error(e);
+            }
+        }
+
+        st = new StreamTokenizer(in);
+        st.slashSlashComments(true);
+
+        // restrict numbers -- don't really need, could interfere with
+        // annotation values
+        // st.ordinaryChar('-');
+        // HMMM this fixes fully-qualified-name strangeness but breaks
+        // floating-point numbers
+        st.ordinaryChar('.');
+
+        // argggh!!! stupid default needs to be overridden! see java bug 4217680
+        st.ordinaryChar('/');
+
+        // for "type-argument"
+        st.wordChars('-', '-');
+
+        // java identifiers can contain numbers, _, and $
+        st.wordChars('0', '9');
+        st.wordChars('_', '_');
+        st.wordChars('$', '$');
+
+        this.scene = scene;
+
+        // See if the nonnull analysis picks up on this:
+        // curPkgPrefix == ""; // will get changed later anyway
+    }
+
+    /**
+     * Reads annotations from <code>in</code> in index file format and merges
+     * them into <code>scene</code>.  Annotations
+     * from the input are merged into the scene; it is an error if both the
+     * scene and the input contain annotations of the same type on the same
+     * element.
+     *
+     * <p>
+     * Since each annotation in a scene carries its own definition and the
+     * scene as a whole no longer has a set of definitions, annotation
+     * definitions that are given in the input but never used are not saved
+     * anywhere and will not be included if the scene is written back to an
+     * index file.  Similarly, retention policies on definitions of annotations
+     * that are never used at the top level are dropped.
+     *
+     * <p>
+     * Caveat: Parsing of floating point numbers currently does not work.
+     */
+    public static Map<String, AnnotationDef> parse(LineNumberReader in,
+            AScene scene) throws IOException, ParseException {
+        IndexFileParser parser = new IndexFileParser(in, scene);
+        // no filename is available in the exception messages
+        return parseAndReturnAnnotationDefs(null, in, parser);
+    }
+
+    /**
+     * Reads annotations from the index file <code>filename</code> and merges
+     * them into <code>scene</code>; see {@link #parse(LineNumberReader, AScene)}.
+     */
+    public static Map<String, AnnotationDef> parseFile(String filename,
+            AScene scene) throws IOException {
+        LineNumberReader in = new LineNumberReader(new FileReader(filename));
+        IndexFileParser parser = new IndexFileParser(in, scene);
+        return parseAndReturnAnnotationDefs(filename, in, parser);
+    }
+
+    /**
+     * Reads annotations from the string (in index file format) and merges
+     * them into <code>scene</code>; see {@link #parse(LineNumberReader, AScene)}.
+     * Primarily for testing.
+     */
+    public static Map<String, AnnotationDef> parseString(String fileContents,
+            AScene scene) throws IOException {
+        String filename =
+            "While parsing string: \n----------------BEGIN----------------\n"
+                    + fileContents + "----------------END----------------\n";
+        LineNumberReader in = new LineNumberReader(
+            new StringReader(fileContents));
+        IndexFileParser parser = new IndexFileParser(in, scene);
+        return parseAndReturnAnnotationDefs(filename, in, parser);
+    }
+
+    private static Map<String, AnnotationDef> parseAndReturnAnnotationDefs(
+          String filename, LineNumberReader in, IndexFileParser parser)
+              throws IOException {
+      try {
+          parser.parse();
+          return Collections.unmodifiableMap(parser.defs);
+      } catch (IOException e) {
+          throw filename == null ? new FileIOException(in, e)
+                  : new FileIOException(in, filename, e);
+      } catch (ParseException e) {
+          throw filename == null ? new FileIOException(in, e)
+                  : new FileIOException(in, filename, e);
+      }
+    }
+
+    /**
+     * Parse the given text into a {@link Type}.
+     * @param text the text to parse
+     * @return the type
+     */
+    public static Type parseType(String text) {
+        StringReader in = new StringReader(text);
+        IndexFileParser parser = new IndexFileParser(in, null);
+        try {
+            parser.st.nextToken();
+            return parser.parseType();
+        } catch (Exception e) {
+            throw new RuntimeException("Error parsing type from: '" + text + "'", e);
+        }
+    }
+}
diff --git a/scene-lib/src/annotations/io/IndexFileWriter.java b/scene-lib/src/annotations/io/IndexFileWriter.java
new file mode 100644
index 0000000..0ca0912
--- /dev/null
+++ b/scene-lib/src/annotations/io/IndexFileWriter.java
@@ -0,0 +1,574 @@
+package annotations.io;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import annotations.Annotation;
+import annotations.el.AClass;
+import annotations.el.AElement;
+import annotations.el.AField;
+import annotations.el.AMethod;
+import annotations.el.AScene;
+import annotations.el.ATypeElement;
+import annotations.el.ATypeElementWithType;
+import annotations.el.AnnotationDef;
+import annotations.el.BoundLocation;
+import annotations.el.DefCollector;
+import annotations.el.DefException;
+import annotations.el.InnerTypeLocation;
+import annotations.el.LocalLocation;
+import annotations.el.RelativeLocation;
+import annotations.el.TypeIndexLocation;
+import annotations.field.AnnotationAFT;
+import annotations.field.AnnotationFieldType;
+import annotations.field.ArrayAFT;
+import annotations.field.BasicAFT;
+import annotations.field.ClassTokenAFT;
+import annotations.util.Strings;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * IndexFileWriter provides two static methods named <code>write</code>
+ * that write a given {@link AScene} to a given {@link Writer} or filename,
+ * in index file format.
+ */
+public final class IndexFileWriter {
+    final AScene scene;
+
+    private static final String INDENT = "    ";
+
+    void printAnnotationDefBody(AnnotationDef d) {
+        for (Map. Entry<String, AnnotationFieldType> f : d.fieldTypes.entrySet()) {
+            String fieldname = f.getKey();
+            AnnotationFieldType fieldType = f.getValue();
+            pw.println(INDENT + fieldType + " " + fieldname);
+        }
+        pw.println();
+    }
+
+    private class OurDefCollector extends DefCollector {
+        OurDefCollector() throws DefException {
+            super(IndexFileWriter.this.scene);
+        }
+
+        @Override
+        protected void visitAnnotationDef(AnnotationDef d) {
+          if (!d.name.contains("+")) {
+            pw.println("package " + annotations.io.IOUtils.packagePart(d.name) + ":");
+            pw.print("annotation @" + annotations.io.IOUtils.basenamePart(d.name) + ":");
+            // TODO: We would only print Retention and Target annotations
+            printAnnotations(requiredMetaannotations(d.tlAnnotationsHere));
+            pw.println();
+            printAnnotationDefBody(d);
+          }
+        }
+
+        private Collection<Annotation> requiredMetaannotations(
+                Collection<Annotation> annos) {
+            Set<Annotation> results = new HashSet<Annotation>();
+            for (Annotation a : annos) {
+                String aName = a.def.name;
+                if (aName.equals(Retention.class.getCanonicalName())
+                    || aName.equals(Target.class.getCanonicalName())) {
+                    results.add(a);
+                }
+            }
+            return results;
+        }
+    }
+
+    final PrintWriter pw;
+
+    private void printValue(AnnotationFieldType aft, Object o) {
+        if (aft instanceof AnnotationAFT) {
+            printAnnotation((Annotation) o);
+        } else if (aft instanceof ArrayAFT) {
+            ArrayAFT aaft = (ArrayAFT) aft;
+            pw.print('{');
+            if (!(o instanceof List)) {
+                printValue(aaft.elementType, o);
+            } else {
+                List<?> l =
+                    (List<?>) o;
+                // watch out--could be an empty array of unknown type
+                // (see AnnotationBuilder#addEmptyArrayField)
+                if (aaft.elementType == null) {
+                    if (l.size() != 0) {
+                        throw new AssertionError();
+                    }
+                } else {
+                    boolean first = true;
+                    for (Object o2 : l) {
+                        if (!first) {
+                            pw.print(',');
+                        }
+                        printValue(aaft.elementType, o2);
+                        first = false;
+                    }
+                }
+            }
+            pw.print('}');
+        } else if (aft instanceof ClassTokenAFT) {
+            pw.print(aft.format(o));
+        } else if (aft instanceof BasicAFT && o instanceof String) {
+            pw.print(Strings.escape((String) o));
+        } else {
+            pw.print(o.toString());
+        }
+    }
+
+    private void printAnnotation(Annotation a) {
+        pw.print("@" + a.def().name);
+        if (!a.fieldValues.isEmpty()) {
+            pw.print('(');
+            boolean first = true;
+            for (Map. Entry<String, Object> f
+                    : a.fieldValues.entrySet()) {
+                if (!first) {
+                    pw.print(',');
+                }
+                pw.print(f.getKey() + "=");
+                printValue(a.def().fieldTypes.get(f.getKey()), f.getValue());
+                first = false;
+            }
+            pw.print(')');
+        }
+    }
+
+    private void printAnnotations(Collection<? extends Annotation> annos) {
+        for (Annotation tla : annos) {
+            pw.print(' ');
+            printAnnotation(tla);
+        }
+    }
+
+    private void printAnnotations(AElement e) {
+        printAnnotations(e.tlAnnotationsHere);
+    }
+
+    private void printElement(String indentation,
+            String desc,
+            AElement e) {
+        pw.print(indentation + desc + ":");
+        printAnnotations(e);
+        pw.println();
+    }
+
+    private void printElementAndInnerTypes(String indentation,
+            String desc, AElement e) {
+        if (e.type != null) {
+            printElement(indentation, desc, e.type);
+            if (!e.type.innerTypes.isEmpty()) {
+                printInnerTypes(indentation + INDENT, e.type);
+            }
+        }
+    }
+
+    private void printTypeElementAndInnerTypes(String indentation,
+            String desc,
+            ATypeElement e) {
+        if (e.tlAnnotationsHere.isEmpty() && e.innerTypes.isEmpty() && desc.equals("type")) {
+            return;
+        }
+        printElement(indentation, desc, e);
+        printInnerTypes(indentation + INDENT, e);
+    }
+
+    private void printInnerTypes(String indentation, ATypeElement e) {
+      for (Map. Entry<InnerTypeLocation,
+              ATypeElement> ite : e.innerTypes.entrySet()) {
+          InnerTypeLocation loc = ite.getKey();
+          AElement it = ite.getValue();
+          pw.print(indentation + "inner-type");
+          char sep = ' ';
+          for (TypePathEntry l : loc.location) {
+              pw.print(sep);
+              pw.print(typePathEntryToString(l));
+              sep = ',';
+          }
+          pw.print(':');
+          printAnnotations(it);
+          pw.println();
+      }
+    }
+
+    private void printInnerTypes(String indentation, ATypeElement e,
+            ASTPath path) {
+        for (Map. Entry<InnerTypeLocation,
+                ATypeElement> ite : e.innerTypes.entrySet()) {
+            InnerTypeLocation loc = ite.getKey();
+            AElement it = ite.getValue();
+            pw.print(indentation + "inner-type");
+            char sep = ' ';
+            for (TypePathEntry l : loc.location) {
+                pw.print(sep);
+                pw.print(typePathEntryToString(l));
+                sep = ',';
+            }
+            pw.print(':');
+            printAnnotations(it);
+            pw.println();
+        }
+    }
+
+    /**
+     * Converts the given {@link TypePathEntry} to a string of the form
+     * {@code tag, arg}, where tag and arg are both integers.
+     */
+    private String typePathEntryToString(TypePathEntry t) {
+        return t.tag.tag + ", " + t.arg;
+    }
+
+    private void printNumberedAmbigiousElements(String indentation,
+            String desc,
+            Map<Integer, ? extends AElement> nels) {
+        for (Map. Entry<Integer,
+                ? extends AElement> te : nels.entrySet()) {
+            AElement t = te.getValue();
+            printAmbElementAndInnerTypes(indentation,
+                    desc + " #" + te.getKey(), t);
+        }
+    }
+
+    private void printAmbElementAndInnerTypes(String indentation,
+            String desc,
+            AElement e) {
+        printElement(indentation, desc, e);
+        if (e.type.tlAnnotationsHere.isEmpty() && e.type.innerTypes.isEmpty()) {
+            return;
+        }
+        printElement(indentation + INDENT, "type", e.type);
+        for (Map. Entry<InnerTypeLocation, ATypeElement> ite
+                : e.type.innerTypes.entrySet()) {
+            InnerTypeLocation loc = ite.getKey();
+            AElement it = ite.getValue();
+            pw.print(indentation + INDENT + INDENT + "inner-type");
+            boolean first = true;
+            for (TypePathEntry l : loc.location) {
+                if (first) {
+                    pw.print(' ');
+                } else {
+                    pw.print(',');
+                }
+                pw.print(typePathEntryToString(l));
+                first = false;
+            }
+            pw.print(':');
+            printAnnotations(it);
+            pw.println();
+        }
+    }
+
+    private void printRelativeElements(String indentation,
+            String desc,
+            Map<RelativeLocation, ATypeElement> nels) {
+        for (Map. Entry<RelativeLocation, ATypeElement> te
+                : nels.entrySet()) {
+            ATypeElement t = te.getValue();
+            printTypeElementAndInnerTypes(indentation,
+                    desc + " " + te.getKey().getLocationString(), t);
+        }
+    }
+
+    private void printRelativeElements(String indentation,
+            String desc1, String desc2,
+            Map<RelativeLocation, ATypeElement> nels) {
+        RelativeLocation prev = null;
+        for (Map. Entry<RelativeLocation, ATypeElement> te
+                : nels.entrySet()) {
+            ATypeElement t = te.getValue();
+            RelativeLocation loc = te.getKey();
+            boolean isOffset = loc.index < 0;
+            if (prev == null || loc.type_index < 0
+                    || (isOffset ? loc.offset != prev.offset
+                            : loc.index != prev.index)) {
+                pw.print(indentation + desc1 + " ");
+                pw.print(isOffset ? "#" + loc.offset : "*" + loc.index);
+                pw.print(":");
+                if (loc.type_index <= 0) { printAnnotations(t); }
+                pw.println();
+                printInnerTypes(indentation + INDENT, t);
+            }
+            if (loc.type_index > 0) {
+                printTypeElementAndInnerTypes(indentation + INDENT,
+                        desc2 + " " + loc.type_index, t);
+            }
+            prev = loc;
+        }
+    }
+
+    private void printBounds(String indentation,
+            Map<BoundLocation, ATypeElement> bounds) {
+        for (Map. Entry<BoundLocation, ATypeElement> be
+                : bounds.entrySet()) {
+            BoundLocation bl = be.getKey();
+            ATypeElement b = be.getValue();
+            if (bl.boundIndex == -1) {
+                printTypeElementAndInnerTypes(indentation,
+                                              "typeparam " + bl.paramIndex, b);
+            } else {
+                printTypeElementAndInnerTypes(indentation,
+                           "bound " + bl.paramIndex + " &" + bl.boundIndex, b);
+            }
+        }
+    }
+
+    private void printExtImpls(String indentation,
+            Map<TypeIndexLocation, ATypeElement> extImpls) {
+
+        for (Map. Entry<TypeIndexLocation, ATypeElement> ei
+                : extImpls.entrySet()) {
+            TypeIndexLocation idx = ei.getKey();
+            ATypeElement ty = ei.getValue();
+            // reading from a short into an integer does not preserve sign?
+            if (idx.typeIndex == -1 || idx.typeIndex == 65535) {
+                printTypeElementAndInnerTypes(indentation, "extends", ty);
+            } else {
+                printTypeElementAndInnerTypes(indentation, "implements " + idx.typeIndex, ty);
+            }
+        }
+    }
+
+    private void printASTInsertions(String indentation,
+            Map<ASTPath, ATypeElement>
+            insertAnnotations,
+            Map<ASTPath, ATypeElementWithType>
+            insertTypecasts) {
+        for (Map. Entry<ASTPath, ATypeElement> e :
+                insertAnnotations.entrySet()) {
+            ASTPath path = e.getKey();
+            ATypeElement el = e.getValue();
+            pw.print(indentation + "insert-annotation " + path + ":");
+            printAnnotations(el);
+            pw.println();
+            printInnerTypes(INDENT, el, path);
+        }
+        for (Map. Entry<ASTPath,
+                    ATypeElementWithType> e :
+                insertTypecasts.entrySet()) {
+            ASTPath path = e.getKey();
+            ATypeElementWithType el = e.getValue();
+            pw.print(indentation + "insert-typecast " + path + ":");
+            printAnnotations(el);
+            pw.print(" ");
+            printType(el.getType());
+            pw.println();
+            printInnerTypes(INDENT, el, path);
+        }
+    }
+
+    private void printType(type.Type type) {
+        switch (type.getKind()) {
+        case ARRAY:
+            type.ArrayType a = (type.ArrayType) type;
+            printType(a.getComponentType());
+            pw.print("[]");
+            break;
+        case BOUNDED:
+            type.BoundedType b = (type.BoundedType) type;
+            printType(b.getName());
+            pw.print(" ");
+            pw.print(b.getBoundKind());
+            pw.print(" ");
+            printType(b.getBound());
+            break;
+        case DECLARED:
+            type.DeclaredType d = (type.DeclaredType) type;
+            pw.print(d.getName());
+            if (!d.isWildcard()) {
+                type.DeclaredType inner = d.getInnerType();
+                Iterator<type.Type> iter = d.getTypeParameters().iterator();
+                // for (String s : d.getAnnotations()) {
+                //    pw.print(s + " ");
+                // }
+                if (iter.hasNext()) {
+                    pw.print("<");
+                    printType(iter.next());
+                    while (iter.hasNext()) {
+                        pw.print(", ");
+                        printType(iter.next());
+                    }
+                    pw.print(">");
+                }
+                if (inner != null) {
+                    pw.print(".");
+                    printType(inner);
+                }
+            }
+            break;
+        }
+    }
+
+    private void write() throws DefException {
+        // First the annotation definitions...
+        OurDefCollector odc = new OurDefCollector();
+        odc.visit();
+
+        // Then any package annotations...
+        for (Map. Entry<String, AElement> pe
+                : scene.packages.entrySet()) {
+            AElement elem = pe.getValue();
+            if (elem != null && !elem.tlAnnotationsHere.isEmpty()) {
+                pw.print("package " + pe.getKey() + ":");
+                printAnnotations(elem);
+                pw.println();
+            }
+        }
+
+        // And then the annotated classes
+        final String indent2 = INDENT + INDENT;
+        final String indent3 = INDENT + indent2;
+        for (Map. Entry<String, AClass> ce
+                : scene.classes.entrySet()) {
+            String cname = ce.getKey();
+            AClass c = ce.getValue();
+            String pkg = annotations.io.IOUtils.packagePart(cname);
+            String basename = annotations.io.IOUtils.basenamePart(cname);
+            if ("package-info".equals(basename)) {
+              if (!c.tlAnnotationsHere.isEmpty()) {
+                pw.print("package " + pkg + ":");
+                printAnnotations(c);
+                pw.println();
+              }
+              continue;
+            } else {
+              pw.println("package " + pkg + ":");
+              pw.print("class " + basename + ":");
+              printAnnotations(c);
+              pw.println();
+            }
+
+            printBounds(INDENT, c.bounds);
+            printExtImpls(INDENT, c.extendsImplements);
+            printASTInsertions(INDENT, c.insertAnnotations, c.insertTypecasts);
+
+            for (Map. Entry<String, AField> fe
+                    : c.fields.entrySet()) {
+                String fname = fe.getKey();
+                AField f = fe.getValue();
+                pw.println();
+                printElement(INDENT, "field " + fname, f);
+                printTypeElementAndInnerTypes(indent2, "type", f.type);
+                printASTInsertions(indent2,
+                        f.insertAnnotations, f.insertTypecasts);
+            }
+            for (Map. Entry<String, AMethod> me
+                    : c.methods.entrySet()) {
+                String mkey = me.getKey();
+                AMethod m = me.getValue();
+                pw.println();
+                printElement(INDENT, "method " + mkey, m);
+                printBounds(indent2, m.bounds);
+                printTypeElementAndInnerTypes(indent2, "return", m.returnType);
+                if (!m.receiver.type.tlAnnotationsHere.isEmpty()
+                        || !m.receiver.type.innerTypes.isEmpty()) {
+                    // Only output the receiver if there is something to
+                    // say.  This is a bit inconsistent with the return
+                    // type, but so be it.
+                    printElementAndInnerTypes(indent2, "receiver", m.receiver);
+                }
+                printNumberedAmbigiousElements(indent2,
+                        "parameter", m.parameters);
+                for (Map. Entry<LocalLocation,
+                        AField> le : m.body.locals.entrySet()) {
+                    LocalLocation loc = le.getKey();
+                    AElement l = le.getValue();
+                    StringBuilder sb = new StringBuilder("local ");
+                    sb.append(loc.varName == null
+                        ? loc.index
+                            + " #" + loc.scopeStart + "+" + loc.scopeLength
+                        : loc.varName);
+                    printElement(indent2, sb.toString(), l);
+                    printTypeElementAndInnerTypes(indent3,
+                            "type", l.type);
+                }
+                printRelativeElements(indent2, "typecast",
+                        m.body.typecasts);
+                printRelativeElements(indent2, "instanceof",
+                        m.body.instanceofs);
+                printRelativeElements(indent2, "new", m.body.news);
+                printRelativeElements(indent2, "reference", "typearg", m.body.refs);
+                printRelativeElements(indent2, "call", "typearg", m.body.calls);
+                for (Map. Entry<RelativeLocation,
+                        AMethod> entry : m.body.funs.entrySet()) {
+                    AMethod lambda = entry.getValue();
+                    RelativeLocation loc = entry.getKey();
+                    pw.print("lambda " + loc.getLocationString() + ":\n");
+                    printBounds(indent3, lambda.bounds);
+                    printTypeElementAndInnerTypes(indent3,
+                        "return", lambda.returnType);
+                }
+                // throwsException field is not processed.  Why?
+                printASTInsertions(indent2,
+                        m.insertAnnotations, m.insertTypecasts);
+            }
+            pw.println();
+        }
+    }
+
+    private IndexFileWriter(AScene scene,
+            Writer out) throws DefException {
+        this.scene = scene;
+        pw = new PrintWriter(out);
+        write();
+        pw.flush();
+    }
+
+    /**
+     * Writes the annotations in <code>scene</code> and their definitions to
+     * <code>out</code> in index file format.
+     *
+     * <p>
+     * An {@link AScene} can contain several annotations of the same type but
+     * different definitions, while an index file can accommodate only a single
+     * definition for each annotation type.  This has two consequences:
+     *
+     * <ul>
+     * <li>Before writing anything, this method uses a {@link DefCollector} to
+     * ensure that all definitions of each annotation type are identical
+     * (modulo unknown array types).  If not, a {@link DefException} is thrown.
+     * <li>There is one case in which, even if a scene is written successfully,
+     * reading it back in produces a different scene.  Consider a scene
+     * containing two annotations of type Foo, each with an array field bar.
+     * In one annotation, bar is empty and of unknown element type (see
+     * {@link annotations.AnnotationBuilder#addEmptyArrayField}); in the other, bar is
+     * of known element type.  This method will
+     * {@linkplain AnnotationDef#unify unify} the two definitions of Foo by
+     * writing a single definition with known element type.  When the index
+     * file is read into a new scene, the definitions of both annotations
+     * will have known element type, whereas in the original scene, one had
+     * unknown element type.
+     * </ul>
+     */
+    public static void write(
+            AScene scene,
+            Writer out) throws DefException {
+        new IndexFileWriter(scene, out);
+    }
+
+    /**
+     * Writes the annotations in <code>scene</code> and their definitions to
+     * the file <code>filename</code> in index file format; see
+     * {@link #write(AScene, Writer)}.
+     */
+    public static void write(
+            AScene scene,
+            String filename) throws IOException, DefException {
+        write(scene, new FileWriter(filename));
+    }
+}
diff --git a/scene-lib/src/annotations/io/JavapParser.java b/scene-lib/src/annotations/io/JavapParser.java
new file mode 100644
index 0000000..c0bf4ae
--- /dev/null
+++ b/scene-lib/src/annotations/io/JavapParser.java
@@ -0,0 +1,413 @@
+package annotations.io;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.*;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import annotations.Annotation;
+import annotations.AnnotationBuilder;
+import annotations.AnnotationFactory;
+import annotations.Annotations;
+import annotations.el.*;
+
+import com.sun.tools.javac.code.TargetType;
+import com.sun.tools.javac.code.TypeAnnotationPosition;
+
+import plume.FileIOException;
+
+/**
+ * <code>JavapParser</code> provides a static method that parses a class dump
+ * in the form produced by <code>xjavap -s -verbose -annotations</code> and adds
+ * the annotations to an {@link AScene}, using the scene's
+ * {@link AnnotationFactory} to build individual annotations.
+ * If the scene's {@link AnnotationFactory} announces that it does not want an
+ * annotation found in the javap output, that annotation is skipped. Annotations
+ * from the javap output are merged into the scene; it is an error if both the
+ * scene and the javap output contain annotations of the same type on the same
+ * element.
+ *
+ * <p>
+ * THIS CLASS IS NOT FINISHED YET!
+ *
+ * <p>
+ * This class does not yet perform any error checking.  Expect strange
+ * behavior and/or exceptions if you give it bad input.
+ */
+public final class JavapParser {
+    private static final String SECTION_TITLE_PREFIX = "  ";
+    private static final String SECTION_DATA_PREFIX = "   ";
+    private static final String CONST_POOL_DATA_PREFIX = "const #";
+
+    private final AScene scene;
+
+    private final BufferedReader bin;
+    private String line; // null means end-of-file
+
+    private int lineNo = 0; // TEMP
+
+    private void nextLine() throws IOException {
+        do {
+            line = bin.readLine();
+            lineNo++;
+        } while (line != null && line.equals(""));
+    }
+
+    private void trim(String prefix) {
+        if (line.startsWith(prefix)) {
+            line = line.substring(prefix.length());
+        }
+    }
+
+    private boolean inMember() {
+        return line.startsWith(SECTION_TITLE_PREFIX);
+    }
+
+    private boolean inData() {
+        return line.startsWith(SECTION_DATA_PREFIX) ||
+            line.startsWith(CONST_POOL_DATA_PREFIX);
+    }
+
+    private enum TargetMode {
+        ORIGINAL, PARAMETER, EXTENDED
+    }
+
+    // This name comes from the section of the javap output that is being read.
+    private enum AnnotationSection {
+        RVA("RuntimeVisibleAnnotations", RetentionPolicy.RUNTIME, TargetMode.ORIGINAL),
+        RIA("RuntimeInvisibleAnnotations", RetentionPolicy.CLASS, TargetMode.ORIGINAL),
+        RVPA("RuntimeVisibleParameterAnnotations", RetentionPolicy.RUNTIME, TargetMode.PARAMETER),
+        RIPA("RuntimeInvisibleParameterAnnotations", RetentionPolicy.CLASS, TargetMode.PARAMETER),
+        RVEA("RuntimeVisibleTypeAnnotations", RetentionPolicy.RUNTIME, TargetMode.EXTENDED),
+        RIEA("RuntimeInvisibleTypeAnnotations", RetentionPolicy.CLASS, TargetMode.EXTENDED),
+        ;
+
+        final String secTitle;
+        final RetentionPolicy retention;
+        final TargetMode locMode;
+
+        AnnotationSection(String secTitle, RetentionPolicy retention, TargetMode locMode) {
+            this.secTitle = secTitle;
+            this.retention = retention;
+            this.locMode = locMode;
+        }
+    }
+
+    private String parseAnnotationHead() throws IOException, ParseException {
+        String annoTypeName = line.substring(
+                line.indexOf(annotationHead) + annotationHead.length(),
+                line.length() - 1).replace('/', '.');
+        nextLine();
+        return annoTypeName;
+    }
+
+    private static final String annotationHead = "//Annotation L"; // TEMP
+    private static final String tagHead = "type = "; // TEMP
+
+    private Annotation parseAnnotationBody(
+            AnnotationBuilder ab,
+            String indent) throws IOException, ParseException {
+        // Grab the fields
+        String fieldIndent = indent + " ";
+        while (line.startsWith(fieldIndent)) {
+            String line2 = line.substring(fieldIndent.length());
+            // Let the caller deal with location information, if any
+            if (line2.startsWith("target") || line2.startsWith("parameter")) {
+                break;
+            }
+            String fieldName =
+                line2.substring(line2.indexOf("//") + "//".length());
+            nextLine();
+            char tag = line.charAt(line.indexOf(tagHead) + tagHead.length());
+            switch (tag) {
+            case '[':
+                break;
+            case '@':
+                break;
+            case 'c':
+                break;
+            case 'e':
+                break;
+            }
+            // FINISH
+        }
+        return ab.finish();
+    }
+
+    private static final String paramIdxHead = "parameter = ";
+    private static final String offsetHead = "offset = ";
+    private static final String typeIndexHead = "type_index = ";
+    private static final Pattern localLocRegex =
+        Pattern.compile("^\\s*start_pc = (\\d+), length = (\\d+), index = (\\d+)$");
+    private static final String itlnHead = "location = ";
+
+    private int parseOffset() throws IOException, ParseException {
+        int offset = Integer.parseInt(
+                line.substring(line.indexOf(offsetHead) + offsetHead.length()));
+        nextLine();
+        return offset;
+    }
+
+    private int parseTypeIndex() throws IOException, ParseException {
+        int typeIndex = Integer.parseInt(
+                line.substring(line.indexOf(typeIndexHead) + typeIndexHead.length()));
+        nextLine();
+        return typeIndex;
+    }
+
+    private List<Integer> parseInnerTypeLocationNums() throws IOException, ParseException {
+        String numsStr
+            = line.substring(line.indexOf(itlnHead) + itlnHead.length());
+        List<Integer> nums = new ArrayList<Integer>();
+        for (;;) {
+            int comma = numsStr.indexOf(',');
+            if (comma == -1) {
+                nums.add(Integer.parseInt(numsStr));
+                break;
+            }
+            nums.add(Integer.parseInt(numsStr.substring(0, comma)));
+            numsStr = numsStr.substring(comma + 2);
+        }
+        nextLine();
+        return nums;
+    }
+
+    private AElement chooseSubElement(AElement member, AnnotationSection sec) throws IOException, ParseException {
+        switch (sec.locMode) {
+        case ORIGINAL:
+            // There can be no location information.
+            return member;
+        case PARAMETER:
+        {
+            // should have a "parameter = "
+            int paramIdx = Integer.parseInt(
+                    line.substring(
+                    line.indexOf(paramIdxHead) + paramIdxHead.length()));
+            nextLine();
+            return ((AMethod) member).parameters.vivify(paramIdx);
+        }
+        case EXTENDED:
+            // should have a "target = "
+            String targetTypeName =
+                line.substring(line.indexOf("//") + "//".length());
+            TargetType targetType;
+            TargetType tt = TargetType.valueOf(targetTypeName);
+            if (tt != null) {
+                targetType = tt;
+            } else {
+                throw new RuntimeException("null target type");
+            }
+            nextLine();
+            ATypeElement subOuterType;
+            AElement subElement;
+            switch (targetType) {
+            case FIELD:
+            case METHOD_RETURN:
+                subOuterType = (ATypeElement) member;
+                break;
+            case METHOD_RECEIVER:
+                subOuterType = ((AMethod) member).receiver.type;
+                break;
+            case METHOD_FORMAL_PARAMETER:
+                int paramIdx = Integer.parseInt(
+                        line.substring(
+                        line.indexOf(paramIdxHead) + paramIdxHead.length()));
+                nextLine();
+                subOuterType = ((AMethod) member).parameters.vivify(paramIdx).type;
+                break;
+            case LOCAL_VARIABLE:
+            case RESOURCE_VARIABLE:
+                int index, scopeStart, scopeLength;
+                Matcher m = localLocRegex.matcher(line);
+                m.matches();
+                index = Integer.parseInt(m.group(1));
+                scopeStart = Integer.parseInt(m.group(2));
+                scopeLength = Integer.parseInt(m.group(3));
+                LocalLocation ll =
+                    new LocalLocation(index, scopeStart, scopeLength);
+                nextLine();
+                subOuterType = ((AMethod) member).body.locals.vivify(ll).type;
+                break;
+            case CAST:
+            {
+                int offset = parseOffset();
+                int typeIndex = parseTypeIndex();
+                subOuterType = ((AMethod) member).body.typecasts.vivify(RelativeLocation.createOffset(offset, typeIndex));
+                break;
+            }
+            case INSTANCEOF:
+            {
+                int offset = parseOffset();
+                subOuterType = ((AMethod) member).body.instanceofs.vivify(RelativeLocation.createOffset(offset, 0));
+                break;
+            }
+            case NEW:
+            {
+                int offset = parseOffset();
+                subOuterType = ((AMethod) member).body.news.vivify(RelativeLocation.createOffset(offset, 0));
+                break;
+            }
+            default:
+                throw new AssertionError();
+            }
+            // TODO: update location representation
+            // if (targetType.) {
+                List<Integer> location = parseInnerTypeLocationNums();
+                InnerTypeLocation itl = new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(location));
+                subElement = subOuterType.innerTypes.vivify(itl);
+            // } else
+            //    subElement = subOuterType;
+            return subElement;
+        default:
+            throw new AssertionError();
+        }
+    }
+
+    private void parseAnnotationSection(AElement member, AnnotationSection sec) throws IOException, ParseException {
+        // FILL
+        while (inData()) {
+            String annoTypeName = parseAnnotationHead();
+            RetentionPolicy retention = sec.retention;
+            AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(annoTypeName, Annotations.getRetentionPolicyMetaAnnotationSet(retention));
+            if (ab == null) {
+                // don't care about the result
+                // but need to skip over it anyway
+                parseAnnotationBody(
+                        AnnotationFactory.saf.beginAnnotation(annoTypeName, Annotations.noAnnotations),
+                        SECTION_DATA_PREFIX);
+            } else {
+                // Wrap it in a TLA with the appropriate retention policy
+                Annotation a = parseAnnotationBody(ab, SECTION_DATA_PREFIX);
+                // Now we need to parse the location information to determine
+                // which element gets the annotation.
+                AElement annoMember = chooseSubElement(member, sec);
+                annoMember.tlAnnotationsHere.add(a);
+            }
+        }
+    }
+
+    private void parseMember(AElement member) throws IOException, ParseException {
+        while (inMember()) {
+            // New section
+            String secTitle =
+                line.substring(2, line.indexOf(':'));
+            AnnotationSection sec0 = null;
+            for (AnnotationSection s : AnnotationSection.values()) {
+                if (s.secTitle.equals(secTitle)) {
+                    sec0 = s;
+                }
+            }
+            if (sec0 != null) {
+                AnnotationSection sec = sec0;
+                nextLine();
+                System.out.println("Got section " + secTitle);
+                parseAnnotationSection(member, sec);
+            } else {
+                System.out.println("Got unrecognized section " + secTitle);
+                nextLine();
+                // Skip the section
+                while (inData()) {
+                    nextLine();
+                }
+            }
+        }
+    }
+
+    private void parseMethodBody(AElement clazz, String methodName) throws IOException, ParseException {
+        String sig = line.substring((SECTION_TITLE_PREFIX + "Signature: ").length());
+        nextLine();
+        String methodKey = methodName + sig;
+        System.out.println("Got method " + methodKey); // TEMP
+        parseMember(((AClass) clazz).methods.vivify(methodKey));
+    }
+
+    // the "clazz" might actually be a package in case of "interface package-info"
+    private void parseClass(AElement clazz) throws IOException, ParseException {
+        parseMember(clazz);
+
+        nextLine(); // {
+
+        while (!line.equals("}")) {
+            // new member
+            if (line.indexOf("static {}") >= 0) {
+                nextLine();
+                parseMethodBody(clazz, "<clinit>");
+            } else {
+                int lparen = line.indexOf('(');
+                if (lparen == -1) {
+                    // field
+                    int space = line.lastIndexOf(' ');
+                    String fieldName = line.substring(space + 1, line.length() - 1);
+                    nextLine();
+                    System.out.println("Got field " + fieldName); // TEMP
+                    parseMember(((AClass) clazz).fields.vivify(fieldName));
+                } else {
+                    // method
+                    int space = line.lastIndexOf(' ', lparen);
+                    String methodName = line.substring(space + 1, lparen);
+                    nextLine();
+                    parseMethodBody(clazz, methodName);
+                }
+            }
+        }
+        nextLine(); // }
+    }
+
+    private void parse() throws IOException, ParseException {
+        try { // TEMP
+        nextLine(); // get the first line
+
+        while (line != null) {
+            // new class
+            nextLine();
+            trim("public ");
+            trim("protected ");
+            trim("private ");
+            trim("abstract ");
+            trim("final ");
+            trim("class ");
+            trim("interface ");
+            int nameEnd = line.indexOf(' ');
+            String className = (nameEnd == -1) ? line
+                    : line.substring(0, line.indexOf(' '));
+            String pp = annotations.io.IOUtils.packagePart(className), bp = annotations.io.IOUtils.basenamePart(className);
+            nextLine();
+            if (bp.equals("package-info")) {
+                parseClass(scene.packages.vivify(pp));
+            } else {
+                parseClass(scene.classes.vivify(className));
+            }
+        }
+        } catch (RuntimeException e) {
+            throw new RuntimeException("Line " + lineNo, e);
+        }
+    }
+
+    private JavapParser(Reader in, AScene scene) {
+        bin = new BufferedReader(in);
+
+        this.scene = scene;
+    }
+
+    /**
+     * Transfers annotations from <code>in</code> to <code>scene</code>.
+     */
+    public static void parse(Reader in, AScene scene) throws IOException, ParseException {
+        new JavapParser(in, scene).parse();
+    }
+
+    public static void parse(String filename, AScene scene) throws IOException, FileIOException {
+        LineNumberReader lnr = new LineNumberReader(new FileReader(filename));
+        try {
+            parse(lnr, scene);
+        } catch (ParseException e) {
+            throw new FileIOException(lnr, filename, e);
+        }
+    }
+}
diff --git a/scene-lib/src/annotations/io/ParseException.java b/scene-lib/src/annotations/io/ParseException.java
new file mode 100644
index 0000000..7b3c03d
--- /dev/null
+++ b/scene-lib/src/annotations/io/ParseException.java
@@ -0,0 +1,36 @@
+package annotations.io;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+// Not package-private because it is used from the Scene Library.
+// But not intended for widespread use.
+
+/**
+ * Thrown when index file or javap parsing fails.
+ * <p>
+ *
+ * Because of the way the parser is implemented, sometimes the error message
+ * isn't very good; in particular, it sometimes says "expected A, B or C"
+ * when there are legal tokens other than A, B, and C.
+ */
+public final class ParseException extends Exception {
+
+    public ParseException() {
+        super();
+    }
+
+    public ParseException(String message) {
+        super(message);
+    }
+
+    public ParseException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public ParseException(Throwable cause)  {
+        super(cause);
+    }
+
+}
diff --git a/scene-lib/src/annotations/io/classfile/ClassAnnotationSceneReader.java b/scene-lib/src/annotations/io/classfile/ClassAnnotationSceneReader.java
new file mode 100644
index 0000000..d1de0cc
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/ClassAnnotationSceneReader.java
@@ -0,0 +1,1224 @@
+// This class is a complete ClassVisitor with many hidden classes that do
+// the work of reading annotations from a class file and inserting them into
+// an AScene.
+package annotations.io.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.File;
+import java.util.*;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+import annotations.*;
+import annotations.el.*;
+import annotations.field.*;
+
+import com.sun.tools.javac.code.TargetType;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * A <code> ClassAnnotationSceneReader </code> is a
+ * {@link org.objectweb.asm.ClassVisitor} that will insert all annotations it
+ * encounters while visiting a class into a given {@link AScene}.
+ *
+ * The "read" in <code> ClassAnnotationSceneReader </code> refers to a class
+ * file being read into a scene.  Also see {@link ClassAnnotationSceneWriter}.
+ *
+ * <p>
+ *
+ * The proper usage of this class is to construct a
+ * <code>ClassAnnotationSceneReader}</code> with an {@link AScene} into which
+ * annotations should be inserted, then pass this as a
+ * {@link org.objectweb.asm.ClassVisitor} to
+ * {@link org.objectweb.asm.ClassReader#accept}
+ *
+ * <p>
+ *
+ * All other methods are intended to be called only by
+ * {@link org.objectweb.asm.ClassReader#accept},
+ * and should not be called anywhere else, due to the order in which
+ * {@link org.objectweb.asm.ClassVisitor} methods should be called.
+ */
+public class ClassAnnotationSceneReader
+extends EmptyVisitor {
+  // general strategy:
+  // -only "Runtime[In]visible[Type]Annotations" are supported
+  // -use an empty visitor for everything besides annotations, fields and
+  //  methods; for those three, use a special visitor that does all the work
+  //  and inserts the annotations correctly into the specified AElement
+
+  // Whether to output tracing information
+  private static final boolean trace = false;
+
+  // Whether to output error messages for unsupported cases
+  private static final boolean strict = false;
+
+  // Whether to include annotations on compiler-generated methods
+  private final boolean ignoreBridgeMethods;
+
+  // The scene into which this class will insert annotations.
+  private final AScene scene;
+
+  // The AClass that represents this class in scene.
+  private AClass aClass;
+
+  private final ClassReader cr;
+
+  /**
+   * Holds definitions we've seen so far.  Maps from annotation name to
+   * the definition itself.  Maps from both the qualified name and the
+   * unqualified name.  If the unqualified name is not unique, it maps
+   * to null and the qualified name should be used instead. */
+  private final Map<String, AnnotationDef> adefs = initAdefs();
+  private static Map<String,AnnotationDef> initAdefs() {
+    Map<String,AnnotationDef> result = new HashMap<String,AnnotationDef>();
+    for (AnnotationDef ad : Annotations.standardDefs) {
+      result.put(ad.name, ad);
+    }
+    return result;
+  }
+
+  /**
+   * constructs a new <code> ClassAnnotationSceneReader </code> that will
+   * insert all the annotations in the class that it visits into
+   * <code> scene </code>
+   * @param cr
+   *
+   * @param scene the annotation scene into which annotations this visits
+   *  will be inserted
+   * @param ignoreBridgeMethods whether to omit annotations on
+   *  compiler-generated methods
+   */
+  public ClassAnnotationSceneReader(ClassReader cr, AScene scene,
+      boolean ignoreBridgeMethods) {
+    this.cr = cr;
+    this.scene = scene;
+    this.ignoreBridgeMethods = ignoreBridgeMethods;
+  }
+
+  /**
+   * @see org.objectweb.asm.commons.EmptyVisitor#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
+   */
+  @Override
+  public void visit(int version, int access, String name, String signature,
+      String superName, String[] interfaces) {
+    aClass = scene.classes.vivify(name.replace('/', '.'));
+  }
+
+  /**
+   * @see org.objectweb.asm.commons.EmptyVisitor#visitAnnotation(java.lang.String, boolean)
+   */
+  @Override
+  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+    if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", desc, visible, this, this.getClass()); }
+    return visitTypeAnnotation(desc, visible, false);
+  }
+
+  /**
+   * @see org.objectweb.asm.commons.EmptyVisitor#visitTypeAnnotation(java.lang.String, boolean, boolean)
+   */
+  @Override
+  public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
+    if (trace) { System.out.printf("visitTypeAnnotation(%s, %s, %s); aClass=%s in %s (%s)%n", desc, inCode, visible, aClass, this, this.getClass()); }
+    return new AnnotationSceneReader(desc, visible, aClass);
+  }
+
+  /**
+   * @see org.objectweb.asm.commons.EmptyVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
+   */
+  @Override
+  public FieldVisitor visitField(
+      int access,
+      String name,
+      String desc,
+      String signature,
+      Object value  ) {
+    if (trace) { System.out.printf("visitField(%s, %s, %s, %s, %s) in %s (%s)%n", access, name, desc, signature, value, this, this.getClass()); }
+    AField aField = aClass.fields.vivify(name);
+    return new FieldAnnotationSceneReader(name, desc, signature, value, aField);
+  }
+
+  /**
+   * @see org.objectweb.asm.commons.EmptyVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
+   */
+  @Override
+  public MethodVisitor visitMethod(
+      int access,
+      String name,
+      String desc,
+      String signature,
+      String[] exceptions) {
+    if (ignoreBridgeMethods && (access & Opcodes.ACC_BRIDGE) != 0) {
+      return null;
+    }
+    if (trace) { System.out.printf("visitMethod(%s, %s, %s, %s, %s) in %s (%s)%n", access, name, desc, signature, exceptions, this, this.getClass()); }
+    AMethod aMethod = aClass.methods.vivify(name+desc);
+    return new MethodAnnotationSceneReader(name, desc, signature, aMethod);
+  }
+
+  // converts JVML format to Java format
+  private static String classDescToName(String desc) {
+    return desc.substring(1, desc.length() - 1).replace('/', '.');
+  }
+
+
+  ///////////////////////////////////////////////////////////////////////////
+  /// Inner classes
+  ///
+
+  // Hackish workaround for odd subclassing.
+  @SuppressWarnings("signature")
+  String dummyDesc = "dummy";
+
+  /*
+   * Most of the complexity behind reading annotations from a class file into
+   * a scene is in AnnotationSceneReader, which fully implements the
+   * TypeAnnotationVisitor interface (and therefore also implements the
+   * AnnotationVisitor interface).  It keeps an AElement of the
+   * element into which this should insert the annotations it visits in
+   * a class file.  Thus, constructing an AnnotationSceneReader with an
+   * AElement of the right type is sufficient for writing out annotations
+   * to that element, which will be done once visitEnd() is called.  Note that
+   * for when inner annotations are expected, the aElement passed in must be
+   * of the correct form (ATypeElement, or AMethod depending on the
+   * target type of the extended annotation).
+   */
+  private class AnnotationSceneReader implements TypeAnnotationVisitor {
+    // Implementation strategy:
+    // For field values and enums, simply pass the information
+    //  onto annotationBuilder.
+    // For arrays, use an ArrayAnnotationBuilder that will
+    //  properly call the right annotationBuilder methods on its visitEnd().
+    // For nested annotations, use a NestedAnnotationSceneReader that will
+    //  properly call the right annotationBuilder methods on its visitEnd().
+    // For extended information, store all arguments passed in and on
+    //  this.visitEnd(), handle all the information based on target type.
+
+
+    // The AElement into which the annotation visited should be inserted.
+    protected AElement aElement;
+
+    // Whether or not this annotation is visible at runtime.
+    protected boolean visible;
+
+    // The AnnotationBuilder used to create this annotation.
+    private AnnotationBuilder annotationBuilder;
+
+    // since AnnotationSceneReader will work for both normal
+    // and extended annotations, all of the following information
+    // may or may not be present, so use a list to store
+    // information as it is received from visitX* methods, and
+    // correctly interpret the information in visitEnd()
+    // note that all of these should contain 0 or 1 elements,
+    // except for xLocations, which is actually a list
+    private final List<Integer> xTargetTypeArgs;
+    private final List<Integer> xIndexArgs;
+    private final List<Integer> xLengthArgs;
+    private final List<TypePathEntry> xLocationsArgs;
+    private final List<Integer> xLocationLengthArgs;
+    private final List<Integer> xOffsetArgs;
+    private final List<Integer> xStartPcArgs;
+    private final List<Integer> xParamIndexArgs;
+    private final List<Integer> xBoundIndexArgs;
+    private final List<Integer> xExceptionIndexArgs;
+    private final List<Integer> xTypeIndexArgs;
+
+    // private AnnotationDef getAnnotationDef(Object o) {
+    //   if (o instanceof AnnotationDef) {
+    //     return (AnnotationDef) o;
+    //   } else if (o instanceof String) {
+    //     return getAnnotationDef((String) o);
+    //   } else {
+    //     throw new Error(String.format("bad type %s : %s", o.getClass(), o));
+    //   }
+    // }
+
+    @SuppressWarnings("unchecked")
+    private AnnotationDef getAnnotationDef(String jvmlClassName) {
+      String annoTypeName = classDescToName(jvmlClassName);
+      // It would be better to not require the .class file to be on the
+      // classpath, but to search for it on a path that is passed to this
+      // program.  Worry about that later.
+      Class<? extends java.lang.annotation.Annotation> annoClass;
+      try {
+        annoClass = (Class<? extends java.lang.annotation.Annotation>) Class.forName(annoTypeName);
+      } catch (ClassNotFoundException e) {
+        System.out.printf("Could not find class: %s%n", e.getMessage());
+        printClasspath();
+        if (annoTypeName.contains("+")) {
+          return Annotations.createValueAnnotationDef(annoTypeName,
+              Annotations.noAnnotations, BasicAFT.forType(int.class));
+        }
+        throw new Error(e);
+      }
+
+      AnnotationDef ad = AnnotationDef.fromClass(annoClass, adefs);
+
+      return ad;
+    }
+
+
+    /*
+     * Constructs a new AnnotationScene reader with the given description and
+     * visibility.  Calling visitEnd() will ensure that this writes out the
+     * annotation it visits into aElement.
+     * @param desc JVML format for the field being read, or ClassAnnotationSceneReader.dummyDesc
+     */
+    public AnnotationSceneReader(String desc, boolean visible, AElement aElement) {
+      if (trace) { System.out.printf("AnnotationSceneReader(%s, %s, %s)%n", desc, visible, aElement); }
+      this.visible = visible;
+      this.aElement = aElement;
+      if (desc != dummyDesc) {    // interned
+        AnnotationDef ad = getAnnotationDef(desc);
+
+        AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(ad);
+        if (ab == null) {
+          throw new IllegalArgumentException("bad description: " + desc);
+        } else {
+          this.annotationBuilder = ab;
+        }
+      }
+
+      // For legal annotations, and except for xLocationsArgs, these should
+      //  contain at most one element.
+      this.xTargetTypeArgs = new ArrayList<Integer>(1);
+      this.xIndexArgs = new ArrayList<Integer>(1);
+      this.xLengthArgs = new ArrayList<Integer>(1);
+      this.xLocationLengthArgs = new ArrayList<Integer>(1);
+      this.xOffsetArgs = new ArrayList<Integer>(1);
+      this.xStartPcArgs = new ArrayList<Integer>(1);
+      this.xLocationsArgs = new ArrayList<TypePathEntry>();
+      this.xParamIndexArgs = new ArrayList<Integer>(1);
+      this.xBoundIndexArgs = new ArrayList<Integer>(1);
+      this.xExceptionIndexArgs = new ArrayList<Integer>(1);
+      this.xTypeIndexArgs = new ArrayList<Integer>(1);
+    }
+
+    /*
+     * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
+     */
+    @Override
+    public void visit(String name, Object value) {
+      if (trace) { System.out.printf("visit(%s, %s) on %s%n", name, value, this); }
+      // BasicAFT.forType(Class) expects int.class instead of Integer.class,
+      // and so on for all primitives.  String.class is ok, since it has no
+      // primitive type.
+      Class<?> c = value.getClass();
+      if (c.equals(Boolean.class)) {
+        c = boolean.class;
+      } else if (c.equals(Byte.class)) {
+        c = byte.class;
+      } else if (c.equals(Character.class)) {
+        c = char.class;
+      } else if (c.equals(Short.class)) {
+        c = short.class;
+      } else if (c.equals(Integer.class)) {
+        c = int.class;
+      } else if (c.equals(Long.class)) {
+        c = long.class;
+      } else if (c.equals(Float.class)) {
+        c = float.class;
+      } else if (c.equals(Double.class)) {
+        c = double.class;
+      } else if (c.equals(Type.class)) {
+        try {
+          annotationBuilder.addScalarField(name, ClassTokenAFT.ctaft, Class.forName(((Type)value).getClassName()));
+        } catch (ClassNotFoundException e) {
+          throw new RuntimeException("Could not load Class for Type: " + value, e);
+        }
+        // Return here, otherwise the annotationBuilder would be called
+        // twice for the same value.
+        return;
+      } else if (!c.equals(String.class)) {
+        // Only possible type for value is String, in which case c is already
+        // String.class, or array of primitive
+        c = c.getComponentType();
+        ArrayBuilder arrayBuilder = annotationBuilder.beginArrayField(
+            name, new ArrayAFT(BasicAFT.forType(c)));
+        // value is of type c[], now add in all the elements of the array
+        for (Object o : asList(value)) {
+          arrayBuilder.appendElement(o);
+        }
+        arrayBuilder.finish();
+        return;
+      }
+
+      // handle everything but arrays
+      annotationBuilder.addScalarField(name, BasicAFT.forType(c),value);
+
+    }
+
+    /*
+     * Method that accepts an Object whose actual type is c[], where c is a
+     * primitive, and returns an equivalent List<Object> that contains
+     * the same elements as in hiddenArray.
+     */
+    private List<Object> asList(Object hiddenArray) {
+      List<Object> objects = new ArrayList<Object>();
+      Class<?> c = hiddenArray.getClass().getComponentType();
+      if (c.equals(boolean.class)) {
+        for (boolean o : (boolean[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else if (c.equals(byte.class)) {
+        for (byte o : (byte[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else if (c.equals(char.class)) {
+        for (char o : (char[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else if (c.equals(short.class)) {
+        for (short o : (short[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else if (c.equals(int.class)) {
+        for (int o : (int[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else if (c.equals(long.class)) {
+        for (long o : (long[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else if (c.equals(float.class)) {
+        for (float o : (float[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else if (c.equals(double.class)) {
+        for (double o : (double[]) hiddenArray) {
+          objects.add(o);
+        }
+      } else {
+        throw new RuntimeException("Array has unknown type: " + hiddenArray);
+      }
+      return objects;
+    }
+
+    /*
+     * @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
+     */
+    @Override
+    public void visitEnum(String name, String desc, String value) {
+      if (trace) { System.out.printf("visitEnum(%s, %s) in %s (%s)%n", name, desc, this, this.getClass()); }
+      annotationBuilder.addScalarField(name, new EnumAFT(desc), value);
+    }
+
+    /*
+     * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
+     */
+    @Override
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+      if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", name, desc, this, this.getClass()); }
+      return new NestedAnnotationSceneReader(this, name, desc);
+    }
+
+    /*
+     * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
+     */
+    @Override
+    public AnnotationVisitor visitArray(String name) {
+      if (trace) { System.out.printf("visitArray(%s) in %s (%s)%n", name, this, this.getClass()); }
+      ArrayAFT aaft = (ArrayAFT) annotationBuilder.fieldTypes().get(name);
+      ScalarAFT aft = aaft.elementType;
+      return new ArrayAnnotationSceneReader(this, name, aft);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXTargetType(int)
+     */
+    @Override
+    public void visitXTargetType(int target_type) {
+      xTargetTypeArgs.add(target_type);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXIndex(int)
+     */
+    @Override
+    public void visitXIndex(int index) {
+      xIndexArgs.add(index);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLength(int)
+     */
+    @Override
+    public void visitXLength(int length) {
+      xLengthArgs.add(length);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocation(TypePathEntry)
+     */
+    @Override
+    public void visitXLocation(TypePathEntry location) {
+      xLocationsArgs.add(location);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocationLength(int)
+     */
+    @Override
+    public void visitXLocationLength(int location_length) {
+      xLocationLengthArgs.add(location_length);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXOffset(int)
+     */
+    @Override
+    public void visitXOffset(int offset) {
+      xOffsetArgs.add(offset);
+    }
+
+    @Override
+    public void visitXNumEntries(int num_entries) {
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXStartPc(int)
+     */
+    @Override
+    public void visitXStartPc(int start_pc) {
+      xStartPcArgs.add(start_pc);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXBoundIndex(int)
+     */
+    @Override
+    public void visitXParamIndex(int param_index) {
+      xParamIndexArgs.add(param_index);
+    }
+
+    /*
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitXBoundIndex(int)
+     */
+    @Override
+    public void visitXBoundIndex(int bound_index) {
+      xBoundIndexArgs.add(bound_index);
+    }
+
+    @Override
+    public void visitXTypeIndex(int type_index) {
+      xTypeIndexArgs.add(type_index);
+    }
+
+    @Override
+    public void visitXExceptionIndex(int exception_index) {
+      xExceptionIndexArgs.add(exception_index);
+    }
+
+    @Override
+    public void visitXNameAndArgsSize() {
+    }
+
+    /*
+     * Visits the end of the annotation, and actually writes out the
+     *  annotation into aElement.
+     *
+     * @see org.objectweb.asm.TypeAnnotationVisitor#visitEnd()
+     */
+    @Override
+    public void visitEnd() {
+      if (trace) { System.out.printf("visitEnd on %s (%s)%n", this, this.getClass()); }
+      if (xTargetTypeArgs.size() >= 1) {
+        TargetType target = TargetType.fromTargetTypeValue(xTargetTypeArgs.get(0));
+        // TEMP
+        // If the expression used to initialize a field contains annotations
+        // on instanceofs, typecasts, or news, the extended compiler enters
+        // those annotations on the field.  If we get such an annotation and
+        // aElement is a field, skip the annotation for now to avoid crashing.
+        switch(target) {
+        case FIELD:
+          handleField(aElement);
+          break;
+        case LOCAL_VARIABLE:
+        case RESOURCE_VARIABLE:
+          handleMethodLocalVariable((AMethod) aElement);
+          break;
+        case NEW:
+          if (aElement instanceof AMethod) {
+            handleMethodObjectCreation((AMethod) aElement);
+          } else {
+            // TODO: in field initializers
+            if (strict) { System.err.println("Unhandled NEW annotation for " + aElement); }
+          }
+          break;
+        case METHOD_FORMAL_PARAMETER:
+          handleMethodParameterType((AMethod) aElement);
+          break;
+        case METHOD_RECEIVER:
+            handleMethodReceiver((AMethod) aElement);
+            break;
+        case CAST:
+          if (aElement instanceof AMethod) {
+            handleMethodTypecast((AMethod) aElement);
+          } else {
+              // TODO: in field initializers
+              if (strict) { System.err.println("Unhandled TYPECAST annotation for " + aElement); }
+          }
+          break;
+        case METHOD_RETURN:
+          handleMethodReturnType((AMethod) aElement);
+          break;
+        case INSTANCEOF:
+          if (aElement instanceof AMethod) {
+            handleMethodInstanceOf((AMethod) aElement);
+          } else {
+              // TODO: in field initializers
+              if (strict) { System.err.println("Unhandled INSTANCEOF annotation for " + aElement); }
+          }
+          break;
+        case CLASS_TYPE_PARAMETER_BOUND:
+          handleClassTypeParameterBound((AClass) aElement);
+          break;
+        case METHOD_TYPE_PARAMETER_BOUND:
+          handleMethodTypeParameterBound((AMethod) aElement);
+          break;
+        case CLASS_EXTENDS:
+          handleClassExtends((AClass) aElement);
+          break;
+        case THROWS:
+          handleThrows((AMethod) aElement);
+          break;
+        case CONSTRUCTOR_REFERENCE:  // TODO
+        case METHOD_REFERENCE:
+          handleMethodReference((AMethod) aElement);
+          break;
+        case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:  // TODO
+        case METHOD_REFERENCE_TYPE_ARGUMENT:
+          handleReferenceTypeArgument((AMethod) aElement);
+          break;
+        case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:  // TODO
+        case METHOD_INVOCATION_TYPE_ARGUMENT:
+          handleCallTypeArgument((AMethod) aElement);
+          break;
+        case METHOD_TYPE_PARAMETER:
+          handleMethodTypeParameter((AMethod) aElement);
+          break;
+        case CLASS_TYPE_PARAMETER:
+          handleClassTypeParameter((AClass) aElement);
+          break;
+
+        // TODO: ensure all cases covered.
+        default:
+          // Rather than throw an error here, since a declaration annotation
+          // is being used as an extended annotation, just make the
+          // annotation and place it in the given aElement as usual.
+
+          // aElement.tlAnnotationsHere.add(makeAnnotation());
+          Annotation a = makeAnnotation();
+          aElement.tlAnnotationsHere.add(a);
+        }
+      } else {
+        // This is not an extended annotation visitor, so just
+        // make the annotation and place it in the given AElement,
+        // possibly moving it to a type annotation location instead.
+
+        Annotation a = makeAnnotation();
+
+        if (a.def.isTypeAnnotation() && (aElement instanceof AMethod)) {
+          AMethod m = (AMethod) aElement;
+          m.returnType.tlAnnotationsHere.add(a);
+
+          // There is not currently a separate location for field/parameter
+          // type annotations; they are mixed in with the declaration
+          // annotations.  This should be fixed in the future.
+          // Also, fields/parameters are just represented as AElement.
+          // } else if (a.def.isTypeAnnotation() && (aElement instanceof AField)) {
+
+        } else {
+          aElement.tlAnnotationsHere.add(a);
+        }
+      }
+    }
+
+    // The following are utility methods to facilitate creating all the
+    // necessary data structures in the scene library.
+
+    /*
+     * Returns an annotation, ready to be placed into the scene, from
+     *  the information visited.
+     */
+    public Annotation makeAnnotation() {
+      return annotationBuilder.finish();
+    }
+
+    /*
+     * Returns a LocalLocation for this annotation.
+     */
+    private LocalLocation makeLocalLocation() {
+      int index = xIndexArgs.get(0);
+      int length = xLengthArgs.get(0);
+      int start = xStartPcArgs.get(0);
+      return new LocalLocation(index, start, length);
+    }
+
+    /*
+     * Returns an InnerTypeLocation for this annotation.
+     */
+    private InnerTypeLocation makeInnerTypeLocation() {
+      return new InnerTypeLocation(xLocationsArgs);
+    }
+
+    /*
+     * Returns the offset for this annotation.
+     */
+    private RelativeLocation makeOffset(boolean needTypeIndex) {
+      int offset = xOffsetArgs.get(0);
+      int typeIndex = needTypeIndex ? xTypeIndexArgs.get(0) : 0;
+      return RelativeLocation.createOffset(offset, typeIndex);
+    }
+
+    /*
+     * Returns the index for this annotation.
+     */
+    /*
+    private int makeIndex() {
+      return xIndexArgs.get(0);
+    }
+    */
+
+    /*
+     * Returns the bound location for this annotation.
+     */
+    private BoundLocation makeTypeParameterLocation() {
+      if (!xParamIndexArgs.isEmpty()) {
+        return new BoundLocation(xParamIndexArgs.get(0), -1);
+      } else {
+        if (strict) { System.err.println("makeTypeParameterLocation with empty xParamIndexArgs!"); }
+        return new BoundLocation(Integer.MAX_VALUE, -1);
+      }
+    }
+
+    /*
+     * Returns the bound location for this annotation.
+     * @see #makeTypeParameterLocation()
+     */
+    private BoundLocation makeBoundLocation() {
+      // TODO: Give up on unbounded wildcards for now!
+      if (!xParamIndexArgs.isEmpty()) {
+        return new BoundLocation(xParamIndexArgs.get(0), xBoundIndexArgs.get(0));
+      } else {
+        if (strict) { System.err.println("makeBoundLocation with empty xParamIndexArgs!"); }
+        return new BoundLocation(Integer.MAX_VALUE, Integer.MAX_VALUE);
+      }
+    }
+
+    private TypeIndexLocation makeTypeIndexLocation() {
+      return new TypeIndexLocation(xTypeIndexArgs.get(0));
+    }
+
+    // TODO: makeExceptionIndexLocation?
+
+    /*
+     * Creates the inner annotation on aElement.innerTypes.
+     */
+    private void handleField(AElement aElement) {
+      if (xLocationsArgs.isEmpty()) {
+        // TODO: resolve issue once classfile format is finalized
+        if (aElement instanceof AClass) {
+          // handleFieldOnClass((AClass) aElement);
+          if (strict) { System.err.println("Unhandled FIELD annotation for " + aElement); }
+        } else if (aElement instanceof ATypeElement) {
+          aElement.tlAnnotationsHere.add(makeAnnotation());
+        } else {
+          throw new RuntimeException("Unknown FIELD aElement: " + aElement);
+        }
+      } else {
+        // TODO: resolve issue once classfile format is finalized
+        if (aElement instanceof AClass) {
+          // handleFieldGenericArrayOnClass((AClass) aElement);
+          if (strict) { System.err.println("Unhandled FIELD_COMPONENT annotation for " + aElement); }
+        } else if (aElement instanceof ATypeElement) {
+          ATypeElement aTypeElement = (ATypeElement) aElement;
+          aTypeElement
+              .innerTypes.vivify(makeInnerTypeLocation()).
+              tlAnnotationsHere.add(makeAnnotation());
+        } else {
+          throw new RuntimeException("Unknown FIELD_COMPONENT: " + aElement);
+        }
+      }
+    }
+
+    /*
+     * Creates the method receiver annotation on aMethod.
+     */
+    private void handleMethodReceiver(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.receiver.type
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.receiver.type
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    /*
+     * Creates the local variable annotation on aMethod.
+     */
+    private void handleMethodLocalVariable(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.body.locals.vivify(makeLocalLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.body.locals.vivify(makeLocalLocation())
+            .type.innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    /*
+     * Creates the object creation annotation on aMethod.
+     */
+    private void handleMethodObjectCreation(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.body.news.vivify(makeOffset(false))
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.body.news.vivify(makeOffset(false))
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    private int makeParamIndex() {
+      return xParamIndexArgs.get(0);
+    }
+
+    /*
+     * Creates the method parameter type generic/array annotation on aMethod.
+     */
+    private void handleMethodParameterType(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.parameters.vivify(makeParamIndex()).type.tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.parameters.vivify(makeParamIndex()).type.innerTypes.vivify(
+            makeInnerTypeLocation()).tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    /*
+     * Creates the typecast annotation on aMethod.
+     */
+    private void handleMethodTypecast(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.body.typecasts.vivify(makeOffset(true))
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.body.typecasts.vivify(makeOffset(true))
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    /*
+     * Creates the method return type generic/array annotation on aMethod.
+     */
+    private void handleMethodReturnType(AMethod aMethod) {
+      // TODO: why is this traced and not other stuff?
+      if (trace) { System.out.printf("handleMethodReturnType(%s)%n", aMethod); }
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.returnType
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.returnType
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    /*
+     * Creates the method instance of annotation on aMethod.
+     */
+    private void handleMethodInstanceOf(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.body.instanceofs.vivify(makeOffset(false))
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.body.typecasts.vivify(makeOffset(false))
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    /*
+     * Creates the class type parameter bound annotation on aClass.
+     */
+    private void handleClassTypeParameter(AClass aClass) {
+      aClass.bounds.vivify(makeTypeParameterLocation())
+          .tlAnnotationsHere.add(makeAnnotation());
+    }
+
+    /*
+     * Creates the class type parameter bound annotation on aClass.
+     */
+    private void handleClassTypeParameterBound(AClass aClass) {
+      if (xLocationsArgs.isEmpty()) {
+        aClass.bounds.vivify(makeBoundLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aClass.bounds.vivify(makeBoundLocation())
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    /*
+     * Creates the class type parameter bound annotation on aClass.
+     */
+    private void handleMethodTypeParameterBound(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.bounds.vivify(makeBoundLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.bounds.vivify(makeBoundLocation())
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    private void handleClassExtends(AClass aClass) {
+      if (xLocationsArgs.isEmpty()) {
+        aClass.extendsImplements.vivify(makeTypeIndexLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aClass.extendsImplements.vivify(makeTypeIndexLocation())
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    private void handleThrows(AMethod aMethod) {
+      aMethod.throwsException.vivify(makeTypeIndexLocation())
+          .tlAnnotationsHere.add(makeAnnotation());
+    }
+
+    private void handleNewTypeArgument(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        // aMethod.news.vivify(makeOffset()).innerTypes.vivify();
+            // makeInnerTypeLocation()).tlAnnotationsHere.add(makeAnnotation());
+        if (strict) { System.err.println("Unhandled handleNewTypeArgument on aMethod: " + aMethod); }
+      } else {
+        // if (strict) { System.err.println("Unhandled handleNewTypeArgumentGenericArray on aMethod: " + aMethod); }
+      }
+    }
+
+    private void handleMethodReference(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.body.refs.vivify(makeOffset(false))
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.body.refs.vivify(makeOffset(false))
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    private void handleReferenceTypeArgument(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.body.refs.vivify(makeOffset(true))
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.body.refs.vivify(makeOffset(true))
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    private void handleCallTypeArgument(AMethod aMethod) {
+      if (xLocationsArgs.isEmpty()) {
+        aMethod.body.calls.vivify(makeOffset(true))
+            .tlAnnotationsHere.add(makeAnnotation());
+      } else {
+        aMethod.body.calls.vivify(makeOffset(true))
+            .innerTypes.vivify(makeInnerTypeLocation())
+            .tlAnnotationsHere.add(makeAnnotation());
+      }
+    }
+
+    private void handleMethodTypeParameter(AMethod aMethod) {
+      // TODO: throw new RuntimeException("METHOD_TYPE_PARAMETER: to do");
+    }
+
+    /*
+     * Hook for NestedAnnotationSceneReader; overridden by
+     * ArrayAnnotationSceneReader to add an array element instead of a field
+     */
+    void supplySubannotation(String fieldName, Annotation annotation) {
+      annotationBuilder.addScalarField(fieldName,
+          new AnnotationAFT(annotation.def()), annotation);
+    }
+
+    @Override
+    public String toString() {
+      return String.format("(AnnotationSceneReader: %s %s %s)",
+                           aElement, visible, annotationBuilder);
+    }
+
+  }
+
+  /*
+   * A NestedAnnotationSceneReader is an AnnotationSceneReader
+   * that will read in an entire annotation on a field (of type annotation)
+   * of its parent, and once it has fully visited that annotation, it will
+   * call its parent annotation builder to include that field, so after
+   * its parent constructs and returns this as an AnnotationVisitor
+   * (visitAnnotation()), it no longer needs to worry about that field.
+   */
+  private class NestedAnnotationSceneReader extends AnnotationSceneReader {
+    private final AnnotationSceneReader parent;
+    private final String name;
+    // private final String desc;
+
+    public NestedAnnotationSceneReader(AnnotationSceneReader parent,
+        String name, String desc) {
+      super(desc, parent.visible, parent.aElement);
+      if (trace) { System.out.printf("NestedAnnotationSceneReader(%s, %s, %s)%n", parent, name, desc); }
+      this.parent = parent;
+      this.name = name;
+      // this.desc = desc;
+    }
+
+    @Override
+    public void visitEnd() {
+      // Do not call super, as that already builds the annotation, causing an exception.
+      // super.visitEnd();
+      if (trace) { System.out.printf("visitEnd on %s (%s)%n", this, this.getClass()); }
+      Annotation a = super.makeAnnotation();
+      parent.supplySubannotation(name, a);
+    }
+  }
+
+  /*
+   * An ArrayAnnotationSceneReader is an AnnotationSceneReader
+   * that reads all elements of an array field
+   * of its parent, and once it has fully visited the array, it will
+   * call its parent annotation builder to include that array, so after
+   * its parent constructs and returns this as an AnnotationVisitor
+   * (visitArray()), it no longer needs to worry about that array.
+   *
+   * Note that by specification of AnnotationVisitor.visitArray(), the only
+   * methods that should be called on this are visit(String name, Object value)
+   * and visitEnd().
+   */
+  // An AnnotationSceneReader reads an annotation.  An
+  // ArrayAnnotationSceneReader reads an arbitrary array field, but not an
+  // entire annotation.  So why is ArrayAnnotationSceneReader a subclass of
+  // AnnotationSceneReader?  Pass ClassAnnotationSceneReader.dummyDesc
+  // in the superclass constructor to
+  // disable superclass behaviors that would otherwise cause trouble.
+  private class ArrayAnnotationSceneReader extends AnnotationSceneReader {
+    private final AnnotationSceneReader parent;
+    private ArrayBuilder arrayBuilder;
+    // private ScalarAFT elementType;
+    private final String arrayName;
+
+    // The element type may be unknown when this is called.
+    // But AnnotationSceneReader expects to know the element type.
+    public ArrayAnnotationSceneReader(AnnotationSceneReader parent,
+        String fieldName, AnnotationFieldType eltType) {
+      super(dummyDesc, parent.visible, parent.aElement);
+      if (trace) { System.out.printf("ArrayAnnotationSceneReader(%s, %s)%n", parent, fieldName); }
+      this.parent = parent;
+      this.arrayName = fieldName;
+      this.arrayBuilder = null;
+    }
+
+    private void prepareForElement(ScalarAFT elementType) {
+      if (trace) { System.out.printf("prepareForElement(%s) in %s (%s)%n", elementType, this, this.getClass()); }
+      assert elementType != null; // but, does this happen when reading from classfile?
+      if (arrayBuilder == null) {
+        // this.elementType = elementType;
+        arrayBuilder = parent.annotationBuilder.beginArrayField(arrayName,
+                new ArrayAFT(elementType));
+      }
+    }
+
+    // There are only so many different array types that are permitted in
+    // an annotation.  (I'm not sure how relevant that is here.)
+    @Override
+    public void visit(String name, Object value) {
+      if (trace) { System.out.printf("visit(%s, %s) (%s) in %s (%s)%n", name, value, value.getClass(), this, this.getClass()); }
+      ScalarAFT aft;
+      if (value.getClass().equals(org.objectweb.asm.Type.class)) {
+        // What if it's an annotation?
+        aft = ClassTokenAFT.ctaft;
+        try {
+          value = Class.forName(((org.objectweb.asm.Type) value).getClassName());
+        } catch (ClassNotFoundException e) {
+          throw new RuntimeException("Could not load Class for Type: " + value, e);
+        }
+      } else {
+        Class<?> vc = value.getClass();
+        aft = BasicAFT.forType(vc);
+        // or: aft = (ScalarAFT) AnnotationFieldType.fromClass(vc, null);
+      }
+      assert aft != null;
+      prepareForElement(aft);
+      assert arrayBuilder != null;
+      arrayBuilder.appendElement(value);
+    }
+
+    @Override
+    public void visitEnum(String name, String desc, String value) {
+      if (trace) { System.out.printf("visitEnum(%s, %s, %s) in %s (%s)%n", name, desc, value, this, this.getClass()); }
+      prepareForElement(new EnumAFT(classDescToName(desc)));
+      assert arrayBuilder != null;
+      arrayBuilder.appendElement(value);
+    }
+
+    @Override
+    public AnnotationVisitor visitArray(String name) {
+      throw new AssertionError("Multidimensional array in annotation!");
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+      if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", name, desc, this, this.getClass()); }
+      // The NASR will regurgitate the name we pass here when it calls
+      // supplySubannotation.  Since we ignore the name there, it doesn't
+      // matter what name we pass here.
+      return new NestedAnnotationSceneReader(this, name, desc);
+    }
+
+    @Override
+    public void visitEnd() {
+      if (trace) { System.out.printf("visitEnd on %s (%s)%n", this, this.getClass()); }
+      if (arrayBuilder != null) {
+        arrayBuilder.finish();
+      } else {
+        // This was a zero-element array
+        parent.annotationBuilder.addEmptyArrayField(arrayName);
+      }
+    }
+
+    @Override
+    void supplySubannotation(String fieldName, Annotation annotation) {
+      prepareForElement(new AnnotationAFT(annotation.def()));
+      assert arrayBuilder != null;
+      arrayBuilder.appendElement(annotation);
+    }
+  }
+
+  /*
+   * A FieldAnnotationSceneReader is a FieldVisitor that only cares about
+   * visiting [extended]annotations.  Attributes are ignored and visitEnd() has
+   * no effect.  An AnnotationSceneReader is returned for declaration and type
+   * AnnotationVisitors.  The AnnotationSceneReaders have a reference to
+   * an ATypeElement that this is visiting, and they will write out
+   * all the information to that ATypeElement after visiting each annotation.
+   */
+  private class FieldAnnotationSceneReader extends EmptyVisitor implements FieldVisitor {
+
+    /*
+    private final String name;
+    private final String desc;
+    private final String signature;
+    private final Object value;
+    */
+    private final AElement aField;
+
+    public FieldAnnotationSceneReader(
+        String name,
+        String desc,
+        String signature,
+        Object value,
+        AElement aField) {
+      /*
+      this.name = name;
+      this.desc = desc;
+      this.signature = signature;
+      this.value = value;
+      */
+      this.aField = aField;
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", desc, visible, this, this.getClass()); }
+      return new AnnotationSceneReader(desc, visible, aField);
+    }
+
+    @Override
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
+      if (trace) { System.out.printf("visitTypeAnnotation(%s, %s, %s); aField=%s, aField.type=%s in %s (%s)%n", desc, visible, inCode, aField, aField.type, this, this.getClass()); }
+      return new AnnotationSceneReader(desc, visible, aField.type);
+    }
+  }
+
+  /*
+   * Similarly to FieldAnnotationSceneReader, this is a MethodVisitor that
+   * only cares about visiting [extended]annotations.  Attributes other than
+   * BootstrapMethods are ignored, all code is ignored, and visitEnd() has no
+   * effect.  An AnnotationSceneReader
+   * is returned for declaration and type AnnotationVisitors.  The
+   * AnnotationSceneReaders have a reference to an AMethod that this is
+   * visiting, and they will write out all the information to that
+   * AMethod after visiting each annotation.
+   */
+  private class MethodAnnotationSceneReader extends EmptyVisitor implements MethodVisitor {
+
+    // private final String name;
+    // private final String desc;
+    // private final String signature;
+    private final AElement aMethod;
+
+    public MethodAnnotationSceneReader(String name, String desc, String signature, AElement aMethod) {
+      // this.name = name;
+      // this.desc = desc;
+      // this.signature = signature;
+      this.aMethod = aMethod;
+    }
+
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", desc, visible, this, this.getClass()); }
+      return visitTypeAnnotation(desc, visible, false);
+    }
+
+    @Override
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
+      if (trace) { System.out.printf("visitTypeAnnotation(%s, %s) method=%s in %s (%s)%n", desc, visible, inCode, aMethod, this, this.getClass()); }
+      return new AnnotationSceneReader(desc, visible, aMethod);
+    }
+
+    @Override
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+      if (trace) { System.out.printf("visitParameterAnnotation(%s, %s, %s) in %s (%s)%n", parameter, desc, visible, this, this.getClass()); }
+      return new AnnotationSceneReader(desc, visible,
+              ((AMethod) aMethod).parameters.vivify(parameter));
+    }
+
+    @Override
+    public void visitLocalVariable(String name, String desc, String signature,
+        Label start, Label end, int index) {
+      // TODO!
+    }
+
+    // TODO: visit code!
+  }
+
+  public static void printClasspath() {
+    System.out.println("\nClasspath:");
+    StringTokenizer tokenizer =
+        new StringTokenizer(System.getProperty("java.class.path"),
+            File.pathSeparator);
+    while (tokenizer.hasMoreTokens()) {
+      System.out.println("  " + tokenizer.nextToken());
+    }
+  }
+}
diff --git a/scene-lib/src/annotations/io/classfile/ClassAnnotationSceneWriter.java b/scene-lib/src/annotations/io/classfile/ClassAnnotationSceneWriter.java
new file mode 100644
index 0000000..fe87095
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/ClassAnnotationSceneWriter.java
@@ -0,0 +1,1445 @@
+// This class is a complete ClassVisitor with many hidden classes that do
+// the work of parsing an AScene and inserting them into a class file, as
+// the original class file is being read.
+
+package annotations.io.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+import java.lang.annotation.RetentionPolicy;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+import com.sun.tools.javac.code.TargetType;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+import annotations.*;
+import annotations.el.*;
+import annotations.field.*;
+
+/**
+ * A ClassAnnotationSceneWriter is a {@link org.objectweb.asm.ClassVisitor}
+ * that can be used to write a class file that is the combination of an
+ * existing class file and annotations in an {@link AScene}.  The "write"
+ * in <code> ClassAnnotationSceneWriter </code> refers to a class file
+ * being rewritten with information from a scene.  Also see {@link
+ * ClassAnnotationSceneReader}.
+ *
+ * <p>
+ *
+ * The proper usage of this class is to construct a
+ * <code>ClassAnnotationSceneWriter</code> with a {@link AScene} that
+ * already contains all its annotations, pass this as a {@link
+ * org.objectweb.asm.ClassVisitor} to {@link
+ * org.objectweb.asm.ClassReader#accept}, and then obtain the resulting
+ * class, ready to be written to a file, with {@link #toByteArray}.  </p>
+ *
+ * <p>
+ *
+ * All other methods are intended to be called only by
+ * {@link org.objectweb.asm.ClassReader#accept},
+ * and should not be called anywhere else, due to the order in which
+ * {@link org.objectweb.asm.ClassVisitor} methods should be called.
+ *
+ * <p>
+ *
+ * Throughout this class, "scene" refers to the {@link AScene} this class is
+ * merging into a class file.
+ */
+public class ClassAnnotationSceneWriter extends ClassAdapter {
+
+  // Strategy for interleaving the necessary calls to visit annotations
+  // from scene into the parsing done by ClassReader
+  //  (the difficulty is that the entire call sequence to every data structure
+  //   to visit annotations is in ClassReader, which should not be modified
+  //   by this library):
+  //
+  // A ClassAnnotationSceneWriter is a ClassAdapter around a ClassWriter.
+  //  - To visit the class' annotations in the scene, right before the code for
+  //     ClassWriter.visit{InnerClass, Field, Method, End} is called,
+  //     ensure that all extended annotations in the scene are visited once.
+  //  - To visit every field's annotations,
+  //     ClassAnnotationSceneWriter.visitField() returns a
+  //     FieldAnnotationSceneWriter that in a similar fashion makes sure
+  //     that each of that field's annotations is visited once on the call
+  //     to visitEnd();
+  //  - To visit every method's annotations,
+  //     ClassAnnotationSceneWriter.visitMethod() returns a
+  //     MethodAnnotationSceneWriter that visits all of that method's
+  //     annotations in the scene at the first call of visit{Code, End}.
+  //
+
+  // Whether to output error messages for unsupported cases
+  private static final boolean strict = false;
+
+  // None of these classes fields should be null, except for aClass, which
+  //  can't be vivified until the first visit() is called.
+
+  /**
+   * The scene from which to get additional annotations.
+   */
+  private final AScene scene;
+
+  /**
+   * The representation of this class in the scene.
+   */
+  private AClass aClass;
+
+  /**
+   * A list of annotations on this class that this has already visited
+   *  in the class file.
+   */
+  private final List<String> existingClassAnnotations;
+
+  /**
+   * Whether or not this has visited the corresponding annotations in scene.
+   */
+  private boolean hasVisitedClassAnnotationsInScene;
+
+  /**
+   * Whether or not to overwrite existing annotations on the same element
+   *  in a class file if a similar annotation is found in scene.
+   */
+  private final boolean overwrite;
+
+  private final Map<String, Set<Integer>> dynamicConstructors;
+  private final Map<String, Set<Integer>> lambdaExpressions;
+
+  private ClassReader cr = null;
+
+  /**
+   * Constructs a new <code> ClassAnnotationSceneWriter </code> that will
+   * insert all the annotations in <code> scene </code> into the class that
+   * it visits.  <code> scene </code> must be an {@link AScene} over the
+   * class that this will visit.
+   *
+   * @param cr the reader for the class being modified
+   * @param scene the annotation scene containing annotations to be inserted
+   * into the class this visits
+   */
+  public ClassAnnotationSceneWriter(ClassReader cr, AScene scene, boolean overwrite) {
+    super(new ClassWriter(cr, false));
+    this.scene = scene;
+    this.hasVisitedClassAnnotationsInScene = false;
+    this.aClass = null;
+    this.existingClassAnnotations = new ArrayList<String>();
+    this.overwrite = overwrite;
+    this.dynamicConstructors = new HashMap<String, Set<Integer>>();
+    this.lambdaExpressions = new HashMap<String, Set<Integer>>();
+    this.cr = cr;
+  }
+
+  /**
+   * Returns a byte array that represents the resulting class file
+   * from merging all the annotations in the scene into the class file
+   * this has visited.  This method may only be called once this has already
+   * completely visited a class, which is done by calling
+   * {@link org.objectweb.asm.ClassReader#accept}.
+   *
+   * @return a byte array of the merged class file
+   */
+  public byte[] toByteArray() {
+    return ((ClassWriter) cv).toByteArray();
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.ClassAdapter#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
+   */
+  @Override
+  public void visit(int version, int access, String name,
+      String signature, String superName, String[] interfaces) {
+    cr.accept(new MethodCodeIndexer(), false);
+    super.visit(version, access, name, signature, superName, interfaces);
+    // class files store fully quantified class names with '/' instead of '.'
+    name = name.replace('/', '.');
+    aClass = scene.classes.vivify(name);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.ClassAdapter#visitInnerClass(java.lang.String, java.lang.String, java.lang.String, int)
+   */
+  @Override
+  public void visitInnerClass(String name, String outerName, String innerName, int access ) {
+    ensureVisitSceneClassAnnotations();
+    super.visitInnerClass(name, outerName, innerName, access);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.ClassAdapter#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
+   */
+  @Override
+  public FieldVisitor visitField(int access, String name, String desc,
+      String signature, Object value) {
+    ensureVisitSceneClassAnnotations();
+    // FieldAnnotationSceneWriter ensures that the field visits all
+    //  its annotations in the scene.
+    return new FieldAnnotationSceneWriter(name,
+        super.visitField(access, name, desc, signature, value));
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.ClassAdapter#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
+   */
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+    ensureVisitSceneClassAnnotations();
+    // MethodAnnotationSceneWriter ensures that the method visits all
+    //  its annotations in the scene.
+    // MethodAdapter is used here only for getting around an unsound
+    //  "optimization" in ClassReader.
+    return new MethodAdapter(new MethodAnnotationSceneWriter(name, desc,
+            super.visitMethod(access, name, desc, signature, exceptions)));
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.ClassAdapter#visitEnd()
+   */
+  @Override
+  public void visitEnd() {
+    ensureVisitSceneClassAnnotations();
+    super.visitEnd();
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.ClassAdapter#visitAnnotation(java.lang.String, boolean)
+   */
+  @Override
+  public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+    existingClassAnnotations.add(desc);
+    // If annotation exists in scene, and in overwrite mode,
+    //  return empty visitor, since annotation from scene will be visited later.
+    if (aClass.lookup(classDescToName(desc)) != null
+        && overwrite) {
+      return new EmptyVisitor();
+    }
+    return super.visitAnnotation(desc, visible);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.ClassAdapter#visitTypeAnnotation(java.lang.String, boolean, boolean)
+   */
+  @Override
+  public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
+    existingClassAnnotations.add(desc);
+    // If annotation exists in scene, and in overwrite mode,
+    //  return empty visitor, annotation from scene will be visited later.
+    if (aClass.lookup(classDescToName(desc)) != null
+       && overwrite) {
+      return new EmptyVisitor();
+    }
+    return new SafeTypeAnnotationVisitor(
+        super.visitTypeAnnotation(desc, visible, inCode));
+  }
+
+  /**
+   * Have this class visit the annotations in scene if and only if it has not
+   * already visited them.
+   */
+  private void ensureVisitSceneClassAnnotations() {
+    if (!hasVisitedClassAnnotationsInScene) {
+      hasVisitedClassAnnotationsInScene = true;
+      for (Annotation tla : aClass.tlAnnotationsHere) {
+        // If not in overwrite mode and annotation already exists in classfile,
+        //  ignore tla.
+        if ((!overwrite) && existingClassAnnotations.contains(name(tla))) {
+          continue;
+        }
+
+        AnnotationVisitor av = visitAnnotation(tla);
+        visitFields(av, tla);
+        av.visitEnd();
+      }
+
+      // do type parameter bound annotations
+      for (Map.Entry<BoundLocation, ATypeElement> e :
+        aClass.bounds.entrySet()) {
+        BoundLocation bloc = e.getKey();
+        ATypeElement bound = e.getValue();
+
+        for (Annotation tla : bound.tlAnnotationsHere) {
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla);
+
+          if (bloc.boundIndex == -1) {
+            visitTargetType(xav, TargetType.CLASS_TYPE_PARAMETER);
+            visitBound(xav, bloc);
+          } else {
+            visitTargetType(xav, TargetType.CLASS_TYPE_PARAMETER_BOUND);
+            visitBound(xav, bloc);
+          }
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e2 :
+          bound.innerTypes.entrySet()) {
+          InnerTypeLocation itloc = e2.getKey();
+          ATypeElement innerType = e2.getValue();
+
+          for (Annotation tla : innerType.tlAnnotationsHere) {
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla);
+
+            visitTargetType(xav, TargetType.CLASS_TYPE_PARAMETER_BOUND);
+            visitBound(xav, bloc);
+            visitLocations(xav, itloc);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+
+      for (Map.Entry<TypeIndexLocation, ATypeElement> e : aClass.extendsImplements.entrySet()) {
+        TypeIndexLocation idx = e.getKey();
+        ATypeElement aty = e.getValue();
+
+        // TODO: How is this annotation written back out?
+        if (strict) { System.err.println("ClassAnnotationSceneWriter: ignoring Extends/Implements annotation " + idx + " with type: " + aty); }
+      }
+
+    }
+  }
+
+  /**
+   * The following methods are utility methods for accessing
+   * information useful to asm from scene-library data structures.
+   *
+   * @return true iff tla is visible at runtime
+   */
+  private static boolean isRuntimeRetention(Annotation tla) {
+    if (tla.def.retention() == null) {
+      return false; // TODO: temporary
+    }
+    return tla.def.retention().equals(RetentionPolicy.RUNTIME);
+  }
+
+  /**
+   * Returns the name of the annotation in the top level.
+   */
+  private static String name(Annotation tla) {
+    return tla.def().name;
+  }
+
+  /**
+   * Wraps the given class name in a class descriptor.
+   */
+  private static String classNameToDesc(String name) {
+    return "L" + name.replace('.', '/') + ";";
+  }
+
+  /**
+   * Unwraps the class name from the given class descriptor.
+   */
+  private static String classDescToName(String desc) {
+    return desc.substring(1, desc.length() - 1).replace('/', '.');
+  }
+
+  /**
+   * Returns an AnnotationVisitor over the given top-level annotation.
+   */
+  private AnnotationVisitor visitAnnotation(Annotation tla) {
+    return super.visitAnnotation(classNameToDesc(name(tla)), isRuntimeRetention(tla));
+  }
+
+  /**
+   * Returns an TypeAnnotationVisitor over the given top-level annotation.
+   */
+  private TypeAnnotationVisitor visitTypeAnnotation(Annotation tla) {
+    return super.visitTypeAnnotation(classNameToDesc(name(tla)), isRuntimeRetention(tla), false);
+  }
+
+  /**
+   * Has tav visit the fields in the given annotation.
+   */
+  private void visitFields(TypeAnnotationVisitor tav, Annotation a) {
+    tav.visitXNameAndArgsSize();
+    visitFields((AnnotationVisitor) tav, a);
+  }
+
+  /**
+   * Has av visit the fields in the given annotation.
+   * This method is necessary even with
+   * visitFields(AnnotationVisitor, Annotation)
+   * because a Annotation cannot be created from the Annotation
+   * specified to be available from the Annotation object for subannotations.
+   */
+  private void visitFields(AnnotationVisitor av, Annotation a) {
+    for (String fieldName : a.def().fieldTypes.keySet()) {
+      Object value = a.getFieldValue(fieldName);
+      if (value == null) {
+          // hopefully a field with a default value
+          continue;
+      }
+      AnnotationFieldType aft = a.def().fieldTypes.get(fieldName);
+      if (value instanceof Annotation) {
+        AnnotationVisitor nav = av.visitAnnotation(fieldName, classDescToName(a.def().name));
+        visitFields(nav, (Annotation) a);
+        nav.visitEnd();
+      } else if (value instanceof List) {
+        // In order to visit an array, the AnnotationVisitor returned by
+        // visitArray needs to visit each element, and by specification
+        // the name should be null for each element.
+        AnnotationVisitor aav = av.visitArray(fieldName);
+        aft = ((ArrayAFT) aft).elementType;
+        for (Object o : (List<?>)value) {
+          if (aft instanceof EnumAFT) {
+            aav.visitEnum(null, ((EnumAFT) aft).typeName, o.toString());
+          } else {
+            aav.visit(null, o);
+          }
+        }
+        aav.visitEnd();
+      } else if (aft instanceof EnumAFT) {
+        av.visitEnum(fieldName, ((EnumAFT) aft).typeName, value.toString());
+      } else if (aft instanceof ClassTokenAFT) {
+        av.visit(fieldName, org.objectweb.asm.Type.getType((Class<?>)value));
+      } else {
+        // everything else is a string
+        av.visit(fieldName, value);
+      }
+    }
+  }
+
+  /**
+   * Has xav visit the given target type.
+   */
+  private void visitTargetType(TypeAnnotationVisitor xav, TargetType t) {
+    xav.visitXTargetType(t.targetTypeValue());
+  }
+
+  /**
+   * Have xav visit the location length  and all locations in loc.
+   */
+  private void visitLocations(TypeAnnotationVisitor xav, InnerTypeLocation loc) {
+    List<TypePathEntry> location = loc.location;
+    xav.visitXLocationLength(location.size());
+    for (TypePathEntry l : location) {
+      xav.visitXLocation(l);
+    }
+  }
+
+  /**
+   * Has xav visit the local varialbe information in loc.
+   */
+  private void visitLocalVar(TypeAnnotationVisitor xav, LocalLocation loc) {
+    xav.visitXNumEntries(1);
+    xav.visitXStartPc(loc.scopeStart);
+    xav.visitXLength(loc.scopeLength);
+    xav.visitXIndex(loc.index);
+  }
+
+  /**
+   * Has xav visit the offset.
+   */
+  private void visitOffset(TypeAnnotationVisitor xav, int offset) {
+    xav.visitXOffset(offset);
+  }
+
+  private void visitParameterIndex(TypeAnnotationVisitor xav, int index) {
+    xav.visitXParamIndex(index);
+  }
+
+  private void visitTypeIndex(TypeAnnotationVisitor xav, int index) {
+    xav.visitXTypeIndex(index);
+  }
+
+  /**
+   * Has xav visit the type parameter bound information in loc.
+   */
+  private void visitBound(TypeAnnotationVisitor xav, BoundLocation loc) {
+    xav.visitXParamIndex(loc.paramIndex);
+    if (loc.boundIndex != -1) {
+      xav.visitXBoundIndex(loc.boundIndex);
+    }
+  }
+
+  /**
+   * A FieldAnnotationSceneWriter is a wrapper class around a FieldVisitor that
+   * delegates all calls to its internal FieldVisitor, and on a call to
+   * visitEnd(), also has its internal FieldVisitor visit all the
+   * corresponding field annotations in scene.
+   */
+  private class FieldAnnotationSceneWriter implements FieldVisitor {
+    // After being constructed, none of these fields should be null.
+
+    /**
+     * Internal FieldVisitor all calls are delegated to.
+     */
+    private final FieldVisitor fv;
+
+    /**
+     * List of all annotations this has already visited.
+     */
+    private final List<String> existingFieldAnnotations;
+
+    /**
+     * The AElement that represents this field in the AScene the
+     *   class is visiting.
+     */
+    private final AElement aField;
+
+    /**
+     * Constructs a new FieldAnnotationSceneWriter with the given name that
+     * wraps the given FieldVisitor.
+     */
+    public FieldAnnotationSceneWriter(String name, FieldVisitor fv) {
+      this.fv = fv;
+      this.existingFieldAnnotations = new ArrayList<String>();
+      this.aField = aClass.fields.vivify(name);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.objectweb.asm.FieldVisitor#visitAnnotation(java.lang.String, boolean)
+     */
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      existingFieldAnnotations.add(desc);
+
+      // If annotation exists in scene, and in overwrite mode,
+      //  return empty visitor, annotation from scene will be visited later.
+      if (aField.lookup(classDescToName(desc)) != null
+          && overwrite)
+        return new EmptyVisitor();
+
+      return fv.visitAnnotation(desc, visible);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.objectweb.asm.FieldVisitor#visitTypeAnnotation(java.lang.String, boolean)
+     */
+    @Override
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
+      existingFieldAnnotations.add(desc);
+
+      // If annotation exists in scene, and in overwrite mode,
+      //  return empty visitor, annotation from scene will be visited later.
+      if (aField.lookup(classDescToName(desc)) != null
+         && overwrite)
+        return new EmptyVisitor();
+
+      return new SafeTypeAnnotationVisitor(
+          fv.visitTypeAnnotation(desc, visible, inCode));
+    }
+
+    /** {@inheritDoc}
+     * @see org.objectweb.asm.FieldVisitor#visitAttribute(org.objectweb.asm.Attribute)
+     */
+    @Override
+    public void visitAttribute(Attribute attr) {
+      fv.visitAttribute(attr);
+    }
+
+    /**
+     * Tells this to visit the end of the field in the class file,
+     * and also ensures that this visits all its annotations in the scene.
+     *
+     * @see org.objectweb.asm.FieldVisitor#visitEnd()
+     */
+    @Override
+    public void visitEnd() {
+      ensureVisitSceneFieldAnnotations();
+      fv.visitEnd();
+    }
+
+    /**
+     * Has this visit the annotations on the corresponding field in scene.
+     */
+    private void ensureVisitSceneFieldAnnotations() {
+      // First do declaration annotations on a field.
+      for (Annotation tla : aField.tlAnnotationsHere) {
+        if ((!overwrite) && existingFieldAnnotations.contains(name(tla))) {
+          continue;
+        }
+        AnnotationVisitor av = fv.visitAnnotation(classNameToDesc(name(tla)), isRuntimeRetention(tla));
+        visitFields(av, tla);
+        av.visitEnd();
+      }
+
+      // Then do the type annotations on the field
+      for (Annotation tla : aField.type.tlAnnotationsHere) {
+        if ((!overwrite) && existingFieldAnnotations.contains(name(tla))) {
+          continue;
+        }
+        TypeAnnotationVisitor av = fv.visitTypeAnnotation(classNameToDesc(name(tla)), isRuntimeRetention(tla), false);
+        visitTargetType(av, TargetType.FIELD);
+        visitLocations(av, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+        visitFields(av, tla);
+        av.visitEnd();
+      }
+
+      // Now do field generics/arrays.
+      for (Map.Entry<InnerTypeLocation, ATypeElement> fieldInnerEntry :
+        aField.type.innerTypes.entrySet()) {
+
+        for (Annotation tla : fieldInnerEntry.getValue().tlAnnotationsHere) {
+          if ((!overwrite) && existingFieldAnnotations.contains(name(tla))) {
+            continue;
+          }
+          TypeAnnotationVisitor xav =
+            fv.visitTypeAnnotation(classNameToDesc(name(tla)), isRuntimeRetention(tla), false);
+          visitTargetType(xav, TargetType.FIELD);
+          visitLocations(xav, fieldInnerEntry.getKey());
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+      }
+    }
+  }
+
+  /**
+   * A MethodAnnotationSceneWriter is to a MethodAdapter exactly
+   * what ClassAnnotationSceneWriter is to a ClassAdapter:
+   * it will ensure that the MethodVisitor behind MethodAdapter
+   * visits each of the extended annotations in scene in the correct
+   * sequence, before any of the later data is visited.
+   */
+  private class MethodAnnotationSceneWriter extends MethodAdapter {
+    // basic strategy:
+    // ensureMethodVisitSceneAnnotation will be called, if it has not already
+    // been called, at the beginning of visitCode, visitEnd
+
+    /**
+     * The AMethod that represents this method in scene.
+     */
+    private final AMethod aMethod;
+
+    /**
+     * Whether or not this has visit the method's annotations in scene.
+     */
+    private boolean hasVisitedMethodAnnotations;
+
+    /**
+     * The existing annotations this method has visited.
+     */
+    private final List<String> existingMethodAnnotations;
+
+    /**
+     * Constructs a new MethodAnnotationSceneWriter with the given name and
+     * description that wraps around the given MethodVisitor.
+     *
+     * @param name the name of the method, as in "foo"
+     * @param desc the method signature minus the name,
+     *  as in "(Ljava/lang/String)V"
+     * @param mv the method visitor to wrap around
+     */
+    MethodAnnotationSceneWriter(String name, String desc, MethodVisitor mv) {
+      super(mv);
+      this.hasVisitedMethodAnnotations = false;
+      this.aMethod = aClass.methods.vivify(name+desc);
+      this.existingMethodAnnotations = new ArrayList<String>();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.objectweb.asm.MethodAdapter#visitCode()
+     */
+    @Override
+    public void visitCode() {
+      ensureVisitSceneMethodAnnotations();
+      super.visitCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.objectweb.asm.MethodAdapter#visitEnd()
+     */
+    @Override
+    public void visitEnd() {
+      ensureVisitSceneMethodAnnotations();
+      super.visitEnd();
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.objectweb.asm.MethodAdapter#visitAnnotation(java.lang.String, boolean)
+     */
+    @Override
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      existingMethodAnnotations.add(desc);
+      // If annotation exists in scene, and in overwrite mode,
+      //  return empty visitor, annotation from scene will be visited later.
+      if (shouldSkipExisting(classDescToName(desc))) {
+        return new EmptyVisitor();
+      }
+
+      return super.visitAnnotation(desc, visible);
+    }
+
+    /**
+     * {@inheritDoc}
+     * @see org.objectweb.asm.MethodAdapter#visitTypeAnnotation(java.lang.String, boolean)
+     */
+    @Override
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
+
+      existingMethodAnnotations.add(desc);
+
+      // If annotation exists in scene, and in overwrite mode,
+      //  return empty visitor, annotation from scene will be visited later.
+      if (shouldSkipExisting(classDescToName(desc))) {
+        return new EmptyVisitor();
+      }
+
+      return new SafeTypeAnnotationVisitor(
+          super.visitTypeAnnotation(desc, visible, inCode));
+    }
+
+    /**
+     * Returns true iff the annotation in tla should not be written because it
+     *  already exists in this method's annotations.
+     */
+    private boolean shouldSkip(Annotation tla) {
+      return ((!overwrite) && existingMethodAnnotations.contains(name(tla)));
+    }
+
+    /**
+     * Returns true iff the annotation with the given name should not be written
+     * because it already exists in this method's annotations.
+     */
+    private boolean shouldSkipExisting(String name) {
+      return ((!overwrite)
+              && aMethod.lookup(name) != null);
+    }
+
+    /**
+     * Has this visit the annotation in tla, and returns the resulting visitor.
+     */
+    private AnnotationVisitor visitAnnotation(Annotation tla) {
+      return super.visitAnnotation(classNameToDesc(name(tla)), isRuntimeRetention(tla));
+    }
+
+    /**
+     * Has this visit the extended annotation in tla and returns the
+     * resulting visitor.
+     */
+    private TypeAnnotationVisitor
+    visitTypeAnnotation(Annotation tla, boolean inCode) {
+      return super.visitTypeAnnotation(classNameToDesc(name(tla)), isRuntimeRetention(tla), inCode);
+    }
+
+    /**
+     * Has this visit the parameter annotation in tla and returns the
+     * resulting visitor.
+     */
+    private AnnotationVisitor visitParameterAnnotation(Annotation tla, int index) {
+      return super.visitParameterAnnotation(index, classNameToDesc(name(tla)), isRuntimeRetention(tla));
+    }
+
+    /**
+     * Has this visit the declaration annotation and the type annotations on the return type.
+     */
+    private void ensureVisitMethodDeclarationAnnotations() {
+      // Annotations on method declaration.
+      for (Annotation tla : aMethod.tlAnnotationsHere) {
+        if (shouldSkip(tla)) {
+          continue;
+        }
+
+        AnnotationVisitor av = visitAnnotation(tla);
+        visitFields(av, tla);
+        av.visitEnd();
+      }
+
+    }
+
+    /**
+     * Has this visit the declaration annotations and the type annotations on the return type.
+     */
+    private void ensureVisitReturnTypeAnnotations() {
+      // Standard annotations on return type.
+      for (Annotation tla : aMethod.returnType.tlAnnotationsHere) {
+        if (shouldSkip(tla)) {
+          continue;
+        }
+
+        TypeAnnotationVisitor av = visitTypeAnnotation(tla, false);
+        visitTargetType(av, TargetType.METHOD_RETURN);
+        visitLocations(av, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+        visitFields(av, tla);
+        av.visitEnd();
+      }
+
+      // Now do generic/array information on return type
+      for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+        aMethod.returnType.innerTypes.entrySet()) {
+        InnerTypeLocation loc = e.getKey();
+        ATypeElement innerType = e.getValue();
+
+        for (Annotation tla : innerType.tlAnnotationsHere) {
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+
+          visitTargetType(xav, TargetType.METHOD_RETURN);
+          // information for raw type (return type)
+          //  (none)
+          // information for generic/array (on return type)
+          visitLocations(xav, loc);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+      }
+
+    }
+
+    /**
+     * Has this visit the annotations on type parameter bounds.
+     */
+    private void ensureVisitTypeParameterBoundAnnotations() {
+      for (Map.Entry<BoundLocation, ATypeElement> e :
+        aMethod.bounds.entrySet()) {
+        BoundLocation bloc = e.getKey();
+        ATypeElement bound = e.getValue();
+
+        for (Annotation tla : bound.tlAnnotationsHere) {
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+
+          if (bloc.boundIndex == -1) {
+            visitTargetType(xav, TargetType.METHOD_TYPE_PARAMETER);
+            visitBound(xav, bloc);
+          } else {
+            visitTargetType(xav, TargetType.METHOD_TYPE_PARAMETER_BOUND);
+            visitBound(xav, bloc);
+          }
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e2 :
+          bound.innerTypes.entrySet()) {
+          InnerTypeLocation itloc = e2.getKey();
+          ATypeElement innerType = e2.getValue();
+
+          for (Annotation tla : innerType.tlAnnotationsHere) {
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+
+            visitTargetType(xav, TargetType.METHOD_TYPE_PARAMETER_BOUND);
+            visitBound(xav, bloc);
+            visitLocations(xav, itloc);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+    }
+
+    /**
+     * Has this visit the annotations on local variables in this method.
+     */
+    private void ensureVisitLocalVariablesAnnotations() {
+      for (Map.Entry<LocalLocation, AField> entry :
+          aMethod.body.locals.entrySet()) {
+        LocalLocation localLocation = entry.getKey();
+        AElement aLocation = entry.getValue();
+
+        for (Annotation tla : aLocation.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+          visitTargetType(xav, TargetType.LOCAL_VARIABLE);
+          visitLocalVar(xav, localLocation);
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        // now do annotations on inner type of aLocation (local variable)
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+          aLocation.type.innerTypes.entrySet()) {
+          InnerTypeLocation localVariableLocation = e.getKey();
+          ATypeElement aInnerType = e.getValue();
+          for (Annotation tla : aInnerType.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+            visitTargetType(xav, TargetType.LOCAL_VARIABLE);
+            // information for raw type (local variable)
+            visitLocalVar(xav, localLocation);
+            // information for generic/array (on local variable)
+            visitLocations(xav, localVariableLocation);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+
+        }
+      }
+    }
+
+    /**
+     * Has this visit the object creation (new) annotations on this method.
+     */
+    private void ensureVisitObjectCreationAnnotations() {
+      for (Map.Entry<RelativeLocation, ATypeElement> entry :
+          aMethod.body.news.entrySet()) {
+        if (!entry.getKey().isBytecodeOffset()) {
+          // if the RelativeLocation is a source index, we cannot insert it
+          // into bytecode
+          // TODO: output a warning or translate
+          if (strict) { System.err.println("ClassAnnotationSceneWriter.ensureVisitObjectCreationAnnotation: no bytecode offset found!"); }
+        }
+        int offset = entry.getKey().offset;
+        ATypeElement aNew = entry.getValue();
+        for (Annotation tla : aNew.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+          visitTargetType(xav, TargetType.NEW);
+          visitOffset(xav, offset);
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        // now do inner annotations on aNew (object creation)
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+          aNew.innerTypes.entrySet()) {
+          InnerTypeLocation aNewLocation = e.getKey();
+          ATypeElement aInnerType = e.getValue();
+          for (Annotation tla : aInnerType.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+            visitTargetType(xav, TargetType.NEW);
+            // information for raw type (object creation)
+            visitOffset(xav, offset);
+            // information for generic/array (on object creation)
+            visitLocations(xav, aNewLocation);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+    }
+
+    /**
+     * Has this visit the parameter annotations on this method.
+     */
+    private void ensureVisitParameterAnnotations() {
+      for (Map.Entry<Integer, AField> entry :
+          aMethod.parameters.entrySet()) {
+        AField aParameter = entry.getValue();
+        int index = entry.getKey();
+        // First visit declaration annotations on the parameter
+        for (Annotation tla : aParameter.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          AnnotationVisitor av = visitParameterAnnotation(tla, index);
+          visitFields(av, tla);
+          av.visitEnd();
+        }
+
+        // Then handle type annotations targeting the parameter
+        for (Annotation tla : aParameter.type.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor av = visitTypeAnnotation(tla, false);
+            visitTargetType(av, TargetType.METHOD_FORMAL_PARAMETER);
+            visitParameterIndex(av, index);
+            visitLocations(av, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+            visitFields(av, tla);
+            av.visitEnd();
+        }
+
+        // now handle inner annotations on aParameter (parameter)
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+          aParameter.type.innerTypes.entrySet()) {
+          InnerTypeLocation aParameterLocation = e.getKey();
+          ATypeElement aInnerType = e.getValue();
+          for (Annotation tla : aInnerType.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+            visitTargetType(xav,
+                TargetType.METHOD_FORMAL_PARAMETER);
+            // information for raw type (parameter)
+            //  (none)
+            // information for generic/array (on parameter)
+            visitParameterIndex(xav, index);
+            visitLocations(xav, aParameterLocation);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+    }
+
+    /**
+     * Has this visit the receiver annotations on this method.
+     */
+    private void ensureVisitReceiverAnnotations() {
+      AField aReceiver = aMethod.receiver;
+
+      // for (Annotation tla : aReceiver.tlAnnotationsHere) {
+      //  if (shouldSkip(tla)) continue;
+      //
+      //  AnnotationVisitor av = visitTypeAnnotation(tla, false);  // FIXME
+      //  visitTargetType(av, TargetType.METHOD_RECEIVER);
+      //  visitLocations(av, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+      //  visitFields(av, tla);
+      //  av.visitEnd();
+      // }
+
+      for (Annotation tla : aReceiver.type.tlAnnotationsHere) {
+        if (shouldSkip(tla)) {
+          continue;
+        }
+
+        TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+        visitTargetType(xav, TargetType.METHOD_RECEIVER);
+        visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+        visitFields(xav, tla);
+        xav.visitEnd();
+      }
+
+      // now do inner annotations of aReceiver
+      for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+          aReceiver.type.innerTypes.entrySet()) {
+        InnerTypeLocation aReceiverLocation = e.getKey();
+        ATypeElement aInnerType = e.getValue();
+        for (Annotation tla : aInnerType.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+          visitTargetType(xav, TargetType.METHOD_RECEIVER);
+          // information for generic/array (on receiver)
+          visitLocations(xav, aReceiverLocation);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+      }
+
+    }
+
+    /**
+     * Has this visit the typecast annotations on this method.
+     */
+    private void ensureVisitTypecastAnnotations() {
+      for (Map.Entry<RelativeLocation, ATypeElement> entry :
+          aMethod.body.typecasts.entrySet()) {
+        if (!entry.getKey().isBytecodeOffset()) {
+          // if the RelativeLocation is a source index, we cannot insert it
+          // into bytecode
+          // TODO: output a warning or translate
+          if (strict) { System.err.println("ClassAnnotationSceneWriter.ensureVisitTypecastAnnotation: no bytecode offset found!"); }
+        }
+        int offset = entry.getKey().offset;
+        int typeIndex = entry.getKey().type_index;
+        ATypeElement aTypecast = entry.getValue();
+        for (Annotation tla : aTypecast.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+          visitTargetType(xav, TargetType.CAST);
+          visitOffset(xav, offset);
+          visitTypeIndex(xav, typeIndex);
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        // now do inner annotations of aTypecast (typecast)
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+          aTypecast.innerTypes.entrySet()) {
+          InnerTypeLocation aTypecastLocation = e.getKey();
+          ATypeElement aInnerType = e.getValue();
+          for (Annotation tla : aInnerType.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+            visitTargetType(xav, TargetType.CAST);
+            // information for raw type (typecast)
+            visitOffset(xav, offset);
+            visitTypeIndex(xav, typeIndex);
+            // information for generic/array (on typecast)
+            visitLocations(xav, aTypecastLocation);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+    }
+
+    /**
+     * Has this visit the typetest annotations on this method.
+     */
+    private void ensureVisitTypeTestAnnotations() {
+      for (Map.Entry<RelativeLocation, ATypeElement> entry :
+        aMethod.body.instanceofs.entrySet()) {
+        if (!entry.getKey().isBytecodeOffset()) {
+          // if the RelativeLocation is a source index, we cannot insert it
+          // into bytecode
+          // TODO: output a warning or translate
+          if (strict) { System.err.println("ClassAnnotationSceneWriter.ensureVisitTypeTestAnnotation: no bytecode offset found!"); }
+        }
+        int offset = entry.getKey().offset;
+        ATypeElement aTypeTest = entry.getValue();
+        for (Annotation tla : aTypeTest.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+          visitTargetType(xav, TargetType.INSTANCEOF);
+          visitOffset(xav, offset);
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        // now do inner annotations of aTypeTest (typetest)
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+          aTypeTest.innerTypes.entrySet()) {
+          InnerTypeLocation aTypeTestLocation = e.getKey();
+          AElement aInnerType = e.getValue();
+          for (Annotation tla : aInnerType.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+            visitTargetType(xav, TargetType.INSTANCEOF);
+            // information for raw type (typetest)
+            visitOffset(xav, offset);
+            // information for generic/array (on typetest)
+            visitLocations(xav, aTypeTestLocation);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+    }
+
+    private void ensureVisitLambdaExpressionAnnotations() {
+      for (Map.Entry<RelativeLocation, AMethod> entry :
+          aMethod.body.funs.entrySet()) {
+        if (!entry.getKey().isBytecodeOffset()) {
+          // if the RelativeLocation is a source index, we cannot insert it
+          // into bytecode
+          // TODO: output a warning or translate
+          if (strict) { System.err.println("ClassAnnotationSceneWriter.ensureMemberReferenceAnnotations: no bytecode offset found!"); }
+          continue;
+        }
+        // int offset = entry.getKey().offset;
+        // int typeIndex = entry.getKey().type_index;
+        AMethod aLambda = entry.getValue();
+
+        for (Map.Entry<Integer, AField> e0 : aLambda.parameters.entrySet()) {
+          AField aParameter = e0.getValue();
+          int index = e0.getKey();
+
+          for (Annotation tla : aParameter.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            AnnotationVisitor av = visitParameterAnnotation(tla, index);
+            visitFields(av, tla);
+            av.visitEnd();
+          }
+
+          for (Annotation tla : aParameter.type.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+            visitTargetType(xav, TargetType.METHOD_FORMAL_PARAMETER);
+            // visitOffset(xav, offset);
+            // visitTypeIndex(xav, typeIndex);
+            visitParameterIndex(xav, index);
+            visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+
+          for (Map.Entry<InnerTypeLocation, ATypeElement> e1 :
+              aParameter.type.innerTypes.entrySet()) {
+            InnerTypeLocation aParameterLocation = e1.getKey();
+            ATypeElement aInnerType = e1.getValue();
+            for (Annotation tla : aInnerType.tlAnnotationsHere) {
+              if (shouldSkip(tla)) {
+                continue;
+              }
+
+              TypeAnnotationVisitor xav = visitTypeAnnotation(tla, false);
+              visitTargetType(xav, TargetType.METHOD_FORMAL_PARAMETER);
+              // visitOffset(xav, offset);
+              // visitTypeIndex(xav, typeIndex);
+              visitParameterIndex(xav, index);
+              visitLocations(xav, aParameterLocation);
+              visitFields(xav, tla);
+              xav.visitEnd();
+            }
+          }
+        }
+      }
+    }
+
+    private void ensureVisitMemberReferenceAnnotations() {
+      for (Map.Entry<RelativeLocation, ATypeElement> entry :
+          aMethod.body.refs.entrySet()) {
+        if (!entry.getKey().isBytecodeOffset()) {
+          // if the RelativeLocation is a source index, we cannot insert it
+          // into bytecode
+          // TODO: output a warning or translate
+          if (strict) { System.err.println("ClassAnnotationSceneWriter.ensureMemberReferenceAnnotations: no bytecode offset found!"); }
+          continue;
+        }
+        int offset = entry.getKey().offset;
+        int typeIndex = entry.getKey().type_index;
+        ATypeElement aTypeArg = entry.getValue();
+        Set<Integer> lset = lambdaExpressions.get(aMethod.methodName);
+        if (lset.contains(offset)) { continue; }  // something's wrong
+        Set<Integer> cset = dynamicConstructors.get(aMethod.methodName);
+        TargetType tt = cset != null && cset.contains(offset)
+                ? TargetType.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT
+                : TargetType.METHOD_REFERENCE_TYPE_ARGUMENT;
+
+        for (Annotation tla : aTypeArg.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+          visitTargetType(xav, tt);
+          visitOffset(xav, offset);
+          visitTypeIndex(xav, typeIndex);
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        // now do inner annotations of member reference
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+            aTypeArg.innerTypes.entrySet()) {
+          InnerTypeLocation aTypeArgLocation = e.getKey();
+          AElement aInnerType = e.getValue();
+          for (Annotation tla : aInnerType.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+            visitTargetType(xav, tt);
+            visitOffset(xav, offset);
+            visitTypeIndex(xav, typeIndex);
+            visitLocations(xav, aTypeArgLocation);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+    }
+
+    private void ensureVisitMethodInvocationAnnotations() {
+      for (Map.Entry<RelativeLocation, ATypeElement>
+          entry : aMethod.body.calls.entrySet()) {
+        if (!entry.getKey().isBytecodeOffset()) {
+          // if the RelativeLocation is a source index, we cannot insert it
+          // into bytecode
+          // TODO: output a warning or translate
+          if (strict) { System.err.println("ClassAnnotationSceneWriter.ensureVisitMethodInvocationAnnotations: no bytecode offset found!"); }
+        }
+        int offset = entry.getKey().offset;
+        int typeIndex = entry.getKey().type_index;
+        ATypeElement aCall = entry.getValue();
+        Set<Integer> cset = dynamicConstructors.get(aMethod.methodName);
+        TargetType tt = cset != null && cset.contains(offset)
+                ? TargetType.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT
+                : TargetType.METHOD_INVOCATION_TYPE_ARGUMENT;
+
+        for (Annotation tla : aCall.tlAnnotationsHere) {
+          if (shouldSkip(tla)) {
+            continue;
+          }
+
+          TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+          visitTargetType(xav, tt);
+          visitOffset(xav, offset);
+          visitTypeIndex(xav, typeIndex);
+          visitLocations(xav, InnerTypeLocation.EMPTY_INNER_TYPE_LOCATION);
+          visitFields(xav, tla);
+          xav.visitEnd();
+        }
+
+        // now do inner annotations of call
+        for (Map.Entry<InnerTypeLocation, ATypeElement> e :
+            aCall.innerTypes.entrySet()) {
+          InnerTypeLocation aCallLocation = e.getKey();
+          AElement aInnerType = e.getValue();
+          for (Annotation tla : aInnerType.tlAnnotationsHere) {
+            if (shouldSkip(tla)) {
+              continue;
+            }
+
+            TypeAnnotationVisitor xav = visitTypeAnnotation(tla, true);
+            visitTargetType(xav, TargetType.INSTANCEOF);
+            visitOffset(xav, offset);
+            visitTypeIndex(xav, typeIndex);
+            visitLocations(xav, aCallLocation);
+            visitFields(xav, tla);
+            xav.visitEnd();
+          }
+        }
+      }
+    }
+
+    /**
+     * Have this method visit the annotations in scene if and only if
+     *  it has not visited them before.
+     */
+    private void ensureVisitSceneMethodAnnotations() {
+      if (!hasVisitedMethodAnnotations) {
+        hasVisitedMethodAnnotations = true;
+
+        ensureVisitMethodDeclarationAnnotations();
+        ensureVisitReturnTypeAnnotations();
+
+        // Now iterate through method's locals, news, parameter, receiver,
+        // typecasts, and type argument annotations, which will all be
+        // extended annotations
+        ensureVisitTypeParameterBoundAnnotations();
+        ensureVisitLocalVariablesAnnotations();
+        ensureVisitObjectCreationAnnotations();
+        ensureVisitParameterAnnotations();
+        ensureVisitReceiverAnnotations();
+        ensureVisitTypecastAnnotations();
+        ensureVisitTypeTestAnnotations();
+        ensureVisitLambdaExpressionAnnotations();
+        ensureVisitMemberReferenceAnnotations();
+        ensureVisitMethodInvocationAnnotations();
+        // TODO: throw clauses?!
+        // TODO: catch clauses!?
+      }
+    }
+  }
+
+  class MethodCodeIndexer extends EmptyVisitor {
+    private int codeStart = 0;
+    Set<Integer> constrs;  // distinguishes constructors from methods
+    Set<Integer> lambdas;  // distinguishes lambda exprs from member refs
+
+    MethodCodeIndexer() {
+      int fieldCount;
+      // const pool size is (not lowest) upper bound of string length
+      codeStart = cr.header + 6;
+      codeStart += 2 + 2 * cr.readUnsignedShort(codeStart);
+      fieldCount = cr.readUnsignedShort(codeStart);
+      codeStart += 2;
+      while (--fieldCount >= 0) {
+        int attrCount = cr.readUnsignedShort(codeStart + 6);
+        codeStart += 8;
+        while (--attrCount >= 0) {
+          codeStart += 6 + cr.readInt(codeStart + 2);
+        }
+      }
+      codeStart += 2;
+    }
+
+    @Override
+    public void visit(int version, int access, String name, String signature,
+        String superName, String[] interfaces) {
+    }
+
+    @Override
+    public void visitSource(String source, String debug) {}
+
+    @Override
+    public void visitOuterClass(String owner, String name, String desc) {}
+
+    @Override
+    public void visitInnerClass(String name, String outerName,
+        String innerName, int access) {
+    }
+
+    @Override
+    public FieldVisitor visitField(int access, String name, String desc,
+        String signature, Object value) {
+      return null;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access,
+        String name, String desc, String signature, String[] exceptions) {
+      String methodDescription = name + desc;
+      constrs = dynamicConstructors.get(methodDescription);
+      if (constrs == null) {
+        constrs = new TreeSet<Integer>();
+        dynamicConstructors.put(methodDescription, constrs);
+      }
+      lambdas = lambdaExpressions.get(methodDescription);
+      if (lambdas == null) {
+        lambdas = new TreeSet<Integer>();
+        lambdaExpressions.put(methodDescription, lambdas);
+      }
+
+      return new MethodAdapter(
+          new MethodCodeOffsetAdapter(cr, new EmptyVisitor(), codeStart) {
+              @Override
+              public void visitInvokeDynamicInsn(String name,
+                  String desc, Handle bsm, Object... bsmArgs) {
+                String methodName = ((Handle) bsmArgs[1]).getName();
+                int off = getMethodCodeOffset();
+                if ("<init>".equals(methodName)) {
+                  constrs.add(off);
+                } else {
+                  int ix = methodName.lastIndexOf('.');
+                  if (ix >= 0) {
+                    methodName = methodName.substring(ix+1);
+                  }
+                  if (methodName.startsWith("lambda$")) {
+                    lambdas.add(off);
+                  }
+                }
+                super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+              }
+          });
+    }
+  }
+}
diff --git a/scene-lib/src/annotations/io/classfile/ClassFileReader.java b/scene-lib/src/annotations/io/classfile/ClassFileReader.java
new file mode 100644
index 0000000..03bb7a3
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/ClassFileReader.java
@@ -0,0 +1,207 @@
+package annotations.io.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.*;
+
+import plume.*;
+
+import com.sun.tools.javac.main.CommandLine;
+
+import org.objectweb.asm.ClassReader;
+
+import annotations.el.AScene;
+import annotations.io.IndexFileWriter;
+
+/**
+ * A <code> ClassFileReader </code> provides methods for reading in annotations
+ *  from a class file into an {@link annotations.el.AScene}.
+ */
+public class ClassFileReader {
+
+  public static final String INDEX_UTILS_VERSION
+    = "Annotation File Utilities v3.6.44";
+
+  @Option("-b omit annotations from bridge (compiler-created) methods")
+  public static boolean ignore_bridge_methods = false;
+
+  @Option("-h print usage information and exit")
+  public static boolean help = false;
+
+  @Option("-v print version information and exit")
+  public static boolean version = false;
+
+  private static String linesep = System.getProperty("line.separator");
+
+  static String usage
+    = "extract-annotations [options] class1 class2 ..."
+    + linesep
+    + "Each argument is a class a.b.C (that is on your classpath) or a class file"
+    + linesep
+    + "a/b/C.class.  Extracts the annotations from each such argument and prints"
+    + linesep
+    + "them in index-file format to a.b.C.jaif .  Arguments beginning with a"
+    + linesep
+    + "single '@' are interpreted as argument files to be read and expanded into"
+    + linesep
+    + "the command line.  Options:";
+
+  /**
+   * From the command line, read annotations from a class file and write
+   * them to an index file.  Also see the Anncat tool, which is more
+   * versatile (and which calls this as a subroutine).
+   * <p>
+   *
+   * For usage information, supply the -h or --help option.
+   * <p>
+   *
+   * For programmatic access to this tool, use the read() methods instead.
+   * <p>
+   *
+   * @param args options and classes to analyze;
+   * @throws IOException if a class file cannot be found
+   */
+  public static void main(String[] args) throws IOException {
+    Options options = new Options(usage, ClassFileReader.class);
+    String[] file_args;
+
+    try {
+      String[] cl_args = CommandLine.parse(args);
+      file_args = options.parse_or_usage(cl_args);
+    } catch (IOException ex) {
+      System.err.println(ex);
+      System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\".");
+      System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'.");
+      System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)");
+      file_args = null;  // Eclipse compiler issue workaround
+      System.exit(1);
+    }
+
+    if (version) {
+      System.out.printf("extract-annotations (%s)", INDEX_UTILS_VERSION);
+    }
+    if (help) {
+      options.print_usage();
+    }
+    if (version || help) {
+      System.exit(-1);
+    }
+
+    if (file_args.length == 0) {
+      options.print_usage("No arguments given.");
+      System.exit(-1);
+    }
+
+    // check args for well-formed names
+    for (String arg : file_args) {
+      if (!checkClass(arg)) {
+        System.exit(-1);
+      }
+    }
+
+    for (String origName : file_args) {
+      System.out.println("reading: " + origName);
+      String className = origName;
+      if (origName.endsWith(".class")) {
+          origName = origName.replace(".class", "");
+      }
+
+      AScene scene = new AScene();
+      try {
+        if (className.endsWith(".class")) {
+          read(scene, className);
+        } else {
+          readFromClass(scene, className);
+        }
+        String outputFile = origName + ".jaif";
+        System.out.println("printing results to : " + outputFile);
+        IndexFileWriter.write(scene, outputFile);
+      } catch (IOException e) {
+        System.out.println("There was an error in reading class: " + origName);
+        System.out.println(
+            "Did you ensure that this class is on your classpath?");
+        return;
+      } catch (Exception e) {
+        System.out.println("Unknown error trying to extract annotations from: " +
+            origName);
+        System.out.println(e.getMessage());
+        e.printStackTrace();
+        System.out.println("Please submit a bug report at");
+        System.out.println("  https://github.com/typetools/annotation-tools/issues");
+        System.out.println("Be sure to include a copy of the output trace, instructions on how");
+        System.out.println("to reproduce this error, and all input files.  Thanks!");
+        return;
+      }
+    }
+  }
+
+  /**
+   * If s is not a valid representation of a class, print a warning message
+   * and return false.
+   */
+  public static boolean checkClass(String arg) {
+    // check for invalid class file paths with '.'
+    if (!arg.contains(".class") && arg.contains("/")) {
+      System.out.println("Error: bad class " + arg);
+      System.out.println("Use a fully qualified class name such as java.lang.Object");
+      System.out.println("or a filename such as .../path/to/MyClass.class");
+      return false;
+    }
+    return true;
+  }
+
+  /**
+   * Reads the annotations from the class file <code> fileName </code>
+   * and inserts them into <code> scene </code>.
+   * <code> fileName </code> should be a file name that can be resolved from
+   * the current working directory, which means it should end in ".class"
+   * for standard Java class files.
+   *
+   * @param scene the scene into which the annotations should be inserted
+   * @param fileName the file name of the class the annotations should be
+   * read from
+   * @throws IOException if there is a problem reading from
+   * <code> fileName </code>
+   */
+  public static void read(AScene scene, String fileName)
+  throws IOException {
+    read(scene, new FileInputStream(fileName));
+  }
+
+  /**
+   * Reads the annotations from the class <code> className </code>,
+   * assumed to be in your classpath,
+   * and inserts them into <code> scene </code>.
+   *
+   * @param scene the scene into which the annotations should be inserted
+   * @param className the name of the class to read in
+   * @throws IOException if there is a problem reading <code> className </code>
+   */
+  public static void readFromClass(AScene scene, String className)
+  throws IOException {
+    read(scene, new ClassReader(className));
+  }
+
+  /**
+   * Reads the annotations from the class file <code> fileName </code>
+   * and inserts them into <code> scene </code>.
+   *
+   * @param scene the scene into which the annotations should be inserted
+   * @param in an input stream containing the class that the annotations
+   * should be read from
+   * @throws IOException if there is a problem reading from <code> in </code>
+   */
+  public static void read(AScene scene, InputStream in)
+  throws IOException {
+    read(scene, new ClassReader(in));
+  }
+
+  public static void read(AScene scene, ClassReader cr) {
+    ClassAnnotationSceneReader ca =
+        new ClassAnnotationSceneReader(cr, scene, ignore_bridge_methods);
+    cr.accept(ca, true);
+  }
+
+}
diff --git a/scene-lib/src/annotations/io/classfile/ClassFileWriter.java b/scene-lib/src/annotations/io/classfile/ClassFileWriter.java
new file mode 100644
index 0000000..9fc53c1
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/ClassFileWriter.java
@@ -0,0 +1,248 @@
+package annotations.io.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.*;
+
+import com.sun.tools.javac.main.CommandLine;
+
+import org.objectweb.asm.ClassReader;
+
+import plume.Option;
+import plume.Options;
+
+import annotations.el.AScene;
+import annotations.io.IndexFileParser;
+
+/**
+ * A <code> ClassFileWriter </code> provides methods for inserting annotations
+ *  from an {@link annotations.el.AScene} into a class file.
+ */
+public class ClassFileWriter {
+
+  @Option("-h print usage information and exit")
+  public static boolean help = false;
+
+  @Option("print version information and exit")
+  public static boolean version = false;
+
+  private static String linesep = System.getProperty("line.separator");
+
+  static String usage
+    = "usage: insert-annotations [options] class1 indexfile1 class2 indexfile2 ..."
+    + ""
+    + linesep
+    + "For each class/index file pair (a.b.C a.b.C.jaif), read annotations from"
+    + linesep
+    + "the index file a.b.C.jaif and insert them into the class a.b.C, then"
+    + linesep
+    + "output the merged class file to a.b.C.class"
+    + linesep
+    + "Each class is either a fully-qualified name of a class on your classpath,"
+    + linesep
+    + "or a path to a .class file, such as e.g. /.../path/to/a/b/C.class ."
+    + linesep
+    + "Arguments beginning with a single '@' are interpreted as argument files to"
+    + linesep
+    + "be read and expanded into the command line.  Options:";
+
+  /**
+   * Main method meant to a a convenient way to write annotations from an index
+   * file to a class file.  For programmatic access to this
+   * tool, one should probably use the insert() methods instead.
+   * <p>
+   * Usage: java annotations.io.ClassFileWriter <em>options</em> [classfile indexfile] ...
+   * <p>
+   * <em>options</em> include:<pre>
+   *   -h, --help   print usage information and exit
+   *   --version    print version information and exit
+   * </pre>
+   * @param args options and classes and index files to analyze;
+   * @throws IOException if a class file or index file cannot be opened/written
+   */
+  public static void main(String[] args) throws IOException {
+    Options options = new Options(usage, ClassFileWriter.class);
+    String[] file_args;
+
+    try {
+      String[] cl_args = CommandLine.parse(args);
+      file_args = options.parse_or_usage(cl_args);
+    } catch (IOException ex) {
+      System.err.println(ex);
+      System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\".");
+      System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'.");
+      System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)");
+      file_args = null;  // Eclipse compiler issue workaround
+      System.exit(1);
+    }
+
+    if (version) {
+      System.out.printf("insert-annotations (%s)",
+                        ClassFileReader.INDEX_UTILS_VERSION);
+    }
+    if (help) {
+      options.print_usage();
+    }
+    if (version || help) {
+      System.exit(-1);
+    }
+
+    if (file_args.length == 0) {
+      options.print_usage("No arguments given.");
+      System.exit(-1);
+    }
+    if (file_args.length % 2 == 1) {
+      options.print_usage("Must supply an even number of arguments.");
+      System.exit(-1);
+    }
+
+    // check args for well-formed names
+    for (int i = 0; i < file_args.length; i += 2) {
+      if (!ClassFileReader.checkClass(file_args[i])) {
+        System.exit(-1);
+      }
+    }
+
+    for (int i = 0; i < file_args.length; i++) {
+
+      String className = file_args[i];
+      i++;
+      if (i >= file_args.length) {
+        System.out.println("Error: incorrect number of arguments");
+        System.out.println("Run insert-annotations --help for usage information");
+        return;
+      }
+      String indexFileName = file_args[i];
+
+      AScene scene = new AScene();
+
+      IndexFileParser.parseFile(indexFileName, scene);
+
+      // annotations loaded from index file into scene, now insert them
+      // into class file
+      try {
+        if (className.endsWith(".class")) {
+          System.out.printf("Adding annotations to class file %s%n", className);
+          insert(scene, className, true);
+        } else {
+          String outputFileName = className + ".class";
+          System.out.printf("Reading class file %s; writing with annotations to %s%n",
+                            className, outputFileName);
+          insert(scene, className, outputFileName, true);
+        }
+      } catch (IOException e) {
+        System.out.printf("IOException: %s%n", e.getMessage());
+        return;
+      } catch (Exception e) {
+        System.out.println("Unknown error trying to insert annotations from: " +
+                           indexFileName + " to " + className);
+        e.printStackTrace();
+        System.out.println("Please submit a bug report at");
+        System.out.println("  https://github.com/typetools/annotation-tools/issues");
+        System.out.println("Be sure to include a copy of the following output trace, instructions on how");
+        System.out.println("to reproduce this error, and all input files.  Thanks!");
+        return;
+      }
+    }
+
+  }
+
+  /**
+   * Inserts the annotations contained in <code> scene </code> into
+   * the class file contained in <code> fileName </code>, and write
+   * the result back into <code> fileName </code>.
+   *
+   * @param scene the scene containing the annotations to insert into a class
+   * @param fileName the file name of the class the annotations should be
+   * inserted into.  Should be a file name that can be resolved from
+   * the current working directory, which means it should end in ".class"
+   * for standard Java class files.
+   * @param overwrite controls behavior when an annotation exists on a
+   * particular element in both the scene and the class file.  If true,
+   * then the one from the scene is used; else the the existing annotation
+   * in the class file is retained.
+   * @throws IOException if there is a problem reading from or writing to
+   * <code> fileName </code>
+   */
+  public static void insert(
+      AScene scene, String fileName, boolean overwrite)
+  throws IOException {
+    assert fileName.endsWith(".class");
+
+    // can't just call other insert, because this closes the input stream
+    InputStream in = new FileInputStream(fileName);
+    ClassReader cr = new ClassReader(in);
+    in.close();
+
+    ClassAnnotationSceneWriter cw =
+      new ClassAnnotationSceneWriter(cr, scene, overwrite);
+    cr.accept(cw, false);
+
+    OutputStream fos = new FileOutputStream(fileName);
+    fos.write(cw.toByteArray());
+    fos.close();
+  }
+
+  /**
+   * Inserts the annotations contained in <code> scene </code> into
+   * the class file read from <code> in </code>, and writes the resulting
+   * class file into <code> out </code>.  <code> in </code> should be a stream
+   * of bytes that specify a valid Java class file, and <code> out </code> will
+   * contain a stream of bytes in the same format, and will also contain the
+   * annotations from <code> scene </code>.
+   *
+   * @param scene the scene containing the annotations to insert into a class
+   * @param in the input stream from which to read a class
+   * @param out the output stream the merged class should be written to
+   * @param overwrite controls behavior when an annotation exists on a
+   * particular element in both the scene and the class file.  If true,
+   * then the one from the scene is used; else the the existing annotation
+   * in the class file is retained.
+   * @throws IOException if there is a problem reading from <code> in </code> or
+   * writing to <code> out </code>
+   */
+  public static void insert(AScene scene, InputStream in,
+      OutputStream out, boolean overwrite) throws IOException {
+    ClassReader cr = new ClassReader(in);
+
+    ClassAnnotationSceneWriter cw =
+      new ClassAnnotationSceneWriter(cr, scene, overwrite);
+
+    cr.accept(cw, false);
+
+    out.write(cw.toByteArray());
+  }
+
+  /**
+   * Inserts the annotations contained in <code> scene </code> into
+   * the class <code> in </code>, and writes the resulting
+   * class file into <code> out </code>.  <code> in </code> should be the
+   * name of a fully-qualified class, and <code> out </code> should be the
+   * name of a file to output the resulting class file to.
+   *
+   * @param scene the scene containing the annotations to insert into a class
+   * @param className the fully qualified class to read
+   * @param outputFileName the name of the output file the class should be written to
+   * @param overwrite controls behavior when an annotation exists on a
+   * particular element in both the scene and the class file.  If true,
+   * then the one from the scene is used; else the the existing annotation
+   * in the class file is retained.
+   * @throws IOException if there is a problem reading from <code> in </code> or
+   * writing to <code> out </code>
+   */
+  public static void insert(AScene scene,
+      String className, String outputFileName, boolean overwrite) throws IOException {
+    ClassReader cr = new ClassReader(className);
+
+    ClassAnnotationSceneWriter cw =
+      new ClassAnnotationSceneWriter(cr, scene, overwrite);
+
+    cr.accept(cw, false);
+
+    OutputStream fos = new FileOutputStream(outputFileName);
+    fos.write(cw.toByteArray());
+    fos.close();
+  }
+}
diff --git a/scene-lib/src/annotations/io/classfile/CodeOffsetAdapter.java b/scene-lib/src/annotations/io/classfile/CodeOffsetAdapter.java
new file mode 100644
index 0000000..435ac0a
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/CodeOffsetAdapter.java
@@ -0,0 +1,213 @@
+package annotations.io.classfile;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import annotations.io.DebugWriter;
+import annotations.util.AbstractClassVisitor;
+
+public class CodeOffsetAdapter extends ClassAdapter {
+  static final DebugWriter debug;
+  final ClassReader cr;
+  final char[] buf;
+  int methodStart;
+  int codeStart;
+  int offset;
+
+  static {
+    debug = new DebugWriter();
+    debug.setEnabled(false);
+  }
+
+  public CodeOffsetAdapter(ClassReader cr) {
+    this(cr, new AbstractClassVisitor());
+  }
+
+  public CodeOffsetAdapter(ClassReader cr, ClassVisitor v) {
+    super(v);
+    this.cr = cr;
+    // const pool size is (not lowest) upper bound of string length
+    buf = new char[cr.header];
+    methodStart = cr.header + 6;
+    methodStart += 4 + 2 * cr.readUnsignedShort(methodStart);
+    for (int i = cr.readUnsignedShort(methodStart-2); i > 0; --i) {
+      methodStart += 8;
+      for (int j = cr.readUnsignedShort(methodStart-2); j > 0; --j) {
+        methodStart += 6 + cr.readInt(methodStart+2);
+      }
+    }
+    methodStart += 2;
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access,
+      String name, String desc,
+      String signature, String[] exceptions) {
+    MethodVisitor v =
+        super.visitMethod(access, name, desc, signature, exceptions);
+    return new MethodAdapter(v) {
+      private int methodEnd;
+
+      {
+        String name = cr.readUTF8(methodStart + 2, buf);
+        String desc = cr.readUTF8(methodStart + 4, buf);
+        int attrCount = cr.readUnsignedShort(methodStart + 6);
+        debug.debug("visiting %s%s (%d)%n", name, desc, methodStart);
+        debug.debug("%d attributes%n", attrCount);
+        methodEnd = methodStart + 8;
+
+        // find code attribute
+        codeStart = methodEnd;
+        if (attrCount > 0) {
+          while (--attrCount >= 0) {
+            String attrName = cr.readUTF8(codeStart, buf);
+            debug.debug("attribute %s%n", attrName);
+            if ("Code".equals(attrName)) {
+              codeStart += 6;
+              offset = codeStart + cr.readInt(codeStart - 4);
+              codeStart += 8;
+              while (--attrCount >= 0) {
+                debug.debug("attribute %s%n", cr.readUTF8(offset, buf));
+                offset += 6 + cr.readInt(offset + 2);
+              }
+              methodEnd = offset;
+              break;
+            }
+            codeStart += 6 + cr.readInt(codeStart + 2);
+            methodEnd = codeStart;
+          }
+        }
+        offset = 0;
+      }
+
+      private int readInt(int i) {
+        return cr.readInt(codeStart + i);
+      }
+
+      @Override
+      public void visitFieldInsn(int opcode,
+          String owner, String name, String desc) {
+        super.visitFieldInsn(opcode, owner, name, desc);
+        debug.debug("%d visitFieldInsn(%d, %s, %s, %s)%n", offset,
+            opcode, owner, name, desc);
+        offset += 3;
+      }
+
+      @Override
+      public void visitIincInsn(int var, int increment) {
+        super.visitIincInsn(var, increment);
+        debug.debug("%d visitIincInsn(%d, %d)%n", offset, var, increment);
+        offset += 3;
+      }
+
+      @Override
+      public void visitInsn(int opcode) {
+        super.visitInsn(opcode);
+        debug.debug("%d visitInsn(%d)%n", offset, opcode);
+        ++offset;
+      }
+
+      @Override
+      public void visitIntInsn(int opcode, int operand) {
+        super.visitIntInsn(opcode, operand);
+        debug.debug("%d visitIntInsn(%d, %d)%n", offset, opcode, operand);
+        offset += opcode == Opcodes.SIPUSH ? 3 : 2;
+      }
+
+      @Override
+      public void visitInvokeDynamicInsn(String name, String desc,
+          Handle bsm, Object... bsmArgs) {
+        super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+        debug.debug("%d visitInvokeDynamicInsn(%s, %s)%n", offset,
+            name, desc, bsm, bsmArgs);
+        offset += 5;
+      }
+
+      @Override
+      public void visitJumpInsn(int opcode, Label label) {
+        super.visitJumpInsn(opcode, label);
+        debug.debug("%d visitJumpInsn(%d, %s)%n", offset, opcode, label);
+        // account for wide instructions goto_w (200) and jsr_w (201)
+        offset += cr.readByte(codeStart + offset) < 200 ? 3 : 4;
+        assert offset > 0 && methodEnd > codeStart + offset;
+      }
+
+      @Override
+      public void visitLdcInsn(Object cst) {
+        super.visitLdcInsn(cst);
+        debug.debug("%d visitLdcInsn(%s)%n", offset, cst);
+        // account for wide instructions ldc_w (19) and ldc2_w (20)
+        offset += cr.readByte(codeStart + offset) > 18 ? 3 : 2;
+        assert offset > 0 && methodEnd > codeStart + offset;
+      }
+
+      @Override
+      public void visitLookupSwitchInsn(Label dflt, int[] keys,
+          Label[] labels) {
+        super.visitLookupSwitchInsn(dflt, keys, labels);
+        debug.debug("%d visitLookupSwitchInsn(%s)%n", offset,
+            dflt, keys, labels);
+        offset += 8 - (offset & 3);
+        offset += 4 + 8 * readInt(offset);
+        assert offset > 0 && methodEnd > codeStart + offset;
+      }
+
+      @Override
+      public void visitMethodInsn(int opcode,
+          String owner, String name, String desc) {
+        super.visitMethodInsn(opcode, owner, name, desc);
+        debug.debug("%d visitMethodInsn(%d, %s, %s, %s)%n", offset,
+            opcode, owner, name, desc);
+        offset += opcode == Opcodes.INVOKEINTERFACE ? 5 : 3;
+      }
+
+      @Override
+      public void visitMultiANewArrayInsn(String desc, int dims) {
+        super.visitMultiANewArrayInsn(desc, dims);
+        debug.debug("%d visitMultiANewArrayInsn(%s, %d)%n", offset,
+            desc, dims);
+        offset += 4;
+      }
+
+      @Override
+      public void visitTableSwitchInsn(int min, int max,
+          Label dflt, Label[] labels) {
+        super.visitTableSwitchInsn(min, max, dflt, labels);
+        debug.debug("%d visitTableSwitchInsn(%d, %d, %s)%n", offset,
+            min, max, dflt, labels);
+        offset += 8 - (offset & 3);
+        offset += 4 * (readInt(offset + 4) - readInt(offset) + 3);
+        assert offset > 0 && methodEnd > codeStart + offset;
+      }
+
+      @Override
+      public void visitTypeInsn(int opcode, String desc) {
+        super.visitTypeInsn(opcode, desc);
+        debug.debug("%d visitTypeInsn(%d, %s)%n", offset, opcode, desc);
+        offset += 3;
+      }
+
+      @Override
+      public void visitVarInsn(int opcode, int var) {
+        super.visitVarInsn(opcode, var);
+        debug.debug("%d visitVarInsn(%d, %d)%n", offset, opcode, var);
+        offset += var < 4 ? 1 : 2;
+      }
+
+      @Override
+      public void visitEnd() {
+        methodStart = methodEnd;
+      }
+    };
+  }
+
+  public int getMethodCodeOffset() { return offset; }
+
+  public int getBytecodeOffset() { return codeStart + offset; }
+}
diff --git a/scene-lib/src/annotations/io/classfile/InvalidTypeAnnotationException.java b/scene-lib/src/annotations/io/classfile/InvalidTypeAnnotationException.java
new file mode 100644
index 0000000..102b88e
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/InvalidTypeAnnotationException.java
@@ -0,0 +1,25 @@
+package annotations.io.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * An <code>InvalidTypeAnnotationException </code> indicates that an
+ * extended annotation was created with invalid information.  For example,
+ * an extended annotation on a local variable should not contain offset
+ * information.
+ */
+public class InvalidTypeAnnotationException extends RuntimeException {
+  static final long serialVersionUID = 20060712L; // Today's date.
+
+  /**
+   * Constructs a new <code> InvalidTypeAnnotationException </code> with
+   * the given error message.
+   *
+   * @param msg a message describing what was wrong with the extended annotation
+   */
+  public InvalidTypeAnnotationException(String msg) {
+    super(msg);
+  }
+}
diff --git a/scene-lib/src/annotations/io/classfile/MethodCodeOffsetAdapter.java b/scene-lib/src/annotations/io/classfile/MethodCodeOffsetAdapter.java
new file mode 100644
index 0000000..2a2160f
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/MethodCodeOffsetAdapter.java
@@ -0,0 +1,136 @@
+package annotations.io.classfile;
+
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+class MethodCodeOffsetAdapter extends MethodAdapter {
+  private final ClassReader cr;
+  private final int methodStart;
+  private int offset = 0;
+  private int codeStart = 0;
+  private int attrCount = 0;
+
+  public MethodCodeOffsetAdapter(ClassReader classReader,
+      MethodVisitor methodVisitor, int start) {
+    super(methodVisitor);
+    char[] buf = new char[classReader.header];
+    this.methodStart = start;
+    cr = classReader;
+    // const pool size is (not lowest) upper bound of string length
+    codeStart = start;
+    attrCount = classReader.readUnsignedShort(codeStart + 6);
+
+    // find code attribute
+    codeStart += 8;
+    while (attrCount > 0) {
+      String attrName = classReader.readUTF8(codeStart, buf);
+      if ("Code".equals(attrName)) { break; }
+      codeStart += 6 + classReader.readInt(codeStart + 2);
+      --attrCount;
+    }
+  }
+
+  private int readInt(int i) {
+    return cr.readInt(codeStart + i);
+  }
+
+  public int getMethodCodeStart() { return methodStart; }
+
+  public int getMethodCodeOffset() { return offset; }
+
+  public int getClassCodeOffset() { return codeStart + offset; }
+
+  @Override
+  public void visitFieldInsn(int opcode,
+      String owner, String name, String desc) {
+    super.visitFieldInsn(opcode, owner, name, desc);
+    offset += 3;
+  }
+
+  @Override
+  public void visitIincInsn(int var, int increment) {
+    super.visitIincInsn(var, increment);
+    offset += 3;
+  }
+
+  @Override
+  public void visitInsn(int opcode) {
+    super.visitInsn(opcode);
+    ++offset;
+  }
+
+  @Override
+  public void visitIntInsn(int opcode, int operand) {
+    super.visitIntInsn(opcode, operand);
+    offset += opcode == Opcodes.SIPUSH ? 3 : 2;
+  }
+
+  @Override
+  public void visitInvokeDynamicInsn(String name, String desc,
+      Handle bsm, Object... bsmArgs) {
+    super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+    offset += 5;
+  }
+
+  @Override
+  public void visitJumpInsn(int opcode, Label label) {
+    super.visitJumpInsn(opcode, label);
+    offset += 3;
+  }
+
+  @Override
+  public void visitLdcInsn(Object cst) {
+    super.visitLdcInsn(cst);
+    offset += 2;
+  }
+
+  @Override
+  public void visitLookupSwitchInsn(Label dflt, int[] keys,
+      Label[] labels) {
+    super.visitLookupSwitchInsn(dflt, keys, labels);
+    offset += 8 - ((offset - codeStart) & 3);
+    offset += 4 + 8 * readInt(offset);
+  }
+
+  @Override
+  public void visitMethodInsn(int opcode,
+      String owner, String name, String desc) {
+    super.visitMethodInsn(opcode, owner, name, desc);
+    offset += opcode == Opcodes.INVOKEINTERFACE ? 5 : 3;
+  }
+
+  @Override
+  public void visitMultiANewArrayInsn(String desc, int dims) {
+    super.visitMultiANewArrayInsn(desc, dims);
+    offset += 4;
+  }
+
+  @Override
+  public void visitTableSwitchInsn(int min, int max,
+      Label dflt, Label[] labels) {
+    super.visitTableSwitchInsn(min, max, dflt, labels);
+    offset += 8 - ((offset - codeStart) & 3);
+    offset += 4 * (readInt(offset + 4) - readInt(offset) + 3);
+  }
+
+  @Override
+  public void visitTypeInsn(int opcode, String desc) {
+    super.visitTypeInsn(opcode, desc);
+    offset += 3;
+  }
+
+  @Override
+  public void visitVarInsn(int opcode, int var) {
+    super.visitVarInsn(opcode, var);
+    offset += var < 4 ? 1 : 2;
+  }
+
+  @Override
+  public void visitEnd() {
+    offset = -1;  // invalidated
+  }
+}
diff --git a/scene-lib/src/annotations/io/classfile/SafeTypeAnnotationVisitor.java b/scene-lib/src/annotations/io/classfile/SafeTypeAnnotationVisitor.java
new file mode 100644
index 0000000..6ef910b
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/SafeTypeAnnotationVisitor.java
@@ -0,0 +1,424 @@
+// This is a wrapper class around an TypeAnnotationVisitor that can be used
+// to verify the information it visits specifies a valid [extended] annotation.
+
+package annotations.io.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+
+import com.sun.tools.javac.code.TargetType;
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * A <code>SafeTypeAnnotationVisitor</code> wraps around an
+ * TypeAnnotationVisitor and delegates all calls to it.  However, it
+ * maintains a record of all methods that have been called and on
+ * calling {@link SafeTypeAnnotationVisitor#visitEnd},
+ * performs a check to verify that all the data passed
+ * to this visitor specifies a legal annotation or extended annotation.  Its
+ * intended use is to wrap around an <code>TypeAnnotationWriter</code> and
+ * thus ensure that no illegal class files are written, although it will work
+ * more generally on any visitor.
+ *
+ * <p>
+ * Also note that nothing special needs to be done about subannotations
+ * and regular arrays, since their lengths are not passed in to this visitor.
+ *
+ * <p>
+ * If none of the <code>visitX*</code> methods has been called, it is
+ * automatically a legal annotation.  Else, if some of the <code>visitX*</code>
+ * methods have been called, the check will ensure that the data passed to this
+ * specifies a legal extended annotation, as defined by its target type.
+ */
+public class SafeTypeAnnotationVisitor
+implements TypeAnnotationVisitor {
+
+  // The visitor this delegates all calls to.
+  private final TypeAnnotationVisitor xav;
+
+  // Each list keeps a record of what was passed in to the similarly-named
+  // method, and except for xLocationArgs, should all contain at most 1 element.
+  private final List<Integer> xIndexArgs;
+  private final List<Integer> xLengthArgs;
+  private final List<TypePathEntry> xLocationArgs;
+  private final List<Integer> xLocationLengthArgs;
+  private final List<Integer> xOffsetArgs;
+  private final List<Integer> xStartPcArgs;
+  private final List<Integer> xTargetTypeArgs;
+  private final List<Integer> xParamIndexArgs;
+  private final List<Integer> xBoundIndexArgs;
+  private final List<Integer> xTypeIndexArgs;
+
+  // Counts the number of times visitXNameAndArgsSize is called.
+  private int xNameAndArgsCount;
+
+  /**
+   * Constructs a new <code> SafeTypeAnnotationVisitor </code> that
+   *  delegates all calls to the given visitor.
+   *
+   * @param xav the visitor to delegate all method calls to
+   */
+  public SafeTypeAnnotationVisitor(TypeAnnotationVisitor xav) {
+    this.xav = xav;
+    // Start most of these with a capacity of one, since for legal annotations
+    // they should not contain more than one element.
+    xIndexArgs = new ArrayList<Integer>(1);
+    xLengthArgs = new ArrayList<Integer>(1);
+    xLocationArgs = new ArrayList<TypePathEntry>();
+    xLocationLengthArgs = new ArrayList<Integer>(1);
+    xOffsetArgs = new ArrayList<Integer>(1);
+    xStartPcArgs = new ArrayList<Integer>(1);
+    xTargetTypeArgs = new ArrayList<Integer>(1);
+    xParamIndexArgs = new ArrayList<Integer>(1);
+    xBoundIndexArgs = new ArrayList<Integer>(1);
+    xTypeIndexArgs = new ArrayList<Integer>(1);
+    xNameAndArgsCount = 0;
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
+   */
+  @Override
+  public void visit(String name, Object value) {
+    xav.visit(name, value);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
+   */
+  @Override
+  public AnnotationVisitor visitAnnotation(String name, String desc) {
+    return xav.visitAnnotation(name, desc);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
+   */
+  @Override
+  public AnnotationVisitor visitArray(String name) {
+    return xav.visitArray(name);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
+   */
+  @Override
+  public void visitEnum(String name, String desc, String value) {
+    xav.visitEnum(name, desc, value);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXIndex(int)
+   */
+  @Override
+  public void visitXIndex(int index) {
+    xIndexArgs.add(index);
+    xav.visitXIndex(index);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLength(int)
+   */
+  @Override
+  public void visitXLength(int length) {
+    xLengthArgs.add(length);
+    xav.visitXLength(length);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocation(TypePathEntry)
+   */
+  @Override
+  public void visitXLocation(TypePathEntry location) {
+    xLocationArgs.add(location);
+    xav.visitXLocation(location);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocationLength(int)
+   */
+  @Override
+  public void visitXLocationLength(int location_length) {
+    xLocationLengthArgs.add(location_length);
+    xav.visitXLocationLength(location_length);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXOffset(int)
+   */
+  @Override
+  public void visitXOffset(int offset) {
+    xOffsetArgs.add(offset);
+    xav.visitXOffset(offset);
+  }
+
+  @Override
+  public void visitXNumEntries(int num_entries) {
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXStartPc(int)
+   */
+  @Override
+  public void visitXStartPc(int start_pc) {
+    xStartPcArgs.add(start_pc);
+    xav.visitXStartPc(start_pc);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXTargetType(int)
+   */
+  @Override
+  public void visitXTargetType(int target_type) {
+    xTargetTypeArgs.add(target_type);
+    xav.visitXTargetType(target_type);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXParamIndex(int)
+   */
+  @Override
+  public void visitXParamIndex(int param_index) {
+    xParamIndexArgs.add(param_index);
+    xav.visitXParamIndex(param_index);
+  }
+
+  /**
+   * {@inheritDoc}
+   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXBoundIndex(int)
+   */
+  @Override
+  public void visitXBoundIndex(int bound_index) {
+    if (bound_index != -1) {
+      xBoundIndexArgs.add(bound_index);
+      xav.visitXBoundIndex(bound_index);
+    }
+  }
+
+  @Override
+  public void visitXTypeIndex(int type_index) {
+    xTypeIndexArgs.add(type_index);
+    xav.visitXTypeIndex(type_index);
+  }
+
+  @Override
+  public void visitXExceptionIndex(int exception_index) {
+    // TODO
+  }
+
+  @Override
+  public void visitXNameAndArgsSize() {
+    xNameAndArgsCount++;
+    xav.visitXNameAndArgsSize();
+  }
+
+  /**
+   * Visits the end of the annotation, and also performs a check
+   *  to ensure that the information this has visited specifies a legal
+   *  annotation.  If the information does not specify a legal annotation,
+   *  throws an exception.
+   *
+   * {@inheritDoc}
+   * @throws InvalidTypeAnnotationException if the information this
+   *  has visited does not specify a legal extended annotation
+   * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
+   */
+  @Override
+  public void visitEnd() {
+    if (xTargetTypeArgs.size() > 0) {
+      checkX();
+    } else {
+      // This has not visited an the target type of an extended annotation, so
+      //  must ensure that all other extended information lists are empty.
+      if (xIndexArgs.size() != 0 ||
+          xLengthArgs.size() != 0 ||
+          xLocationArgs.size() != 0 ||
+          xLocationLengthArgs.size() != 0 ||
+          xOffsetArgs.size() != 0 ||
+          xStartPcArgs.size() != 0) {
+        throw new InvalidTypeAnnotationException(
+        "No target type was specified, yet other visitX* methods were still called.");
+      }
+    }
+    xav.visitEnd();
+  }
+
+  /**
+   * Checks that the extended information this has visited is valid.
+   *
+   * @throws InvalidTypeAnnotationException if extended information is
+   *  not valid
+   */
+  private void checkX() {
+    // First, check to see that only one target type was specified, and
+    // then dispatch to checkListSize() based on that target type.
+    if (xTargetTypeArgs.size() != 1) {
+      throw new
+      InvalidTypeAnnotationException("More than one target type visited.");
+    }
+
+    if (xNameAndArgsCount != 1) {
+      throw new InvalidTypeAnnotationException("Name and args count should "
+          + " be visited 1 time, actually visited " + xNameAndArgsCount
+          + " times.");
+    }
+
+    // Since the correct size of xLocationArgs is specified by
+    // xLocationLengthArgs, this information must be looked up first.
+    int c = 0;
+    if (xLocationLengthArgs.size() > 0) {
+      c = xLocationLengthArgs.get(0);
+    }
+
+    switch(TargetType.fromTargetTypeValue(xTargetTypeArgs.get(0))) {
+    case CAST:
+      checkListSize(0, 0, c, 1, 1, 0, 0, 0, 1,
+          "Invalid typecast annotation:");
+      break;
+    case INSTANCEOF:
+      checkListSize(0, 0, c, 1, 1, 0, 0, 0, 0,
+      "Invalid type test annotation:");
+      break;
+    case NEW:
+      checkListSize(0, 0, c, 1, 1, 0, 0, 0, 0,
+      "Invalid object creation annotation:");
+      break;
+    case METHOD_RECEIVER:
+      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0,
+      "Invalid method receiver annotation:");
+      break;
+    case LOCAL_VARIABLE:
+      checkListSize(1, 1, c, 1, 0, 1, 0, 0, 0,
+      "Invalid local variable annotation:");
+      break;
+    case METHOD_RETURN:
+      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0,
+      "Invalid method return type annotation:");
+      break;
+    case METHOD_FORMAL_PARAMETER:
+      checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0,
+      "Invalid method parameter annotation:");
+      break;
+    case FIELD:
+      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0,
+      "Invalid field annotation:");
+      break;
+    case CLASS_TYPE_PARAMETER:
+      checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0,
+      "Invalid class type parameter annotation:");
+      break;
+    case CLASS_TYPE_PARAMETER_BOUND:
+      checkListSize(0, 0, c, 1, 0, 0, 1, 1, 0,
+      "Invalid class type parameter bound annotation:");
+      break;
+    case METHOD_TYPE_PARAMETER:
+      checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0,
+      "Invalid method type parameter annotation:");
+      break;
+    case METHOD_TYPE_PARAMETER_BOUND:
+      checkListSize(0, 0, c, 1, 0, 0, 1, 1, 0,
+      "Invalid method type parameter bound annotation:");
+      break;
+    case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
+    case METHOD_INVOCATION_TYPE_ARGUMENT:
+    case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
+    case METHOD_REFERENCE_TYPE_ARGUMENT:
+      // TODO
+      break;
+    case CLASS_EXTENDS:
+      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 1,
+      "Invalid class extends/implements annotation:");
+      break;
+    case THROWS:
+      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 1,
+      "Invalid exception type in throws annotation:");
+      break;
+    default:
+      throw new InvalidTypeAnnotationException(
+          "Unknown target type given: " + xTargetTypeArgs.get(0));
+    }
+  }
+
+  /**
+   * If list.size() != correctLength, appends a descriptive error message to sb
+   *  specifying how many times methodName was supposed to be called and how
+   *  many times it was actually called.
+   * Else, has no effect.
+   *
+   * @param list the list of arguments actually visited
+   * @param correctLength the correct length of list
+   * @param methodName the name of the method whose arguments went into list
+   * @param sb the StringBuilder to append error messages to
+   */
+  private void appendMessage(List<?> list, int idealLength,
+      String methodName, StringBuilder sb) {
+    if (list.size() != idealLength) {
+      sb.append("\nInvalid method calls: ");
+      sb.append(methodName);
+      sb.append(" was called ");
+      sb.append(list.size());
+      sb.append(" times, but should have only been called ");
+      sb.append(idealLength);
+      sb.append(" times");
+    }
+  }
+
+  /**
+   * Checks that the seven lists containing extended annotation information are
+   *  of the specified sizes.  If at least one of the lists is not of the
+   *  correct length, this throws an exception with a message equal to msg, plus
+   *  information describing which lists were incorrect.
+   *
+   * @throws InvalidTypeAnnotationException if the extended information
+   *  lists are not of the correct length
+   */
+  private void checkListSize(
+      int correctLengthIndex,
+      int correctLengthLength,
+      int correctLengthLocation,
+      int correctLengthLocationLength,
+      int correctLengthOffset,
+      int correctLengthStartPc,
+      int correctLengthParamIndex,
+      int correctLengthBoundIndex,
+      int correctLengthTypeIndex,
+      String msg) {
+    StringBuilder sb = new StringBuilder();
+    appendMessage(xIndexArgs, correctLengthIndex, "visitXIndex", sb);
+    appendMessage(xLengthArgs, correctLengthLength, "visitXLength", sb);
+    appendMessage(xLocationArgs, correctLengthLocation, "visitXLocation", sb);
+    appendMessage(xLocationLengthArgs, correctLengthLocationLength,
+        "visitXLocationLength", sb);
+    appendMessage(xOffsetArgs, correctLengthOffset, "visitXOffset", sb);
+    appendMessage(xStartPcArgs, correctLengthStartPc, "visitXStartPc", sb);
+    appendMessage(xParamIndexArgs, correctLengthParamIndex, "visitXParamIndex", sb);
+    appendMessage(xBoundIndexArgs, correctLengthBoundIndex, "visitXBoundIndex", sb);
+    appendMessage(xTypeIndexArgs, correctLengthTypeIndex, "VisitXTypeIndex", sb);
+
+    // At this point, sb will contain Strings iff there is an error
+    //  in the extended annotation information.
+    String s = sb.toString();
+    if (s.length() > 0) {
+      throw new InvalidTypeAnnotationException(msg + s);
+    }
+  }
+}
diff --git a/scene-lib/src/annotations/io/classfile/package-info.java b/scene-lib/src/annotations/io/classfile/package-info.java
new file mode 100644
index 0000000..509bbd0
--- /dev/null
+++ b/scene-lib/src/annotations/io/classfile/package-info.java
@@ -0,0 +1,13 @@
+/**
+ * <code> annotations.io.classfile </code> provides methods for writing
+ * {@link annotations.el.AScene}s to Java class files and reading in annotations
+ * from a Java class file into an {@link annotations.el.AScene}.
+ * This package requires the core ASM package (see
+ * <a href="http://asm.objectweb.org/"> http://asm.objectweb.org/</a> ).
+ * The two main methods of this package are
+ * {@link annotations.io.classfile.ClassFileWriter#insert} for
+ * writing annotations to a class file, and
+ * {@link annotations.io.classfile.ClassFileReader#read} for
+ * reading annotations from a class file.
+ */
+package annotations.io.classfile;
diff --git a/scene-lib/src/annotations/io/package-info.java b/scene-lib/src/annotations/io/package-info.java
new file mode 100644
index 0000000..6e9a562
--- /dev/null
+++ b/scene-lib/src/annotations/io/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * <code>annotations.io</code> provides classes for the input and output
+ * of {@link annotations.el.AScene}s to/from various formats.
+ */
+package annotations.io;
+
diff --git a/scene-lib/src/annotations/package-info.java b/scene-lib/src/annotations/package-info.java
new file mode 100644
index 0000000..642ae28
--- /dev/null
+++ b/scene-lib/src/annotations/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * <code>annotations</code> provides classes that represent annotations,
+ * their definitions, and their factories.
+ */
+package annotations;
+
diff --git a/scene-lib/src/annotations/tools/Anncat.java b/scene-lib/src/annotations/tools/Anncat.java
new file mode 100644
index 0000000..0c8853a
--- /dev/null
+++ b/scene-lib/src/annotations/tools/Anncat.java
@@ -0,0 +1,149 @@
+package annotations.tools;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.*;
+
+import annotations.el.AScene;
+import annotations.el.DefException;
+import annotations.io.IndexFileParser;
+import annotations.io.IndexFileWriter;
+import annotations.io.JavapParser;
+import annotations.io.classfile.ClassFileReader;
+import annotations.io.classfile.ClassFileWriter;
+import plume.FileIOException;
+
+/** Concatenates multiple descriptions of annotations into a single one. */
+public class Anncat {
+    private static void usage() {
+        System.err.print(
+                "anncat, part of the Annotation File Utilities\n" +
+                "(https://checkerframework.org/annotation-file-utilities/)\n" +
+                "usage: anncat <inspec>* [ --out <outspec> ], where:\n" +
+                "    <inspec> ::=\n" +
+                "        ( --javap <in.javap> )\n" +
+                "        | ( --index <in.jaif> )\n" +
+                "        | ( --class <in.class> )\n" +
+                "    <outspec> ::=\n" +
+                "        ( --index <out.jaif> )\n" +
+                "        | ( --class [ --overwrite ] <orig.class> [ --to <out.class> ] )\n" +
+        "If outspec is omitted, default is index file to stdout.\n");
+    }
+
+    private static void usageAssert(boolean b) {
+        if (!b) {
+            System.err.println("*** Usage error ***");
+            usage();
+            System.exit(3);
+        }
+    }
+
+    public static void main(String[] args) {
+        usageAssert(0 < args.length);
+        if (args[0].equals("--help")) {
+            usage();
+            System.exit(0);
+        }
+
+        try {
+
+            int idx = 0;
+
+            /*@NonNull*/ AScene theScene =
+                new AScene();
+
+            // Read the scene
+            while (idx < args.length && !args[idx].equals("--out")) {
+                if (args[idx].equals("--javap")) {
+                    idx++;
+                    usageAssert(idx < args.length);
+                    String infile = args[idx++];
+                    System.out.println("Reading javap file " + infile + "...");
+                    JavapParser.parse(infile, theScene);
+                    System.out.println("Finished.");
+                } else if (args[idx].equals("--index")) {
+                    idx++;
+                    usageAssert(idx < args.length);
+                    String infile = args[idx++];
+                    System.err.println("Reading index file " + infile + "...");
+                    IndexFileParser.parseFile(infile, theScene);
+                    System.err.println("Finished.");
+                } else if (args[idx].equals("--class")) {
+                    idx++;
+                    usageAssert(idx < args.length);
+                    String infile = args[idx++];
+                    System.err.println("Reading class file " + infile + "...");
+                    ClassFileReader.read(theScene, infile);
+                    System.err.println("Finished.");
+                } else {
+                    usageAssert(false);
+                }
+            }
+
+            // Write the scene
+            if (idx == args.length) {
+                System.err.println("Writing index file to standard output...");
+                IndexFileWriter.write(theScene, new OutputStreamWriter(System.out));
+                System.err.println("Finished.");
+            } else {
+                idx++;
+                usageAssert(idx < args.length);
+                if (args[idx].equals("--index")) {
+                    idx++;
+                    usageAssert(idx < args.length);
+                    String outfile = args[idx];
+                    idx++;
+                    usageAssert(idx == args.length);
+                    System.err.println("Writing index file to " + outfile + "...");
+                    IndexFileWriter.write(theScene, new FileWriter(outfile));
+                    System.err.println("Finished.");
+                } else if (args[idx].equals("--class")) {
+                    idx++;
+                    usageAssert(idx < args.length);
+                    boolean overwrite;
+                    if (args[idx].equals("--overwrite")) {
+                        System.err.println("Overwrite mode enabled.");
+                        overwrite = true;
+                        idx++;
+                        usageAssert(idx < args.length);
+                    } else {
+                        overwrite = false;
+                    }
+                    String origfile = args[idx];
+                    idx++;
+                    if (idx < args.length) {
+                        usageAssert(args[idx].equals("--to"));
+                        idx++;
+                        usageAssert(idx < args.length);
+                        String outfile = args[idx];
+                        idx++;
+                        usageAssert(idx == args.length);
+                        System.err.println("Reading original class file " + origfile);
+                        System.err.println("and writing annotated version to " + outfile + "...");
+                        ClassFileWriter.insert(theScene, new FileInputStream(origfile), new FileOutputStream(outfile), overwrite);
+                        System.err.println("Finished.");
+                    } else {
+                        System.err.println("Rewriting class file " + origfile + " with annotations...");
+                        ClassFileWriter.insert(theScene, origfile, overwrite);
+                        System.err.println("Finished.");
+                    }
+                } else {
+                    usageAssert(false);
+                }
+            }
+
+        } catch (FileIOException e) {
+            e.printStackTrace(System.err);
+            System.exit(1);
+        } catch (IOException e) {
+            e.printStackTrace(System.err);
+            System.exit(2);
+        } catch (DefException e) {
+            e.printStackTrace(System.err);
+            System.exit(1);
+        }
+        System.exit(0);
+    }
+}
diff --git a/scene-lib/src/annotations/tools/IndexFileMerger.java b/scene-lib/src/annotations/tools/IndexFileMerger.java
new file mode 100644
index 0000000..1fea1e4
--- /dev/null
+++ b/scene-lib/src/annotations/tools/IndexFileMerger.java
@@ -0,0 +1,278 @@
+package annotations.tools;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.SetMultimap;
+import com.sun.tools.javac.main.CommandLine;
+
+import plume.FileIOException;
+import annotations.Annotation;
+import annotations.Annotations;
+import annotations.el.ABlock;
+import annotations.el.AClass;
+import annotations.el.ADeclaration;
+import annotations.el.AElement;
+import annotations.el.AExpression;
+import annotations.el.AField;
+import annotations.el.AMethod;
+import annotations.el.AScene;
+import annotations.el.ATypeElement;
+import annotations.el.ATypeElementWithType;
+import annotations.el.AnnotationDef;
+import annotations.el.DefException;
+import annotations.el.ElementVisitor;
+import annotations.field.AnnotationFieldType;
+import annotations.io.IndexFileParser;
+import annotations.io.IndexFileWriter;
+
+/**
+ * Utility for merging index files, including multiple versions for the
+ *  same class.
+ *
+ * @author dbro
+ */
+public class IndexFileMerger {
+  public static void main(String[] args) {
+    if (args.length < 1) { System.exit(0); }
+
+    final SetMultimap<String, String> annotatedFor = HashMultimap.create();
+    String[] inputArgs;
+
+    // TODO: document assumptions
+    // collect annotations into scene
+    try {
+      try {
+        inputArgs = CommandLine.parse(args);
+      } catch (IOException ex) {
+        System.err.println(ex);
+        System.err.println("(For non-argfile beginning with \"@\", use \"@@\" for initial \"@\".");
+        System.err.println("Alternative for filenames: indicate directory, e.g. as './@file'.");
+        System.err.println("Alternative for flags: use '=', as in '-o=@Deprecated'.)");
+        System.exit(1);
+        return;  // so compiler knows inputArgs defined after try/catch
+      }
+
+      File baseFile = new File(inputArgs[0]);
+      boolean byDir = baseFile.isDirectory();
+      String basePath = baseFile.getCanonicalPath();
+      AScene scene = new AScene();
+
+      for (int i = byDir ? 1 : 0; i < inputArgs.length; i++) {
+        File inputFile = new File(inputArgs[i]);
+        String inputPath = inputFile.getCanonicalPath();
+        String filename = inputFile.getName();
+
+        if (byDir) {
+          if (!(filename.endsWith(".jaif") || filename.endsWith("jann"))) {
+            System.err.println("WARNING: ignoring non-JAIF " + filename);
+            continue;
+          }
+          if (!inputPath.startsWith(basePath)) {
+            System.err.println("WARNING: ignoring file outside base directory "
+                + filename);
+            continue;
+          }
+
+          // note which base subdirectory JAIF came from
+          String relPath = inputPath.substring(basePath.length()+1);  // +1 '/'
+          int ix = relPath.indexOf(File.separator);
+          String subdir = ix < 0 ? relPath : relPath.substring(0, ix);
+          // trim .jaif or .jann and subdir, convert directory to package id
+          String classname = relPath.substring(0, relPath.lastIndexOf('.'))
+              .substring(relPath.indexOf('/')+1).replace(File.separator, ".");
+          annotatedFor.put(classname, "\"" + subdir + "\"");
+        }
+
+        try {
+          IndexFileParser.parseFile(inputPath, scene);
+        } catch (FileNotFoundException e) {
+          System.err.println("IndexFileMerger: can't read "
+              + inputPath);
+          System.exit(1);
+        } catch (FileIOException e) {
+          e.printStackTrace();  // TODO
+          System.exit(1);
+        }
+      }
+
+      if (!byDir) {
+/*
+        // collect defs
+        Map<String, String> annoPkgs = new HashMap<String, String>();
+        try {
+          new DefCollector(scene) {
+            @Override
+            protected void visitAnnotationDef(AnnotationDef d) {
+              String[] a = d.name.split("\\.");
+              if (a.length > 2 && a[a.length-2].matches("quals?")) {
+                String s = a[a.length-1];
+                annoPkgs.put(s, d.name.substring(0));
+              }
+            }
+          }.visit();
+        } catch (DefException e) {
+          System.err.println("DefCollector failed!");
+          e.printStackTrace();
+          System.exit(1);
+        }
+*/
+
+        for (Map.Entry<String, AClass> entry : scene.classes.entrySet()) {
+          // final String classname = entry.getKey();
+
+          entry.getValue().accept(new ElementVisitor<Void, Void>() {
+            // Map<String, String> annoPkgs = new HashMap<String, String>();
+
+            // Void process(AElement el) {
+            //  for (Annotation anno : el.tlAnnotationsHere) {
+            //    AnnotationDef def = anno.def();
+            //    String[] a = def.name.split("\\.");
+            //    if ("AnnotatedFor".equals(a[a.length-1])) {
+            //      @SuppressWarnings("unchecked")
+            //      List<String> vals =
+            //          (List<String>) anno.getFieldValue("value");
+            //      for (String val : vals) {
+            //        annotatedFor.put(classname, val);
+            //      }
+            //    } else if (a.length > 2 && a[a.length-2].matches("quals?")) {
+            //      annotatedFor.put(classname, a[a.length-3]);
+            //    }
+            //  }
+            //  return null;
+            // }
+
+            Void visit(AElement el) {
+              if (el != null) { el.accept(this, null); }
+              return null;
+            }
+
+            <T, E extends AElement> Void visitMap(Map<T, E> map) {
+              if (map != null) {
+                for (E el : map.values()) { visit(el); }
+              }
+              return null;
+            }
+
+            @Override
+            public Void visitAnnotationDef(AnnotationDef d, Void v) {
+              // String[] a = d.name.split("\\.");
+              // if (a.length > 2 && a[a.length-2].matches("quals?")) {
+              //  String s = a[a.length-1];
+              //  annoPkgs.put(s, d.name.substring(0));
+              // }
+              return null;  // process(d);
+            }
+
+            @Override
+            public Void visitBlock(ABlock el, Void v) {
+              visitMap(el.locals);
+              return visitExpression(el, v);
+            }
+
+            @Override
+            public Void visitClass(AClass el, Void v) {
+              visitMap(el.bounds);
+              visitMap(el.extendsImplements);
+              visitMap(el.instanceInits);
+              visitMap(el.staticInits);
+              visitMap(el.methods);
+              visitMap(el.fields);
+              visitMap(el.fieldInits);
+              return visitDeclaration(el, v);
+            }
+
+            @Override
+            public Void visitDeclaration(ADeclaration el, Void v) {
+              visitMap(el.insertAnnotations);
+              visitMap(el.insertTypecasts);
+              return visitElement(el, v);
+            }
+
+            @Override
+            public Void visitExpression(AExpression el, Void v) {
+              visitMap(el.calls);
+              visitMap(el.funs);
+              visitMap(el.instanceofs);
+              visitMap(el.news);
+              visitMap(el.refs);
+              visitMap(el.typecasts);
+              return visitElement(el, v);
+            }
+
+            @Override
+            public Void visitField(AField el, Void v) {
+              visit(el.init);
+              return visitDeclaration(el, v);
+            }
+
+            @Override
+            public Void visitMethod(AMethod el, Void v) {
+              visit(el.receiver);
+              visitMap(el.parameters);
+              visitMap(el.bounds);
+              visit(el.returnType);
+              visit(el.body);
+              visitMap(el.throwsException);
+              return visitDeclaration(el, v);
+            }
+
+            @Override
+            public Void visitTypeElement(ATypeElement el, Void v) {
+              visitMap(el.innerTypes);
+              return visitElement(el, v);
+            }
+
+            @Override
+            public Void visitTypeElementWithType(ATypeElementWithType el,
+                Void v) {
+              return visitTypeElement(el, v);
+            }
+
+            @Override
+            public Void visitElement(AElement el, Void v) {
+              visit(el.type);
+              return null;  // process(el);
+            }
+          }, null);
+        }
+      }
+
+      // add AnnotatedFor to each annotated class
+      AnnotationFieldType stringArray =
+          AnnotationFieldType.fromClass(new String[0].getClass(),
+              Collections.<String, AnnotationDef>emptyMap());
+      AnnotationDef afDef =
+          Annotations.createValueAnnotationDef("AnnotatedFor",
+              Collections.<Annotation>emptySet(), stringArray);
+      for (Map.Entry<String, Collection<String>> entry :
+          annotatedFor.asMap().entrySet()) {
+        String key = entry.getKey();
+        Collection<String> values = entry.getValue();
+        Annotation afAnno = new Annotation(afDef, Collections
+                .<String, Collection<String>>singletonMap("value", values));
+        scene.classes.vivify(key).tlAnnotationsHere.add(afAnno);
+      }
+      scene.prune();
+      annotatedFor.clear();  // for gc
+
+      try {
+        IndexFileWriter.write(scene, new PrintWriter(System.out, true));
+      } catch (SecurityException e) {
+        e.printStackTrace();
+        System.exit(1);
+      } catch (DefException e) {
+        e.printStackTrace();
+        System.exit(1);
+      }
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+}
diff --git a/scene-lib/src/annotations/toys/BalanceEnum.java b/scene-lib/src/annotations/toys/BalanceEnum.java
new file mode 100644
index 0000000..88ae533
--- /dev/null
+++ b/scene-lib/src/annotations/toys/BalanceEnum.java
@@ -0,0 +1,5 @@
+package annotations.toys;
+
+public enum BalanceEnum {
+    BALANCED, LEFT_HEAVY, RIGHT_HEAVY;
+}
diff --git a/scene-lib/src/annotations/toys/ClassTokenAnnotation.java b/scene-lib/src/annotations/toys/ClassTokenAnnotation.java
new file mode 100644
index 0000000..876846e
--- /dev/null
+++ b/scene-lib/src/annotations/toys/ClassTokenAnnotation.java
@@ -0,0 +1,5 @@
+package annotations.toys;
+
+public @interface ClassTokenAnnotation {
+    Class<?>[] favoriteClasses();
+}
diff --git a/scene-lib/src/annotations/toys/FancierAnnotation.java b/scene-lib/src/annotations/toys/FancierAnnotation.java
new file mode 100644
index 0000000..f67e27b
--- /dev/null
+++ b/scene-lib/src/annotations/toys/FancierAnnotation.java
@@ -0,0 +1,5 @@
+package annotations.toys;
+
+public @interface FancierAnnotation {
+    FancyAnnotation fa();
+}
diff --git a/scene-lib/src/annotations/toys/FancyAnnotation.java b/scene-lib/src/annotations/toys/FancyAnnotation.java
new file mode 100644
index 0000000..1b8033f
--- /dev/null
+++ b/scene-lib/src/annotations/toys/FancyAnnotation.java
@@ -0,0 +1,9 @@
+package annotations.toys;
+
+public @interface FancyAnnotation {
+    int myInt();
+
+    String left();
+
+    SimplerAnnotation[] friends();
+}
diff --git a/scene-lib/src/annotations/toys/SimplerAnnotation.java b/scene-lib/src/annotations/toys/SimplerAnnotation.java
new file mode 100644
index 0000000..7c97147
--- /dev/null
+++ b/scene-lib/src/annotations/toys/SimplerAnnotation.java
@@ -0,0 +1,13 @@
+package annotations.toys;
+
+import java.util.*;
+
+public @interface SimplerAnnotation {
+    BalanceEnum be();
+
+    int height();
+
+    int[] wrappedHeight();
+
+    Class<? super HashMap<String, String>> favoriteClass();
+}
diff --git a/scene-lib/src/annotations/toys/SimplerInterface.java b/scene-lib/src/annotations/toys/SimplerInterface.java
new file mode 100644
index 0000000..95a638e
--- /dev/null
+++ b/scene-lib/src/annotations/toys/SimplerInterface.java
@@ -0,0 +1,7 @@
+package annotations.toys;
+
+@ClassTokenAnnotation(favoriteClasses = { String.class, int.class, void.class,
+        int[].class, Object[][][].class })
+public interface SimplerInterface {
+    int myField = 1;
+}
diff --git a/scene-lib/src/annotations/toys/SubAnnotation.java b/scene-lib/src/annotations/toys/SubAnnotation.java
new file mode 100644
index 0000000..d2b3ef6
--- /dev/null
+++ b/scene-lib/src/annotations/toys/SubAnnotation.java
@@ -0,0 +1,5 @@
+package annotations.toys;
+
+public @interface SubAnnotation {
+    int[] value();
+}
diff --git a/scene-lib/src/annotations/toys/ValuesAnnotation.java b/scene-lib/src/annotations/toys/ValuesAnnotation.java
new file mode 100644
index 0000000..2cdef6d
--- /dev/null
+++ b/scene-lib/src/annotations/toys/ValuesAnnotation.java
@@ -0,0 +1,36 @@
+package annotations.toys;
+
+@ValuesAnnotation(
+        B = -128,
+        S = -32768,
+        I = -2147483648,
+        J = -3141592653589793238L,
+        F = 0.1e-5f,
+        D = 9.8e99,
+        Z = true,
+        C = '\'',
+        Ltok = java.util.Map.Entry.class,
+        string = "\"yfwq\" yfwq \'\n\t\\",
+        arrayI = {1, 2},
+        arrayI2 = {},
+        balEnum = BalanceEnum.BALANCED,
+        subann = @SubAnnotation({3, 4}),
+        arraySubann = {@SubAnnotation({}), @SubAnnotation({5})}
+        )
+public @interface ValuesAnnotation {
+    byte B();
+    short S();
+    int I();
+    long J();
+    float F();
+    double D();
+    boolean Z();
+    char C();
+    Class<?> Ltok();
+    String string();
+    int[] arrayI();
+    int[] arrayI2();
+    BalanceEnum balEnum();
+    SubAnnotation subann();
+    SubAnnotation[] arraySubann();
+}
diff --git a/scene-lib/src/annotations/toys/package-info.java b/scene-lib/src/annotations/toys/package-info.java
new file mode 100644
index 0000000..b38de37
--- /dev/null
+++ b/scene-lib/src/annotations/toys/package-info.java
@@ -0,0 +1,2 @@
+@SimplerAnnotation(be = BalanceEnum.BALANCED, height = 0, wrappedHeight = { 1 }, favoriteClass = Object.class)
+package annotations.toys;
diff --git a/scene-lib/src/annotations/util/AbstractClassVisitor.java b/scene-lib/src/annotations/util/AbstractClassVisitor.java
new file mode 100644
index 0000000..ed6ec7a
--- /dev/null
+++ b/scene-lib/src/annotations/util/AbstractClassVisitor.java
@@ -0,0 +1,348 @@
+package annotations.util;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.TypePath;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+public class AbstractClassVisitor implements ClassVisitor {
+  @Override
+  public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+      boolean visible, boolean inCode) {
+    return new TypeAnnotationVisitor() {
+      @Override
+      public void visit(String name, Object value) {}
+      @Override
+      public void visitEnum(String name, String desc, String value) {}
+      @Override
+      public AnnotationVisitor visitAnnotation(String name,
+          String desc) {
+        return null;
+      }
+      @Override
+      public AnnotationVisitor visitArray(String name) {
+        return null;
+      }
+      @Override
+      public void visitEnd() {}
+      @Override
+      public void visitXTargetType(int target_type) {}
+      @Override
+      public void visitXOffset(int offset) {}
+      @Override
+      public void visitXLocationLength(int location_length) {}
+      @Override
+      public void visitXLocation(TypePathEntry location) {}
+      @Override
+      public void visitXNumEntries(int num_entries) {}
+      @Override
+      public void visitXStartPc(int start_pc) {}
+      @Override
+      public void visitXLength(int length) {}
+      @Override
+      public void visitXIndex(int index) {}
+      @Override
+      public void visitXParamIndex(int param_index) {}
+      @Override
+      public void visitXBoundIndex(int bound_index) {}
+      @Override
+      public void visitXTypeIndex(int type_index) {}
+      @Override
+      public void visitXExceptionIndex(int exception_index) {}
+      @Override
+      public void visitXNameAndArgsSize() {}
+    };
+  }
+
+  @Override
+  public void visit(int version, int access, String name,
+      String signature, String superName, String[] interfaces) {
+  }
+
+  @Override
+  public void visitSource(String source, String debug) {}
+
+  @Override
+  public void visitOuterClass(String owner, String name, String desc) {}
+
+  @Override
+  public AnnotationVisitor visitAnnotation(String desc,
+      boolean visible) {
+    return new AnnotationVisitor() {
+      @Override
+      public void visit(String name, Object value) {}
+      @Override
+      public void visitEnum(String name, String desc, String value) {}
+      @Override
+      public AnnotationVisitor visitAnnotation(String name,
+          String desc) {
+        return null;
+      }
+      @Override
+      public AnnotationVisitor visitArray(String name) {
+        return null;
+      }
+      @Override
+      public void visitEnd() {}
+    };
+  }
+
+  @Override
+  public void visitAttribute(Attribute attr) {}
+
+  @Override
+  public void visitInnerClass(String name,
+      String outerName, String innerName, int access) {
+  }
+
+  @Override
+  public FieldVisitor visitField(int access, String name, String desc,
+      String signature, Object value) {
+    return new FieldVisitor() {
+      @Override
+      public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+          boolean visible, boolean inCode) {
+        return new TypeAnnotationVisitor() {
+          @Override
+          public void visit(String name, Object value) {}
+          @Override
+          public void visitEnum(String name, String desc, String value) {}
+          @Override
+          public AnnotationVisitor visitAnnotation(String name,
+              String desc) {
+            return null;
+          }
+          @Override
+          public AnnotationVisitor visitArray(String name) {
+            return null;
+          }
+          @Override
+          public void visitEnd() {}
+          @Override
+          public void visitXTargetType(int target_type) {}
+          @Override
+          public void visitXOffset(int offset) {}
+          @Override
+          public void visitXLocationLength(int location_length) {}
+          @Override
+          public void visitXLocation(TypePathEntry location) {}
+          @Override
+          public void visitXNumEntries(int num_entries) {}
+          @Override
+          public void visitXStartPc(int start_pc) {}
+          @Override
+          public void visitXLength(int length) {}
+          @Override
+          public void visitXIndex(int index) {}
+          @Override
+          public void visitXParamIndex(int param_index) {}
+          @Override
+          public void visitXBoundIndex(int bound_index) {}
+          @Override
+          public void visitXTypeIndex(int type_index) {}
+          @Override
+          public void visitXExceptionIndex(int exception_index) {}
+          @Override
+          public void visitXNameAndArgsSize() {}
+        };
+      }
+      @Override
+      public AnnotationVisitor visitAnnotation(String desc,
+          boolean visible) {
+        return null;
+      }
+      @Override
+      public void visitAttribute(Attribute attr) {}
+      @Override
+      public void visitEnd() {}
+    };
+  }
+
+  @Override
+  public MethodVisitor visitMethod(int access, String name, String desc,
+      String signature, String[] exceptions) {
+    return new MethodVisitor() {
+      @Override
+      public TypeAnnotationVisitor visitTypeAnnotation(String desc,
+          boolean visible, boolean inCode) {
+        return new TypeAnnotationVisitor() {
+          @Override
+          public void visit(String name, Object value) {}
+          @Override
+          public void visitEnum(String name, String desc, String value) {}
+          @Override
+          public AnnotationVisitor visitAnnotation(String name,
+              String desc) {
+            return null;
+          }
+          @Override
+          public AnnotationVisitor visitArray(String name) {
+            return null;
+          }
+          @Override
+          public void visitEnd() {}
+          @Override
+          public void visitXTargetType(int target_type) {}
+          @Override
+          public void visitXOffset(int offset) {}
+          @Override
+          public void visitXLocationLength(int location_length) {}
+          @Override
+          public void visitXLocation(TypePathEntry location) {}
+          @Override
+          public void visitXNumEntries(int num_entries) {}
+          @Override
+          public void visitXStartPc(int start_pc) {}
+          @Override
+          public void visitXLength(int length) {}
+          @Override
+          public void visitXIndex(int index) {}
+          @Override
+          public void visitXParamIndex(int param_index) {}
+          @Override
+          public void visitXBoundIndex(int bound_index) {}
+          @Override
+          public void visitXTypeIndex(int type_index) {}
+          @Override
+          public void visitXExceptionIndex(int exception_index) {}
+          @Override
+          public void visitXNameAndArgsSize() {}
+        };
+      }
+      @Override
+      public AnnotationVisitor visitAnnotationDefault() {
+        return new AnnotationVisitor() {
+          @Override
+          public void visit(String name, Object value) {}
+          @Override
+          public void visitEnum(String name, String desc, String value) {}
+          @Override
+          public AnnotationVisitor visitAnnotation(String name,
+              String desc) {
+            return null;
+          }
+          @Override
+          public AnnotationVisitor visitArray(String name) {
+            return null;
+          }
+          @Override
+          public void visitEnd() {}
+        };
+      }
+      @Override
+      public AnnotationVisitor visitAnnotation(String desc,
+          boolean visible) {
+        return null;
+      }
+      @Override
+      public AnnotationVisitor visitParameterAnnotation(int parameter,
+          String desc, boolean visible) {
+        return new AnnotationVisitor() {
+          @Override
+          public void visit(String name, Object value) {}
+          @Override
+          public void visitEnum(String name, String desc, String value) {}
+          @Override
+          public AnnotationVisitor visitAnnotation(String name,
+              String desc) {
+            return null;
+          }
+          @Override
+          public AnnotationVisitor visitArray(String name) {
+            return null;
+          }
+          @Override
+          public void visitEnd() {}
+        };
+      }
+      @Override
+      public void visitAttribute(Attribute attr) {}
+      @Override
+      public void visitCode() {}
+      @Override
+      public void visitInsn(int opcode) {}
+      @Override
+      public void visitIntInsn(int opcode, int operand) {}
+      @Override
+      public void visitVarInsn(int opcode, int var) {}
+      @Override
+      public void visitTypeInsn(int opcode, String desc) {}
+      @Override
+      public void visitFieldInsn(int opcode, String owner, String name,
+          String desc) {
+      }
+      @Override
+      public void visitMethodInsn(int opcode, String owner, String name,
+          String desc) {
+      }
+      @Override
+      public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
+          Object... bsmArgs) {
+      }
+      @Override
+      public void visitJumpInsn(int opcode, Label label) {
+      }
+      @Override
+      public void visitLabel(Label label) {}
+      @Override
+      public void visitLdcInsn(Object cst) {}
+      @Override
+      public void visitIincInsn(int var, int increment) {}
+      @Override
+      public void visitTableSwitchInsn(int min, int max, Label dflt,
+          Label[] labels) {
+      }
+      @Override
+      public void visitLookupSwitchInsn(Label dflt, int[] keys,
+          Label[] labels) {
+      }
+      @Override
+      public void visitMultiANewArrayInsn(String desc, int dims) {}
+      @Override
+      public AnnotationVisitor visitInsnAnnotation(int typeRef,
+          TypePath typePath, String desc, boolean visible) {
+        return new AnnotationVisitor() {
+          @Override
+          public void visit(String name, Object value) {}
+          @Override
+          public void visitEnum(String name, String desc, String value) {}
+          @Override
+          public AnnotationVisitor visitAnnotation(String name,
+              String desc) {
+            return null;
+          }
+          @Override
+          public AnnotationVisitor visitArray(String name) {
+            return null;
+          }
+          @Override
+          public void visitEnd() {}
+        };
+      }
+      @Override
+      public void visitTryCatchBlock(Label start, Label end,
+          Label handler, String type) {
+      }
+      @Override
+      public void visitLocalVariable(String name, String desc,
+          String signature, Label start, Label end, int index) {
+      }
+      @Override
+      public void visitLineNumber(int line, Label start) {}
+      @Override
+      public void visitMaxs(int maxStack, int maxLocals) {}
+      @Override
+      public void visitEnd() {}
+    };
+  }
+
+  @Override
+  public void visitEnd() {}
+}
diff --git a/scene-lib/src/annotations/util/EqualByStringRepresentation.java b/scene-lib/src/annotations/util/EqualByStringRepresentation.java
new file mode 100644
index 0000000..1e12b86
--- /dev/null
+++ b/scene-lib/src/annotations/util/EqualByStringRepresentation.java
@@ -0,0 +1,36 @@
+package annotations.util;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * {@link EqualByStringRepresentation} is a "mix-in" class for objects that are
+ * equal if and only if their {@link #toString toString} representations are
+ * equal.  {@link EqualByStringRepresentation} provides implementations of
+ * {@link #equals} and {@link #hashCode} in terms of {@link #toString}.
+ */
+public abstract class EqualByStringRepresentation {
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public abstract String toString();
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final boolean equals(Object that) {
+        return that != null && this.getClass() == that.getClass()
+                && this.toString().equals(that.toString());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final int hashCode() {
+        return toString().hashCode();
+    }
+}
diff --git a/scene-lib/src/annotations/util/Hasher.java b/scene-lib/src/annotations/util/Hasher.java
new file mode 100644
index 0000000..29b4e5e
--- /dev/null
+++ b/scene-lib/src/annotations/util/Hasher.java
@@ -0,0 +1,27 @@
+package annotations.util;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A simple class to mash a lot of data into a hash code for use in
+ * implementing {@link Object#hashCode}.  The mashing algorithm currently is
+ * not very good; the advantage of a {@link Hasher} class is that an
+ * improvement to its mashing algorithm benefits all classes that use it.
+ */
+public final class Hasher {
+    /**
+     * The calculated hash code for the data that has been contributed so far.
+     */
+    public int hash = 0;
+
+    private static final int SHIFT = 5;
+
+    /**
+     * Contributes the data <code>x</code> to the calculation of the hash code.
+     */
+    public void mash(int x) {
+        hash = ((hash << SHIFT) | (hash >> (32 - SHIFT))) ^ x;
+    }
+}
diff --git a/scene-lib/src/annotations/util/JVMNames.java b/scene-lib/src/annotations/util/JVMNames.java
new file mode 100644
index 0000000..6449d89
--- /dev/null
+++ b/scene-lib/src/annotations/util/JVMNames.java
@@ -0,0 +1,148 @@
+package annotations.util;
+
+import com.sun.source.tree.ArrayTypeTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.Tree;
+import com.sun.tools.javac.code.Type;
+import com.sun.tools.javac.code.TypeTag;
+import com.sun.tools.javac.tree.JCTree.JCExpression;
+import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
+import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
+import com.sun.tools.javac.util.List;
+
+import plume.UtilMDE;
+
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * Class to generate class formatted names from Trees.
+ *
+ * @author mcarthur
+ */
+public class JVMNames {
+
+    /**
+     * Converts a MethodTree into a jvml format method signature.
+     * There is probably an API to do this, but I couldn't find it.
+     *
+     * @param methodTree the tree to convert
+     * @return a String signature of methodTree in jvml format
+     */
+    public static String getJVMMethodName(MethodTree methodTree) {
+        ExecutableElement methodElement = ((JCMethodDecl) methodTree).sym;
+        StringBuilder builder = new StringBuilder();
+        String returnTypeStr;
+        builder.append(methodTree.getName());
+        builder.append("(");
+
+        if (methodElement == null) {
+            // use source AST in lieu of symbol table
+            List<JCVariableDecl> params = ((JCMethodDecl) methodTree).params;
+            JCVariableDecl param = params.head;
+            JCExpression typeTree = ((JCMethodDecl) methodTree).restype;
+            returnTypeStr = treeToJVMLString(typeTree);
+            while (param != null) {
+                builder.append(treeToJVMLString(param.vartype));
+                params = params.tail;
+                param = params.head;
+            }
+        } else {
+            TypeMirror returnType = methodElement.getReturnType();
+            returnTypeStr = typeToJvmlString((Type)returnType);
+            for (VariableElement ve : methodElement.getParameters()) {
+                Type vt = (Type) ve.asType();
+                if (vt.getTag() == TypeTag.TYPEVAR) {
+                    vt = vt.getUpperBound();
+                }
+                builder.append(typeToJvmlString(vt));
+            }
+        }
+        builder.append(")");
+        builder.append(returnTypeStr);
+        return builder.toString();
+    }
+
+    /**
+     * Converts a method element into a jvml format method signature.
+     * There is probably an API to do this, but I couldn't find it.
+     *
+     * @param methodElement the method element to convert
+     * @return a String signature of methodElement in jvml format
+     */
+    public static String getJVMMethodName(ExecutableElement methodElement) {
+        StringBuilder builder = new StringBuilder();
+        String returnTypeStr;
+        builder.append(methodElement.getSimpleName());
+        builder.append("(");
+        TypeMirror returnType = methodElement.getReturnType();
+        returnTypeStr = typeToJvmlString((Type)returnType);
+        for (VariableElement ve : methodElement.getParameters()) {
+            Type vt = (Type) ve.asType();
+            if (vt.getTag() == TypeTag.TYPEVAR) {
+                vt = vt.getUpperBound();
+            }
+            builder.append(typeToJvmlString(vt));
+        }
+        builder.append(")");
+        builder.append(returnTypeStr);
+        return builder.toString();
+    }
+
+    /**
+     * Create a JVML string for a type.
+     * Uses {@link UtilMDE#binaryNameToFieldDescriptor(String)}
+     *
+     * Array strings are built by recursively converting the component type.
+     *
+     * @param type the Type to convert to JVML
+     * @return the JVML representation of type
+     */
+    public static String typeToJvmlString(Type type) {
+        if (type.getKind() == TypeKind.ARRAY) {
+            return "[" + typeToJvmlString((Type) ((ArrayType) type).getComponentType());
+        } else if (type.getKind() == TypeKind.INTERSECTION) {
+            // replace w/erasure (== erasure of 1st conjunct)
+            return typeToJvmlString(type.tsym.erasure_field);
+        } else if (type.getKind() == TypeKind.VOID) {
+            return "V";  // special case since UtilMDE doesn't handle void
+        } else {
+            return UtilMDE.binaryNameToFieldDescriptor(type.tsym.flatName().toString());
+        }
+    }
+
+    /**
+     * Create a JVML string for an AST node representing a type.
+     *
+     * @param typeTree a Tree representing a type
+     * @return the JVML representation of type
+     */
+    private static String treeToJVMLString(Tree typeTree) {
+        StringBuilder builder = new StringBuilder();
+        treeToJVMLString(typeTree, builder);
+        return builder.toString();
+    }
+
+    private static void treeToJVMLString(Tree typeTree, StringBuilder builder) {
+        // FIXME: not robust in presence of comments
+        switch (typeTree.getKind()) {
+        case ARRAY_TYPE:
+            builder.append('[');
+            treeToJVMLString(((ArrayTypeTree) typeTree).getType(), builder);
+            break;
+        default:
+            String str = typeTree.toString();
+            builder.append("void".equals(str) ? "V"
+                : UtilMDE.binaryNameToFieldDescriptor(typeTree.toString()));
+            break;
+        }
+    }
+
+    public static String jvmlStringToJavaTypeString(String str) {
+        return str.equals("V") ? "void"
+                : UtilMDE.fieldDescriptorToBinaryName(str);
+    }
+}
diff --git a/scene-lib/src/annotations/util/PersistentStack.java b/scene-lib/src/annotations/util/PersistentStack.java
new file mode 100644
index 0000000..f04fb8b
--- /dev/null
+++ b/scene-lib/src/annotations/util/PersistentStack.java
@@ -0,0 +1,15 @@
+package annotations.util;
+
+/**
+ * Stack that creates new stacks rather than modify data in place.
+ *
+ * @author dbro
+ * @param <E> type of stack elements
+ */
+public interface PersistentStack<E> {
+  public boolean isEmpty();
+  public E peek();
+  public PersistentStack<E> pop();
+  public PersistentStack<E> push(E elem);
+  public int size();
+}
diff --git a/scene-lib/src/annotations/util/SceneOps.java b/scene-lib/src/annotations/util/SceneOps.java
new file mode 100644
index 0000000..5ea448b
--- /dev/null
+++ b/scene-lib/src/annotations/util/SceneOps.java
@@ -0,0 +1,380 @@
+package annotations.util;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Map;
+import java.util.Set;
+
+import com.sun.tools.javac.util.Pair;
+
+import annotations.el.ABlock;
+import annotations.el.AClass;
+import annotations.el.ADeclaration;
+import annotations.el.AElement;
+import annotations.el.AExpression;
+import annotations.el.AField;
+import annotations.el.AMethod;
+import annotations.el.AScene;
+import annotations.el.ATypeElement;
+import annotations.el.ATypeElementWithType;
+import annotations.el.AnnotationDef;
+import annotations.el.DefException;
+import annotations.el.ElementVisitor;
+import annotations.io.IndexFileParser;
+import annotations.io.IndexFileWriter;
+import annotations.util.coll.VivifyingMap;
+
+/**
+ * Algebraic operations on scenes.
+ *
+ * Also includes a {@link #main(String[])} method that lets these
+ * operations be performed from the command line.
+ *
+ * @author dbro
+ */
+public class SceneOps {
+  private SceneOps() {}
+
+  /**
+   * Run an operation on a subcommand-specific number of JAIFs.
+   * Currently the only available subcommand is "diff", which must be
+   * the first of three arguments, followed in order by the "minuend"
+   * and the "subtrahend" (see {@link #diff(AScene, AScene)}).  If
+   * successful, the diff subcommand writes the scene it calculates to
+   * {@link System#out}.
+   *
+   * @throws IOException
+   */
+  public static void main(String[] args) throws IOException {
+    if (args.length != 3 || !"diff".equals(args[0])) {
+      System.err.println(
+          "usage: java annotations.util.SceneOps diff first.jaif second.jaif");
+      System.exit(1);
+    }
+
+    AScene s1 = new AScene();
+    AScene s2 = new AScene();
+
+    try {
+      IndexFileParser.parseFile(args[1], s1);
+      IndexFileParser.parseFile(args[2], s2);
+      AScene diff = diff(s1, s2);
+
+      try (Writer w = new PrintWriter(System.out)) {
+        IndexFileWriter.write(diff, w);
+      } catch (DefException e) {
+        exitWithException(e);
+      }
+    } catch (IOException e) {
+      exitWithException(e);
+    }
+  }
+
+  /**
+   * Compute the difference of two scenes, that is, a scene containing
+   * all and only those insertion specifications that exist in the first
+   * but not in the second.
+   *
+   * @param s1 the "minuend"
+   * @param s2 the "subtrahend"
+   * @return s1 - s2 ("set difference")
+   */
+  public static AScene diff(AScene s1, AScene s2) {
+    AScene diff = new AScene();
+    new DiffVisitor().visitScene(s1, s2, diff);
+    diff.prune();
+    return diff;
+  }
+
+  /** Print stack trace (for debugging) and exit with return code 1. */
+  private static void exitWithException(Exception e) {
+    e.printStackTrace();
+    System.exit(1);
+  }
+
+  // TODO: integrate into scene-lib test suite
+  public static void testDiffEmpties() {
+    assert new AScene().equals(diff(new AScene(), new AScene()));
+  }
+  /** Test that X-X=0, for several scenes X. */
+  public static void testDiffSame() throws IOException {
+    String dirname =
+        "test/annotations/tests/classfile/cases";
+    String[] testcases = { "ClassEmpty", "ClassNonEmpty", "FieldGeneric",
+        "FieldSimple", "LocalVariableGenericArray", "MethodReceiver",
+        "MethodReturnTypeGenericArray", "ObjectCreationGenericArray",
+        "ObjectCreation", "TypecastGenericArray", "Typecast" };
+    AScene emptyScene = new AScene();
+    for (String testcase : testcases) {
+      AScene scene1 = new AScene();
+      AScene scene2 = new AScene();
+      String filename = dirname+"/Test"+testcase+".jaif";
+      IndexFileParser.parseFile(filename, scene1);
+      IndexFileParser.parseFile(filename, scene2);
+      assert emptyScene.equals(diff(scene1, scene1));
+      assert emptyScene.equals(diff(scene1, scene2));
+    }
+  }
+}
+
+/**
+ * Visitor for calculating "set difference" of scenes.
+ * Visitor methods fill in a scene instead of returning one because an
+ * {@link AElement} can be created only inside an {@link AScene}.
+ *
+ * @author dbro
+ */
+class DiffVisitor
+implements ElementVisitor<Void, Pair<AElement, AElement>> {
+
+  /**
+   * Adds all annotations that are in {@code minuend} but not in
+   * {@code subtrahend} to {@code difference}.
+   */
+  public void visitScene(AScene minuend, AScene subtrahend,
+      AScene difference) {
+    visitElements(minuend.packages, subtrahend.packages,
+        difference.packages);
+    diff(minuend.imports, subtrahend.imports, difference.imports);
+    visitElements(minuend.classes, subtrahend.classes,
+        difference.classes);
+  }
+
+  // Never used, as annotations and definitions don't get duplicated.
+  @Override
+  public Void visitAnnotationDef(AnnotationDef minuend,
+      Pair<AElement, AElement> eltPair) {
+    throw new IllegalStateException(
+        "BUG: DiffVisitor.visitAnnotationDef invoked");
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitBlock(ABlock minuend, Pair<AElement, AElement> eltPair) {
+    ABlock subtrahend = (ABlock) eltPair.fst;
+    ABlock difference = (ABlock) eltPair.snd;
+    visitElements(minuend.locals, subtrahend.locals, difference.locals);
+    return visitExpression(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitClass(AClass minuend, Pair<AElement, AElement> eltPair) {
+    AClass subtrahend = (AClass) eltPair.fst;
+    AClass difference = (AClass) eltPair.snd;
+    visitElements(minuend.bounds, subtrahend.bounds, difference.bounds);
+    visitElements(minuend.extendsImplements,
+        subtrahend.extendsImplements, difference.extendsImplements);
+    visitElements(minuend.methods, subtrahend.methods,
+        difference.methods);
+    visitElements(minuend.staticInits, subtrahend.staticInits,
+        difference.staticInits);
+    visitElements(minuend.instanceInits, subtrahend.instanceInits,
+        difference.instanceInits);
+    visitElements(minuend.fields, subtrahend.fields, difference.fields);
+    visitElements(minuend.fieldInits, subtrahend.fieldInits,
+        difference.fieldInits);
+    return visitDeclaration(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitDeclaration(ADeclaration minuend,
+      Pair<AElement, AElement> eltPair) {
+    ADeclaration subtrahend = (ADeclaration) eltPair.fst;
+    ADeclaration difference = (ADeclaration) eltPair.snd;
+    visitElements(minuend.insertAnnotations,
+        subtrahend.insertAnnotations, difference.insertAnnotations);
+    visitElements(minuend.insertTypecasts, subtrahend.insertTypecasts,
+        difference.insertTypecasts);
+    return visitElement(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitExpression(AExpression minuend,
+      Pair<AElement, AElement> eltPair) {
+    AExpression subtrahend = (AExpression) eltPair.fst;
+    AExpression difference = (AExpression) eltPair.snd;
+    visitElements(minuend.typecasts, subtrahend.typecasts,
+        difference.typecasts);
+    visitElements(minuend.instanceofs, subtrahend.instanceofs,
+        difference.instanceofs);
+    visitElements(minuend.news, subtrahend.news, difference.news);
+    visitElements(minuend.calls, subtrahend.calls, difference.calls);
+    visitElements(minuend.refs, subtrahend.refs, difference.refs);
+    visitElements(minuend.funs, subtrahend.funs, difference.funs);
+    return visitElement(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitField(AField minuend, Pair<AElement, AElement> eltPair) {
+    return visitDeclaration(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitMethod(AMethod minuend,
+      Pair<AElement, AElement> eltPair) {
+    AMethod subtrahend = (AMethod) eltPair.fst;
+    AMethod difference = (AMethod) eltPair.snd;
+    visitElements(minuend.bounds, subtrahend.bounds, difference.bounds);
+    visitElements(minuend.parameters, subtrahend.parameters,
+        difference.parameters);
+    visitElements(minuend.throwsException, subtrahend.throwsException,
+        difference.throwsException);
+    visitElements(minuend.parameters, subtrahend.parameters,
+        difference.parameters);
+    visitBlock(minuend.body,
+        elemPair(subtrahend.body, difference.body));
+    if (minuend.returnType != null) {
+      minuend.returnType.accept(this,
+          elemPair(subtrahend.returnType, difference.returnType));
+    }
+    if (minuend.receiver != null) {
+      minuend.receiver.accept(this,
+          elemPair(subtrahend.receiver, difference.receiver));
+    }
+    return visitDeclaration(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitTypeElement(ATypeElement minuend,
+      Pair<AElement, AElement> eltPair) {
+    ATypeElement subtrahend = (ATypeElement) eltPair.fst;
+    ATypeElement difference = (ATypeElement) eltPair.snd;
+    visitElements(minuend.innerTypes, subtrahend.innerTypes,
+        difference.innerTypes);
+    return visitElement(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitTypeElementWithType(ATypeElementWithType minuend,
+      Pair<AElement, AElement> eltPair) {
+    return visitTypeElement(minuend, eltPair);
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  @Override
+  public Void visitElement(AElement minuend,
+      Pair<AElement, AElement> eltPair) {
+    AElement subtrahend = eltPair.fst;
+    AElement difference = eltPair.snd;
+    diff(minuend.tlAnnotationsHere, subtrahend.tlAnnotationsHere,
+        difference.tlAnnotationsHere);
+    if (minuend.type != null) {
+      AElement stype = subtrahend.type;
+      AElement dtype = difference.type;
+      minuend.type.accept(this, elemPair(stype, dtype));
+    }
+    return null;
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and first component
+   * of {@code eltPair}, adding results to second component of {@code eltPair}.
+   */
+  private <K, V extends AElement>
+  void visitElements(VivifyingMap<K, V> minuend,
+      VivifyingMap<K, V> subtrahend, VivifyingMap<K, V> difference) {
+    if (minuend != null) {
+      for (Map.Entry<K, V> e : minuend.entrySet()) {
+        K key = e.getKey();
+        V mval = e.getValue();
+        V sval = subtrahend.get(key);
+        if (sval == null) {
+          difference.put(key, mval);
+        } else {
+          mval.accept(this, elemPair(sval, difference.vivify(key)));
+        }
+      }
+    }
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and
+   * {@code subtrahend}, adding the result to {@code difference}.
+   */
+  private static <T> void diff(Set<T> minuend, Set<T> subtrahend,
+      Set<T> difference) {
+    if (minuend != null) {
+      for (T t : minuend) {
+        if (!subtrahend.contains(t)) {
+          difference.add(t);
+        }
+      }
+    }
+  }
+
+  /**
+   * Calculates difference between {@code minuend} and
+   * {@code subtrahend}, adding the results to {@code difference}.
+   */
+  private static <K, V> void diff(Map<K, Set<V>> minuend,
+      Map<K, Set<V>> subtrahend, Map<K, Set<V>> difference) {
+    if (minuend != null) {
+      for (K key : minuend.keySet()) {
+        Set<V> mval = minuend.get(key);
+        Set<V> sval = subtrahend.get(key);
+        if (sval == null) {
+          difference.put(key, mval);
+        } else if (!sval.equals(mval)) {
+          try {
+            @SuppressWarnings("unchecked")
+            Set<V> set = (Set<V>) sval.getClass().newInstance();
+            diff(mval, sval, set);
+            if (!set.isEmpty()) {
+              difference.put(key, set);
+            }
+          } catch (InstantiationException e) {
+            e.printStackTrace();
+            System.exit(1);
+          } catch (IllegalAccessException e) {
+            e.printStackTrace();
+            System.exit(1);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Convenience method for ensuring returned {@link Pair} is of the
+   * most general type.
+   */
+  private Pair<AElement, AElement> elemPair(AElement stype,
+      AElement dtype) {
+    return Pair.of(stype, dtype);
+  }
+}
diff --git a/scene-lib/src/annotations/util/Strings.java b/scene-lib/src/annotations/util/Strings.java
new file mode 100644
index 0000000..d2de2ed
--- /dev/null
+++ b/scene-lib/src/annotations/util/Strings.java
@@ -0,0 +1,45 @@
+package annotations.util;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * {@link Strings} provides useful static methods related to strings.
+ */
+public abstract class Strings {
+    private Strings() {}
+
+    /**
+     * Returns the given string, escaped and quoted according to Java
+     * conventions.  Currently, only newlines, backslashes, tabs, and
+     * single and double quotes are escaped.  Perhaps nonprinting
+     * characters should also be escaped somehow.
+     */
+    public static String escape(String in) {
+        StringBuilder out = new StringBuilder("\"");
+        for (int pos = 0; pos < in.length(); pos++) {
+            switch (in.charAt(pos)) {
+            case '\n':
+                out.append("\\n");
+                break;
+            case '\t':
+                out.append("\\t");
+                break;
+            case '\\':
+                out.append("\\\\");
+                break;
+            case '\'':
+                out.append("\\\'");
+                break;
+            case '\"':
+                out.append("\\\"");
+                break;
+            default:
+                out.append(in.charAt(pos));
+            }
+        }
+        out.append('\"');
+        return out.toString();
+    }
+}
diff --git a/scene-lib/src/annotations/util/coll/KeyedSet.java b/scene-lib/src/annotations/util/coll/KeyedSet.java
new file mode 100644
index 0000000..7da5ff7
--- /dev/null
+++ b/scene-lib/src/annotations/util/coll/KeyedSet.java
@@ -0,0 +1,93 @@
+package annotations.util.coll;
+
+import java.util.Set;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A <code>KeyedSet</code> is a set whose members have distinct <em>keys</em>
+ * and can be looked up by key. A {@link Keyer} provides keys for the elements.
+ * It is forbidden for an element's key to change while the element is in the
+ * set.
+ */
+public interface KeyedSet<K, V> extends Set<V> {
+    /**
+     * Returns the <code>Keyer</code> that this <code>KeyedSet</code> uses
+     * to obtain keys for elements.
+     *
+     * @return the <code>Keyer</code> that this <code>KeyedSet</code> uses
+     *         to obtain keys for elements
+     */
+    public abstract Keyer<? extends K, ? super V> getKeyer();
+
+    /**
+     * Calls
+     *
+     * @{link #add(V, int, int) add(v, THROW_EXCEPTION, IGNORE)} and returns
+     *        true if <code>v</code> was added.
+     * @param v
+     *            the object to be added
+     * @return <code>true</code> if <code>v</code> was added
+     */
+    // public abstract boolean add(V v); // causes "ambiguities" for some
+    // strange reason
+    /**
+     * Conflict/equal behavior that does nothing.
+     */
+    public static final int IGNORE = -1;
+
+    /**
+     * Conflict/equal behavior that throws an {@link IllegalStateException}.
+     */
+    public static final int THROW_EXCEPTION = 0;
+
+    /**
+     * Conflict/equal behavior that removes the existing object and then adds
+     * the new object.
+     */
+    public static final int REPLACE = 1;
+
+    /**
+     * Adds <code>v</code> to this <code>KeyedSet</code>; this set's
+     * <code>Keyer</code> will be called once to fetch <code>v</code>'s
+     * key. If an object equal to <code>v</code> is already present in this
+     * <code>KeyedSet</code>, then this method carries out the
+     * <code>equalBehavior</code> and returns the existing object. Otherwise,
+     * if an object having a key equal to <code>v</code>'s is already present
+     * in this <code>KeyedSet</code>, then this method carries out the
+     * <code>conflictBehavior</code> and returns the existing object.
+     * Otherwise, this method adds <code>v</code> to this
+     * <code>KeyedSet</code> and returns null.
+     *
+     * @param v
+     *            the object to be added
+     * @param conflictBehavior
+     *            specifies what to do if <code>v</code>'s key equals an
+     *            existing object's key
+     * @param equalBehavior
+     *            specifies what to do if <code>v</code> equals an existing
+     *            object
+     * @return the existing object whose key matched <code>v</code>'s, if any
+     */
+    public abstract V add(V v, int conflictBehavior, int equalBehavior);
+
+    /**
+     * Adds <code>v</code> to this <code>KeyedSet</code>, replacing and
+     * returning an existing object with the same key as <code>v</code> (if
+     * any).  The existing object is replaced with <code>v</code> even if it
+     * equals <code>v</code>.  If no existing object is replaced, null is
+     * returned.
+     */
+    public abstract V replace(V v);
+
+    /**
+     * Looks for and returns an element with key <code>k</code>, or
+     * <code>null</code> if none.
+     *
+     * @return the element with key <code>k</code>, or <code>null</code> if
+     *         none
+     */
+    public abstract V lookup(K k);
+}
diff --git a/scene-lib/src/annotations/util/coll/Keyer.java b/scene-lib/src/annotations/util/coll/Keyer.java
new file mode 100644
index 0000000..797085f
--- /dev/null
+++ b/scene-lib/src/annotations/util/coll/Keyer.java
@@ -0,0 +1,14 @@
+package annotations.util.coll;
+
+/**
+ * A {@link Keyer} supplies keys for the elements of a {@link KeyedSet}.
+ * @param <K> the key type
+ * @param <V> the element type
+ */
+public interface Keyer<K, V> {
+    /**
+     * Returns the key that this keyer wishes to assign to the element
+     * <code>v</code>.
+     */
+    public abstract K getKeyFor(V v);
+}
diff --git a/scene-lib/src/annotations/util/coll/LinkedHashKeyedSet.java b/scene-lib/src/annotations/util/coll/LinkedHashKeyedSet.java
new file mode 100644
index 0000000..9f00673
--- /dev/null
+++ b/scene-lib/src/annotations/util/coll/LinkedHashKeyedSet.java
@@ -0,0 +1,185 @@
+package annotations.util.coll;
+
+import java.util.*;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+/**
+ * A simple implementation of {@link KeyedSet} backed by an insertion-order
+ * {@link java.util.LinkedHashMap} and its
+ * {@link java.util.LinkedHashMap#values() value collection}.
+ */
+public class LinkedHashKeyedSet<K, V> extends AbstractSet<V> implements KeyedSet<K, V> {
+    private final Keyer<? extends K, ? super V> keyer;
+
+    private final Map<K, V> theMap = new LinkedHashMap<K, V>();
+
+    final Collection<V> theValues = theMap.values();
+
+    /**
+     * Constructs a {@link LinkedHashKeyedSet} that uses the given
+     * {@link Keyer} to obtain keys for elements.
+     */
+    public LinkedHashKeyedSet(Keyer<? extends K, ? super V> keyer) {
+        this.keyer = keyer;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int size() {
+        return theValues.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean contains(Object o) {
+        return theValues.contains(o);
+    }
+
+    private class KeyedSetIterator implements Iterator<V> {
+        private final Iterator<V> itr = theValues.iterator();
+
+        @Override
+        public boolean hasNext() {
+            return itr.hasNext();
+        }
+
+        @Override
+        public V next() {
+            return itr.next();
+        }
+
+        @Override
+        public void remove() {
+            itr.remove();
+        }
+
+        KeyedSetIterator() {
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Iterator<V> iterator() {
+        return new KeyedSetIterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object[] toArray() {
+        return theValues.toArray();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <T> T[] toArray(T[] a) {
+        return theValues.toArray(a);
+    }
+
+    private boolean checkAdd(int behavior, V old) {
+        switch (behavior) {
+        case REPLACE:
+            remove(old);
+            return true;
+        case IGNORE:
+            return false;
+        case THROW_EXCEPTION:
+            throw new IllegalStateException();
+        default:
+            throw new IllegalArgumentException();
+        }
+    }
+
+    private static boolean eq(Object x, Object y) {
+        return x == y || (x != null && x.equals(y));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public V add(V o, int conflictBehavior, int equalBehavior) {
+        K key = keyer.getKeyFor(o);
+        V old = theMap.get(key);
+        if (old == null
+                || (eq(o, old) ? checkAdd(equalBehavior, old) : checkAdd(
+                        conflictBehavior, old)))
+            theMap.put(key, o);
+        return old;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean add(V o) {
+        return add(o, THROW_EXCEPTION, IGNORE) == null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean remove(Object o) {
+        return theValues.remove(o);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean addAll(Collection<? extends V> c) {
+        boolean changed = false;
+        for (V o : c) {
+            changed |= add(o);
+        }
+        return changed;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void clear() {
+        theValues.clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Keyer<? extends K, ? super V> getKeyer() {
+        return keyer;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public V replace(V v) {
+        return theMap.put(keyer.getKeyFor(v), v);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public V lookup(K k) {
+        return theMap.get(k);
+    }
+
+    // Use inherited equals and hashCode algorithms because
+    // those of HashMap.Values are broken!
+}
diff --git a/scene-lib/src/annotations/util/coll/VivifyingMap.java b/scene-lib/src/annotations/util/coll/VivifyingMap.java
new file mode 100644
index 0000000..ad41c4d
--- /dev/null
+++ b/scene-lib/src/annotations/util/coll/VivifyingMap.java
@@ -0,0 +1,75 @@
+package annotations.util.coll;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * A {@link VivifyingMap} is a map that can create "empty" values on demand
+ * and prune "empty" values, for some definition of "empty".
+ */
+public abstract class VivifyingMap<K, V> extends WrapperMap<K, V> {
+    /**
+     * Constructs a new {@link VivifyingMap} backed by the given map.  All
+     * reads and writes to this {@link VivifyingMap} go through to the backing
+     * map.  However, since the {@link VivifyingMap} generally provides a
+     * superset of the functionality of the backing map, it is rarely useful to
+     * access the backing map directly; the parameter is given mainly so you
+     * can provide a new map of your favorite class ({@link java.util.HashMap},
+     * {@link java.util.LinkedHashMap}, etc.).
+     */
+    public VivifyingMap(Map<K, V> back) {
+        super(back);
+    }
+
+    /**
+     * Returns the value to which the specified key is mapped; if the key is
+     * not currently mapped to a value, a new, empty value is created, stored,
+     * and returned.
+     */
+    public V vivify(K k) {
+        if (containsKey(k)) {
+            return get(k);
+        } else {
+            V v = createValueFor(k);
+            put(k, v);
+            return v;
+        }
+    }
+
+    /**
+     * Prunes this map by deleting entries with empty values (i.e., entries
+     * that could be recreated by {@link #vivify} without information loss).
+     */
+    public boolean prune() {
+        boolean empty = true;
+        for (Iterator<Map.Entry<K, V>> ei
+                = entrySet().iterator(); ei.hasNext(); ) {
+            Map.Entry<K, V> e = ei.next();
+            boolean subEmpty = subPrune(e.getValue());
+            if (subEmpty) {
+                ei.remove();
+            } else {
+                empty = false;
+            }
+        }
+        return empty;
+    }
+
+    /**
+     * Returns a new, "empty" value to which the key <code>k</code> can be
+     * mapped; subclasses must implement.
+     */
+    protected abstract V createValueFor(K k);
+
+    /**
+     * Returns whether the given value is "empty" and thus may be discarded
+     * by {@link #prune}.  Before returning, {@link #subPrune} may carry out
+     * some sort of recursive pruning on the value; for example, if the value
+     * is another {@link VivifyingMap}, {@link #subPrune} could prune that map.
+     */
+    protected abstract boolean subPrune(V v);
+}
diff --git a/scene-lib/src/annotations/util/coll/WrapperMap.java b/scene-lib/src/annotations/util/coll/WrapperMap.java
new file mode 100644
index 0000000..860d42d
--- /dev/null
+++ b/scene-lib/src/annotations/util/coll/WrapperMap.java
@@ -0,0 +1,141 @@
+package annotations.util.coll;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.Nullable;
+*/
+
+/**
+ * A {@link WrapperMap} is a map all of whose methods delegate by default to
+ * those of a supplied {@linkplain #back backing map}.  Subclasses can add or
+ * override methods.  Compare to {@link java.io.FilterInputStream}.
+ */
+public class WrapperMap<K, V> implements Map<K, V> {
+    /**
+     * The backing map.
+     */
+    protected final Map<K, V> back;
+
+    /**
+     * Constructs a new {@link WrapperMap} with the given backing map.
+     */
+    protected WrapperMap(Map<K, V> back) {
+        this.back = back;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void clear() {
+        back.clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean containsKey(Object key) {
+        return back.containsKey(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean containsValue(Object value) {
+        return back.containsValue(value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<java.util.Map.Entry<K, V>>
+        entrySet() {
+        return back.entrySet();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public V get(Object key) {
+        return back.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isEmpty() {
+        return back.isEmpty();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Set<K> keySet() {
+        return back.keySet();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public V put(K key, V value) {
+        return back.put(key, value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void putAll(Map<? extends K, ? extends V> m) {
+        back.putAll(m);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public V remove(Object key) {
+        return back.remove(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int size() {
+        return back.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Collection<V> values() {
+        return back.values();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(Object o) {
+        return back.equals(o);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return back.hashCode();
+    }
+}
diff --git a/scene-lib/src/type/ArrayType.java b/scene-lib/src/type/ArrayType.java
new file mode 100644
index 0000000..1578688
--- /dev/null
+++ b/scene-lib/src/type/ArrayType.java
@@ -0,0 +1,35 @@
+package type;
+
+/**
+ * A representation of an array type.
+ */
+public class ArrayType extends Type {
+
+    /**
+     * The type of elements this array holds.
+     */
+    private Type componentType;
+
+    /**
+     * Constructs a new array type.
+     * @param componentType the type of elements this array holds
+     */
+    public ArrayType(Type componentType) {
+        super();
+        this.componentType = componentType;
+    }
+
+    /**
+     * Gets the component type.
+     * @return the component type
+     */
+    public Type getComponentType() {
+        return componentType;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Kind getKind() {
+        return Kind.ARRAY;
+    }
+}
diff --git a/scene-lib/src/type/BoundedType.java b/scene-lib/src/type/BoundedType.java
new file mode 100644
index 0000000..ccab173
--- /dev/null
+++ b/scene-lib/src/type/BoundedType.java
@@ -0,0 +1,124 @@
+package type;
+
+import java.util.List;
+
+/**
+ * A Java bounded type. For example:
+ * <pre>
+ *   K extends Object
+ *   E super String
+ *   ? super String
+ * </pre>
+ * Calling {@link #addAnnotation(String)}, {@link #getAnnotation(int)}, or
+ * {@link #getAnnotations()} on a {@code BoundedType} will result in an
+ * {@link UnsupportedOperationException}. Annotations should be added to the
+ * {@code name} and {@code bound} of this {@code BoundedType}.
+ */
+public class BoundedType extends Type {
+
+    /**
+     * The possible bound kinds.
+     */
+    public enum BoundKind {
+        EXTENDS,
+        SUPER;
+
+        /**
+         * Gets this bound kind in a format that can be inserted into source
+         * code.
+         */
+        @Override
+        public String toString() {
+            return super.toString().toLowerCase();
+        }
+    }
+
+    /**
+     * The type name. For example, 'K' in:
+     * <pre>
+     *   K extends Object
+     * </pre>
+     */
+    private DeclaredType name;
+
+    /**
+     * The bound kind.
+     */
+    private BoundKind boundKind;
+
+    /**
+     * The bound of this type. For example, 'Object' in:
+     * <pre>
+     *   K extends Object
+     * </pre>
+     */
+    private DeclaredType bound;
+
+    /**
+     * Creates a new bounded type.
+     * @param name the type variable name
+     * @param boundKind the bound kind
+     * @param bound the bound
+     */
+    public BoundedType(DeclaredType name, BoundKind boundKind, DeclaredType bound) {
+        super();
+        this.name = name;
+        this.boundKind = boundKind;
+        this.bound = bound;
+    }
+
+    /**
+     * Gets the type variable name. For example, 'K' in:
+     * <pre>
+     *   K extends Object
+     * </pre>
+     * @return the type variable name
+     */
+    public DeclaredType getName() {
+        return name;
+    }
+
+    /**
+     * Gets the bound of this type.
+     * @return the bound
+     */
+    public Type getBound() {
+        return bound;
+    }
+
+    /**
+     * Gets the bound kind of this type.
+     * @return the bound kind
+     */
+    public BoundKind getBoundKind() {
+        return boundKind;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Kind getKind() {
+        return Kind.BOUNDED;
+    }
+
+    // Override Type methods and throw an exception since annotations can not be
+    // put on a bounded type. Annotations should be added to the "type" and
+    // "bound" of a bounded type.
+
+    @Override
+    public void addAnnotation(String annotation) {
+        throw new UnsupportedOperationException(
+                "BoundedType cannot have annotations.");
+    }
+
+    @Override
+    public String getAnnotation(int index) {
+        throw new UnsupportedOperationException(
+                "BoundedType cannot have annotations.");
+    }
+
+    @Override
+    public List<String> getAnnotations() {
+        throw new UnsupportedOperationException(
+                "BoundedType cannot have annotations.");
+    }
+}
diff --git a/scene-lib/src/type/DeclaredType.java b/scene-lib/src/type/DeclaredType.java
new file mode 100644
index 0000000..ff83bfe
--- /dev/null
+++ b/scene-lib/src/type/DeclaredType.java
@@ -0,0 +1,161 @@
+package type;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A Java type with optional type parameters and inner type. For example:
+ *
+ * <pre>
+ *   <em>type</em>
+ *   <em>type</em>&lt;<em>type parameters</em>&gt;.<em>inner type</em>
+ * </pre>
+ *
+ * A {@code DeclaredType} can represent a wildcard by using "?" as the
+ * {@code name}. If this type is a wildcard, it is illegal to call
+ * {@link #addTypeParameter(Type)}, {@link #getTypeParameter(int)},
+ * {@link #getTypeParameters()}, {@link #setInnerType(DeclaredType)},
+ * {@link #getInnerType()}, and {@link #setTypeParameters(List)}. If called, an
+ * {@link IllegalStateException} will be thrown.
+ * <p>
+ * Types are stored with the outer most type at the top of the type tree. This
+ * is opposite to the way types are stored in javac.
+ */
+public class DeclaredType extends Type {
+
+    /**
+     * The {@code name} of a wildcard type.
+     */
+    public static final String WILDCARD = "?";
+
+    /**
+     * The raw, un-annotated name of this type. "?" for a wildcard.
+     */
+    private String name;
+
+    /**
+     * The type parameters to this type. Empty if there are none.
+     */
+    private List<Type> typeParameters;
+
+    /**
+     * The inner type of this type. {@code null} if there is none.
+     */
+    private DeclaredType innerType;
+
+    /**
+     * Creates a new declared type with no type parameters or inner type.
+     * @param name the raw, un-annotated name of this type, or "?" for a
+     *             wildcard
+     */
+    public DeclaredType(String name) {
+        super();
+        this.name = name;
+        this.typeParameters = new ArrayList<Type>();
+        this.innerType = null;
+    }
+
+    /**
+     * Creates a new declared type with an empty name and no type parameters or
+     * inner type.
+     */
+    public DeclaredType() {
+        this("");
+    }
+
+    /**
+     * Sets the raw, un-annotated name of this type.
+     * @param name the name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Gets the raw, un-annotated name of this type.
+     * @return the name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Adds the given type parameter to this type.
+     * @param typeParameter the type parameter
+     */
+    public void addTypeParameter(Type typeParameter) {
+        checkWildcard();
+        typeParameters.add(typeParameter);
+    }
+
+    /**
+     * Sets the type parameters of this type.
+     * @param typeParameters the type parameters
+     */
+    public void setTypeParameters(List<Type> typeParameters) {
+        checkWildcard();
+        this.typeParameters = typeParameters;
+    }
+
+    /**
+     * Gets the type parameter at the given index.
+     * @param index the index
+     * @return the type parameter
+     */
+    public Type getTypeParameter(int index) {
+        checkWildcard();
+        return typeParameters.get(index);
+    }
+
+    /**
+     * Gets a copy of the type parameters of this type. This will
+     * be empty if there are none.
+     * @return the type parameters
+     */
+    public List<Type> getTypeParameters() {
+        checkWildcard();
+        return new ArrayList<Type>(typeParameters);
+    }
+
+    /**
+     * Sets the inner type.
+     * @param innerType the inner type
+     */
+    public void setInnerType(DeclaredType innerType) {
+        checkWildcard();
+        this.innerType = innerType;
+    }
+
+    /**
+     * Gets the inner type. This will be {@code null} if there is none.
+     * @return the inner type or {@code null}.
+     */
+    public DeclaredType getInnerType() {
+        checkWildcard();
+        return innerType;
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    public Kind getKind() {
+        return Kind.DECLARED;
+    }
+
+    /**
+     * Determines if this type is a wildcard.
+     * @return {@code true} if this type is a wildcard, {@code false} otherwise.
+     */
+    public boolean isWildcard() {
+        return WILDCARD.equals(name);
+    }
+
+    /**
+     * Throw an {@link IllegalStateException} if this type is a wildcard.
+     */
+    private void checkWildcard() {
+        if (isWildcard()) {
+            throw new IllegalStateException("This method can't be called "
+                    + "since this DeclaredType is a wildcard.");
+        }
+    }
+}
diff --git a/scene-lib/src/type/Type.java b/scene-lib/src/type/Type.java
new file mode 100644
index 0000000..b3a7402
--- /dev/null
+++ b/scene-lib/src/type/Type.java
@@ -0,0 +1,79 @@
+package type;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A representation of a Java type. Handles type parameters, bounded types, arrays
+ * and inner types.
+ */
+public abstract class Type {
+
+    /**
+     * The different kinds of {@link Type}s.
+     */
+    public enum Kind {
+        ARRAY,
+        BOUNDED,
+        DECLARED
+    }
+
+    /**
+     * The annotations on the outer type. Empty if there are none.
+     */
+    private List<String> annotations;
+
+    /**
+     * Constructs a new type with no outer annotations.
+     */
+    public Type() {
+        annotations = new ArrayList<String>();
+    }
+
+    /**
+     * Adds an outer annotation to this type.
+     * @param annotation the annotation to add
+     */
+    public void addAnnotation(String annotation) {
+        annotations.add(annotation);
+    }
+
+    /**
+     * Replaces the annotations on this type with the given annotations.
+     * @param annotations the new annotations to be placed on this type
+     */
+    public void setAnnotations(List<String> annotations) {
+        this.annotations = annotations;
+    }
+
+    /**
+     * Gets an outer annotation on this type at the given index.
+     * @param index the index
+     * @return the annotation
+     */
+    public String getAnnotation(int index) {
+        return annotations.get(index);
+    }
+
+    /**
+     * Gets a copy of the outer annotations on this type. This
+     * will be empty if there are none.
+     * @return the annotations
+     */
+    public List<String> getAnnotations() {
+        return new ArrayList<String>(annotations);
+    }
+
+    /**
+     * Removes the annotations from this type.
+     */
+    public void clearAnnotations() {
+        annotations.clear();
+    }
+
+    /**
+     * Gets the {@link Kind} of this {@link Type}.
+     * @return the kind
+     */
+    public abstract Kind getKind();
+}
diff --git a/scene-lib/test/README b/scene-lib/test/README
new file mode 100644
index 0000000..b2c0272
--- /dev/null
+++ b/scene-lib/test/README
@@ -0,0 +1,10 @@
+# To create a new scene-lib test case, do something like the following with a properly-functioning version of AFU:
+
+cd $t/annotation-tools/scene-lib/test/annotations/tests/classfile/cases
+$ch/bin/javac -g TestLocalVariableA.java
+\mv -pf TestLocalVariableA.class $t/annotation-tools/scene-lib/test/annotations-expected/tests/classfile/cases
+(CLASSPATH=$t/scene-lib/test insert-annotations-to-source TestLocalVariableA.jaif TestLocalVariableA.java)
+cd annotated
+$ch/bin/javac -g -cp $t/annotation-tools/scene-lib/bin annotations/tests/classfile/cases/TestLocalVariableA.java
+\cp -p annotations/tests/classfile/cases/TestLocalVariableA.class $t/annotation-tools/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableA_Expected.class
+
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/Makefile b/scene-lib/test/annotations-expected/tests/classfile/cases/Makefile
new file mode 100644
index 0000000..8a15621
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/Makefile
@@ -0,0 +1,22 @@
+bless:
+	-echo Make sure to run "ant test" in scene-lib before!
+	make bless-files
+
+bless-files: TestClassEmpty_Expected.class \
+	TestClassNonEmpty_Expected.class \
+	TestLocalVariable_Expected.class \
+	TestLocalVariableA_Expected.class \
+	TestLocalVariableGenericArray_Expected.class \
+	TestFieldSimple_Expected.class \
+	TestFieldGeneric_Expected.class \
+	TestTypecast_Expected.class \
+	TestTypecastGenericArray_Expected.class \
+	TestTypeTest_Expected.class \
+	TestObjectCreation_Expected.class \
+	TestObjectCreationGenericArray_Expected.class \
+	TestMethodReceiver_Expected.class \
+	TestMethodReturnTypeGenericArray_Expected.class \
+
+
+%_Expected.class: %_Generated.class
+	-mv $< $@
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassEmpty.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassEmpty.class
new file mode 100644
index 0000000..fa97e0e
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassEmpty.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassEmpty_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassEmpty_Expected.class
new file mode 100644
index 0000000..726ee54
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassEmpty_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassNonEmpty.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassNonEmpty.class
new file mode 100644
index 0000000..643f995
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassNonEmpty.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassNonEmpty_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassNonEmpty_Expected.class
new file mode 100644
index 0000000..20f3fda
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestClassNonEmpty_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldGeneric.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldGeneric.class
new file mode 100644
index 0000000..727875c
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldGeneric.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldGeneric_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldGeneric_Expected.class
new file mode 100644
index 0000000..4e2dbce
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldGeneric_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldSimple.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldSimple.class
new file mode 100644
index 0000000..756f762
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldSimple.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldSimple_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldSimple_Expected.class
new file mode 100644
index 0000000..4a84ae2
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestFieldSimple_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariable.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariable.class
new file mode 100644
index 0000000..bd1f2d7
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariable.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableA.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableA.class
new file mode 100644
index 0000000..96f94f6
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableA.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableA_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableA_Expected.class
new file mode 100644
index 0000000..479fe4b
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableA_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableGenericArray.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableGenericArray.class
new file mode 100644
index 0000000..e3c21aa
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableGenericArray.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableGenericArray_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableGenericArray_Expected.class
new file mode 100644
index 0000000..df05bdf
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariableGenericArray_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariable_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariable_Expected.class
new file mode 100644
index 0000000..dcb878a
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestLocalVariable_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReceiver.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReceiver.class
new file mode 100644
index 0000000..b3f5ca4
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReceiver.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReceiver_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReceiver_Expected.class
new file mode 100644
index 0000000..73f4b72
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReceiver_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReturnTypeGenericArray.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReturnTypeGenericArray.class
new file mode 100644
index 0000000..98f1305
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReturnTypeGenericArray.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReturnTypeGenericArray_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReturnTypeGenericArray_Expected.class
new file mode 100644
index 0000000..53e1d6a
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestMethodReturnTypeGenericArray_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreation.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreation.class
new file mode 100644
index 0000000..4a10cc3
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreation.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreationGenericArray.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreationGenericArray.class
new file mode 100644
index 0000000..2386399
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreationGenericArray.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreationGenericArray_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreationGenericArray_Expected.class
new file mode 100644
index 0000000..98f58a4
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreationGenericArray_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreation_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreation_Expected.class
new file mode 100644
index 0000000..33257fd
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestObjectCreation_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypeTest.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypeTest.class
new file mode 100644
index 0000000..add9796
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypeTest.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypeTest_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypeTest_Expected.class
new file mode 100644
index 0000000..886e410
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypeTest_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecast.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecast.class
new file mode 100644
index 0000000..10a7a00
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecast.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecastGenericArray.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecastGenericArray.class
new file mode 100644
index 0000000..9aae58b
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecastGenericArray.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecastGenericArray_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecastGenericArray_Expected.class
new file mode 100644
index 0000000..60e530a
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecastGenericArray_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecast_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecast_Expected.class
new file mode 100644
index 0000000..4a3030d
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/TestTypecast_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/package-info.class b/scene-lib/test/annotations-expected/tests/classfile/cases/package-info.class
new file mode 100644
index 0000000..7d774b8
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/package-info.class
Binary files differ
diff --git a/scene-lib/test/annotations-expected/tests/classfile/cases/package-info_Expected.class b/scene-lib/test/annotations-expected/tests/classfile/cases/package-info_Expected.class
new file mode 100644
index 0000000..7d774b8
--- /dev/null
+++ b/scene-lib/test/annotations-expected/tests/classfile/cases/package-info_Expected.class
Binary files differ
diff --git a/scene-lib/test/annotations/tests/classfile/AnnotationVerifier.java b/scene-lib/test/annotations/tests/classfile/AnnotationVerifier.java
new file mode 100644
index 0000000..543c05c
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/AnnotationVerifier.java
@@ -0,0 +1,608 @@
+package annotations.tests.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.TypeAnnotationVisitor;
+import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.commons.EmptyVisitor;
+
+import plume.UtilMDE;
+
+import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
+
+/**
+ * An <code>AnnotationVerifier</code> provides a way to check to see if two
+ * versions of the same class (from two different <code>.class</code> files),
+ * have the same annotations on the same elements.
+ */
+public class AnnotationVerifier {
+
+  private static final String lineSep = System.getProperty("line.separator");
+
+  /**
+   * The "correct" version of the class to verify against.
+   */
+  private ClassRecorder originalVisitor;
+
+  /**
+   * The uncertain version of the class to verify.
+   */
+  private ClassRecorder newVisitor;
+
+  /**
+   * Constructs a new <code>AnnotationVerifier</code> that does not yet have
+   * any information about the class.
+   */
+  public AnnotationVerifier() {
+    originalVisitor = new ClassRecorder();
+    newVisitor = new ClassRecorder();
+  }
+
+  /**
+   * Returns the <code>ClassVisitor</code> which should be made to visit the
+   * version of the class known to be correct.
+   *
+   * @return a visitor for the good version of the class
+   */
+  public ClassVisitor originalVisitor() {
+    return originalVisitor;
+  }
+
+  /**
+   * Returns the <code>ClassVisitor</code> which should be made to visit the
+   * version of the class being tested.
+   *
+   * @return a visitor the the experimental version of the class
+   */
+  public ClassVisitor newVisitor() {
+    return newVisitor;
+  }
+
+  /**
+   * Verifies that the visitors returned by {@link #originalVisitor()} and
+   * {@link #newVisitor()} have visited the same class.  This method can only
+   * be called if both visitors have already visited a class.
+   *
+   * @throws AnnotationMismatchException if the two visitors have not visited
+   * two versions of the same class that contain idential annotations
+   */
+  public void verify() {
+    if (!newVisitor.name.equals(originalVisitor.name)) {
+      throw new AnnotationMismatchException(
+          "Cannot verify two different classes:\n  " +
+          newVisitor.name + "\n  " + originalVisitor.name);
+    }
+    newVisitor.verifyAgainst(originalVisitor);
+  }
+
+  // print the expected and found annotations to standard out
+  public void verifyPrettyPrint() {
+    System.out.println("expected:");
+    System.out.println(originalVisitor.prettyPrint());
+    System.out.println("actual:");
+    System.out.println(newVisitor.prettyPrint());
+  }
+
+
+  /**
+   * A ClassRecorder records all the annotations that it visits, and serves
+   * as a ClassVisitor, FieldVisitor, and MethodVisitor.
+   */
+  private class ClassRecorder extends EmptyVisitor {
+
+    private String description;
+
+    public String name;
+    private String signature;
+
+    private Map<String, ClassRecorder> fieldRecorders;
+    // key is unparameterized name
+
+    private Map<String, ClassRecorder> methodRecorders;
+    // key is complete method signature
+
+    // general annotations
+    private Map<String, AnnotationRecorder> anns;
+    private Map<String, AnnotationRecorder> xanns;
+
+    // method specific annotations
+    private Set<AnnotationRecorder> danns; // default annotations
+    private Map<ParameterDescription, AnnotationRecorder> panns; // parameter annotations
+
+    public ClassRecorder() {
+      this("class: ","","");
+    }
+
+    public ClassRecorder(String internalDescription, String name, String signature) {
+      this.description = internalDescription;
+      this.name = name;
+      this.signature = signature;
+
+      fieldRecorders = new HashMap<String, ClassRecorder>();
+      methodRecorders = new HashMap<String, ClassRecorder>();
+
+      anns = new HashMap<String, AnnotationRecorder>();
+      xanns = new HashMap<String, AnnotationRecorder>();
+
+      danns = new HashSet<AnnotationRecorder>();
+      panns = new HashMap<ParameterDescription, AnnotationRecorder>();
+    }
+
+    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
+      this.name = name;
+      this.signature = signature;
+      description = description + name;
+    }
+
+    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
+      AnnotationRecorder av = new AnnotationRecorder(
+          description + " annotation: " + desc);
+      anns.put(desc,av);
+      return av;
+    }
+
+    public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible) {
+      AnnotationRecorder av = new AnnotationRecorder(
+          description + " annotation: " + desc);
+      xanns.put(desc,av);
+      return av;
+    }
+
+    public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
+      ClassRecorder fr =
+        new ClassRecorder(
+            description + " field: " + name,
+            name, signature);
+      fieldRecorders.put(name, fr);
+      return fr;
+    }
+
+    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
+      ClassRecorder mr =
+        new ClassRecorder(
+            description + " method: " + name + desc, name+desc, signature);
+      methodRecorders.put(name+desc, mr);
+      return mr;
+    }
+
+    // MethodVisitor methods:
+    public AnnotationVisitor visitAnnotationDefault() {
+      AnnotationRecorder dr = new AnnotationRecorder(
+          description + " default annotation");
+      danns.add(dr);
+      return dr;
+    }
+
+    public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
+      ParameterDescription pd =
+        new ParameterDescription(parameter, desc, visible);
+      AnnotationRecorder pr =
+        new AnnotationRecorder(description +
+            " parameter annotation: " + pd);
+      panns.put(pd, pr);
+      return pr;
+    }
+
+    public void verifyAgainst(ClassRecorder correct) {
+      // first, ensure all annotations are correct
+      verifyAnns(this.anns, correct.anns);
+      verifyAnns(this.xanns, correct.xanns);
+
+      // then recurse into any annotations on fields/methods
+      verifyMemberAnns(this.fieldRecorders, correct.fieldRecorders);
+      verifyMemberAnns(this.methodRecorders, correct.methodRecorders);
+    }
+
+    private void verifyAnns(
+        Map<String, AnnotationRecorder> questionableAnns,
+        Map<String, AnnotationRecorder> correctAnns) {
+      Set<AnnotationRecorder> unresolvedQuestionableAnns =
+        new HashSet<AnnotationRecorder>(questionableAnns.values());
+
+      for (Map.Entry<String, AnnotationRecorder> entry :
+        correctAnns.entrySet()) {
+        String name = entry.getKey();
+        AnnotationRecorder correctAnn = entry.getValue();
+        AnnotationRecorder questionableAnn = questionableAnns.get(name);
+        if (questionableAnn == null) {
+          throw new AnnotationMismatchException("{" + description + "}" +
+              "\n  does not contain expected annotation:\n  " + "{" + correctAnn + "}");
+        }
+
+        questionableAnn.verifyAgainst(correctAnn);
+
+        unresolvedQuestionableAnns.remove(questionableAnn);
+      }
+
+      for (AnnotationRecorder unexpectedAnnOnThis : unresolvedQuestionableAnns) {
+        throw new AnnotationMismatchException("{" + description + "}" +
+            "\n  contains unexpected annotation:\n  " + "{" + unexpectedAnnOnThis + "}");
+      }
+    }
+
+    private void verifyMemberAnns(
+        Map<String, ClassRecorder> questionableMembers,
+        Map<String, ClassRecorder> correctMembers) {
+      Set<ClassRecorder> unresolvedQuestionableMembers =
+        new HashSet<ClassRecorder>(questionableMembers.values());
+
+      for (Map.Entry<String, ClassRecorder> entry :
+        correctMembers.entrySet()) {
+        String name = entry.getKey();
+        ClassRecorder correctMember = entry.getValue();
+        ClassRecorder questionableMember = questionableMembers.get(name);
+        if (questionableMember == null) {
+          throw new AnnotationMismatchException("{" + description + "}" +
+              "\n  does not contain expected member:\n  " + "{" + correctMember + "}");
+        }
+
+        questionableMember.verifyAgainst(correctMember);
+
+        unresolvedQuestionableMembers.remove(questionableMember);
+      }
+
+      for (ClassRecorder unexpectedMemberOnThis : unresolvedQuestionableMembers) {
+        System.out.println("Going to throw exception: ");
+        System.out.println("questionable: " + questionableMembers);
+        System.out.println("correct: " + correctMembers);
+
+        throw new AnnotationMismatchException("{" + description + "}" +
+            "\n  contains unexpected member:\n  " + "{" + unexpectedMemberOnThis + "}");
+      }
+    }
+
+    public String toString() {
+      return description;
+    }
+
+    public String prettyPrint() {
+      StringBuilder sb = new StringBuilder();
+      prettyPrint(sb, "");
+      return sb.toString();
+    }
+
+    // pretty-prints this into the given list of lines
+    public void prettyPrint(StringBuilder sb, String indent) {
+
+      // avoid boilerplate of adding indent and lineSep every time
+      List<String> lines = new ArrayList<String>();
+
+      lines.add("description: " + description);
+      lines.add("  name: " + name);
+      lines.add("  signature: " + signature);
+      if (! anns.isEmpty()) {
+        lines.add("  anns:");
+        for (Map.Entry<String, AnnotationRecorder> e : anns.entrySet()) {
+          // in the future, maybe get a fuller description
+          lines.add("    " + e.getKey() + ": " + e.getValue().description);
+        }
+      }
+      if (! xanns.isEmpty()) {
+        lines.add("  xanns:");
+        for (Map.Entry<String, AnnotationRecorder> e : xanns.entrySet()) {
+          // in the future, maybe get a fuller description
+          lines.add("    " + e.getKey() + ": " + e.getValue().description);
+        }
+      }
+      for (String line : lines) {
+        sb.append(indent);
+        sb.append(line);
+        sb.append(lineSep);
+      }
+      for (Map.Entry<String, ClassRecorder> e : fieldRecorders.entrySet()) {
+        sb.append(indent + "  " + e.getKey() + ":" + lineSep);
+        e.getValue().prettyPrint(sb, indent + "    ");
+      }
+      for (Map.Entry<String, ClassRecorder> e : methodRecorders.entrySet()) {
+        sb.append(indent + "  " + e.getKey() + ":" + lineSep);
+        e.getValue().prettyPrint(sb, indent + "    ");
+      }
+    }
+  }
+
+
+  /**
+   * An AnnotationRecorder is an TypeAnnotationVisitor that records all the
+   * information it visits.
+   */
+  private class AnnotationRecorder implements TypeAnnotationVisitor {
+    private String description;
+
+    private List<String> fieldArgsName;
+    private List<Object> fieldArgsValue;
+
+    private List<String> enumArgsName;
+    private List<String> enumArgsDesc;
+    private List<String> enumArgsValue;
+
+    private List<String> innerAnnotationArgsName;
+    private List<String> innerAnnotationArgsDesc;
+    private Map<String, AnnotationRecorder> innerAnnotationMap;
+
+    private List<String> arrayArgs;
+    private Map<String, AnnotationRecorder> arrayMap;
+
+    private List<Integer> xIndexArgs;
+    private List<Integer> xLengthArgs;
+    private List<TypePathEntry> xLocationArgs;
+    private List<Integer> xLocationLengthArgs;
+    private List<Integer> xOffsetArgs;
+    private List<Integer> xStartPcArgs;
+    private List<Integer> xTargetTypeArgs;
+    private List<Integer> xParamIndexArgs;
+    private List<Integer> xBoundIndexArgs;
+    private List<Integer> xExceptionIndexArgs;
+    private List<Integer> xTypeIndexArgs;
+
+    public AnnotationRecorder(String description) {
+      this.description = description;
+      fieldArgsName = new ArrayList<String>();
+      fieldArgsValue = new ArrayList<Object>();
+
+      enumArgsName = new ArrayList<String>();
+      enumArgsDesc = new ArrayList<String>();
+      enumArgsValue = new ArrayList<String>();
+
+      innerAnnotationArgsName = new ArrayList<String>();
+      innerAnnotationArgsDesc = new ArrayList<String>();
+      innerAnnotationMap = new HashMap<String, AnnotationRecorder>();
+
+      arrayArgs = new ArrayList<String>();
+      arrayMap = new HashMap<String, AnnotationRecorder>();
+
+      xIndexArgs = new ArrayList<Integer>();
+      xLengthArgs = new ArrayList<Integer>();
+      xLocationArgs = new ArrayList<TypePathEntry>();
+      xLocationLengthArgs = new ArrayList<Integer>();
+      xOffsetArgs = new ArrayList<Integer>();
+      xStartPcArgs = new ArrayList<Integer>();
+      xTargetTypeArgs = new ArrayList<Integer>();
+      xParamIndexArgs = new ArrayList<Integer>();
+      xBoundIndexArgs = new ArrayList<Integer>();
+      xExceptionIndexArgs = new ArrayList<Integer>();
+      xTypeIndexArgs = new ArrayList<Integer>();
+    }
+
+    public void visitXIndex(int index) {
+      xIndexArgs.add(index);
+    }
+
+    public void visitXLength(int length) {
+      xLengthArgs.add(length);
+    }
+
+    public void visitXLocation(TypePathEntry location) {
+      xLocationArgs.add(location);
+    }
+
+    public void visitXLocationLength(int location_length) {
+     xLocationLengthArgs.add(location_length);
+    }
+
+    public void visitXOffset(int offset) {
+      xOffsetArgs.add(offset);
+    }
+
+    public void visitXNumEntries(int num_entries) {
+    }
+
+    public void visitXStartPc(int start_pc) {
+      xStartPcArgs.add(start_pc);
+    }
+
+    public void visitXTargetType(int target_type) {
+      xTargetTypeArgs.add(target_type);
+    }
+
+    public void visitXParamIndex(int param_index) {
+      xParamIndexArgs.add(param_index);
+    }
+
+    public void visitXBoundIndex(int bound_index) {
+      if (bound_index != -1) {
+        xBoundIndexArgs.add(bound_index);
+      }
+    }
+
+    public void visitXExceptionIndex(int except_index) {
+      xExceptionIndexArgs.add(except_index);
+    }
+
+    public void visitXTypeIndex(int type_index) {
+      xTypeIndexArgs.add(type_index);
+    }
+
+    public void visit(String name, Object value) {
+      fieldArgsName.add(name);
+      fieldArgsValue.add(value);
+    }
+
+    public AnnotationVisitor visitAnnotation(String name, String desc) {
+      innerAnnotationArgsName.add(name);
+      innerAnnotationArgsDesc.add(desc);
+
+      AnnotationRecorder av= new AnnotationRecorder(description + name);
+      innerAnnotationMap.put(name, av);
+      return av;
+    }
+
+    public AnnotationVisitor visitArray(String name) {
+      arrayArgs.add(name);
+      AnnotationRecorder av = new AnnotationRecorder(description + name);
+      arrayMap.put(name, av);
+      return av;
+    }
+
+    public void visitEnd() {
+    }
+
+    public void visitXNameAndArgsSize() {
+    }
+
+    public void visitEnum(String name, String desc, String value) {
+      enumArgsName.add(name);
+      enumArgsDesc.add(desc);
+      enumArgsValue.add(value);
+    }
+
+    public String toString() {
+      return description;
+    }
+
+    /**
+     * Checks that the information passed into this matches the information
+     * passed into another AnnotationRecorder.  For right now, the order in
+     * which information is passed in does matter.  If there is a conflict in
+     * information, an exception will be thrown.
+     *
+     * @param ar an annotation recorder that has visited the correct information
+     *  this should visit
+     * @throws AnnotationMismatchException if the information visited by this
+     *  does not match the information in ar
+     */
+    public void verifyAgainst(AnnotationRecorder ar) {
+      StringBuilder sb = new StringBuilder();
+      verifyList(sb, "visit()", 1, this.fieldArgsName, ar.fieldArgsName);
+      verifyList(sb, "visit()", 2, this.fieldArgsValue, ar.fieldArgsValue);
+
+      verifyList(sb, "visitEnum()", 1, this.enumArgsName, ar.enumArgsName);
+      verifyList(sb, "visitEnum()", 2, this.enumArgsDesc, ar.enumArgsDesc);
+      verifyList(sb, "visitEnum()", 3, this.enumArgsValue, ar.enumArgsValue);
+
+      verifyList(sb, "visitAnnotation()", 1, this.innerAnnotationArgsName, ar.innerAnnotationArgsName);
+      verifyList(sb, "visitAnnotation()", 2, this.innerAnnotationArgsDesc, ar.innerAnnotationArgsDesc);
+
+      verifyList(sb, "visitArray()", 1, this.arrayArgs, ar.arrayArgs);
+
+      verifyList(sb, "visitXIndexArgs()", 1, this.xIndexArgs, ar.xIndexArgs);
+      verifyList(sb, "visitXLength()", 1, this.xLengthArgs, ar.xLengthArgs);
+      verifyList(sb, "visitXLocation()", 1, this.xLocationArgs, ar.xLocationArgs);
+      verifyList(sb, "visitXLocationLength()", 1, this.xLocationLengthArgs, ar.xLocationLengthArgs);
+      verifyList(sb, "visitXOffset()", 1, this.xOffsetArgs, ar.xOffsetArgs);
+      verifyList(sb, "visitXStartPc()", 1, this.xStartPcArgs, ar.xStartPcArgs);
+      verifyList(sb, "visitXTargetType()", 1, this.xTargetTypeArgs, ar.xTargetTypeArgs);
+      verifyList(sb, "visitXParamIndex()", 1, this.xParamIndexArgs, ar.xParamIndexArgs);
+      verifyList(sb, "visitXBoundIndex()", 1, this.xBoundIndexArgs, ar.xBoundIndexArgs);
+      verifyList(sb, "visitXExceptionIndex()", 1, this.xExceptionIndexArgs, ar.xExceptionIndexArgs);
+      verifyList(sb, "visitXTypeIndex()", 1, this.xTypeIndexArgs, ar.xTypeIndexArgs);
+
+      verifyInnerAnnotationRecorder(sb, this.innerAnnotationMap, ar.innerAnnotationMap);
+      verifyInnerAnnotationRecorder(sb, this.arrayMap, ar.arrayMap);
+
+      if (sb.length() > 0) {
+        throw new AnnotationMismatchException(sb.toString());
+      }
+    }
+
+    private void verifyList(
+        StringBuilder sb,
+        String methodName,
+        int parameter,
+        List questionable,
+        List correct) {
+      if (questionable.equals(correct)) {
+        return;
+      }
+      if (UtilMDE.deepEquals(questionable, correct)) {
+        return;
+      }
+
+      sb.append(lineSep);
+      sb.append(methodName);
+      sb.append(" was called with unexpected information in parameter: ");
+      sb.append(parameter);
+      sb.append(lineSep);
+      sb.append("Description: ");
+      sb.append(description);
+      sb.append(lineSep);
+      sb.append(String.format("Received (%s): %s%n",
+                              questionable.getClass(), questionable));
+      sb.append(String.format("Expected (%s): %s%n",
+                              correct.getClass(), correct));
+      // new Error("The backtrace:").printStackTrace();
+    }
+
+    private void verifyInnerAnnotationRecorder(
+        StringBuilder sb,
+        Map<String, AnnotationRecorder> questionableAR,
+        Map<String, AnnotationRecorder> correctAR) {
+      // checks on arguments passed in to the methods that created these
+      // AnnotationRecorders (i.e. the checks on the String keys on these maps)
+      // ensure that these have identical keys
+      for (Map.Entry<String, AnnotationRecorder> questionableEntry :
+        questionableAR.entrySet()) {
+        questionableEntry.getValue().verifyAgainst(
+            correctAR.get(questionableEntry.getClass()));
+      }
+    }
+
+
+  }
+
+  /**
+   * A ParameterDescription is a convenient class used to keep information about
+   * method parameters.  Parameters are equal if they have the same index,
+   * regardless of their description.
+   */
+  private class ParameterDescription {
+    public final int parameter;
+    public final String desc;
+    public final boolean visible;
+
+    public ParameterDescription(int parameter, String desc, boolean visible) {
+      this.parameter = parameter;
+      this.desc = desc;
+      this.visible = visible;
+    }
+
+    public boolean equals(/*@Nullable*/ Object o) {
+      if (o instanceof ParameterDescription) {
+        ParameterDescription p = (ParameterDescription) o;
+        return this.parameter == p.parameter;
+      }
+      return false;
+    }
+
+    public int hashCode() {
+      return parameter * 17;
+    }
+
+    public String toString() {
+      return
+        "parameter index: " + parameter +
+        " desc: " + desc +
+        " visible: " + visible;
+    }
+  }
+
+  /**
+   * An AnnotationMismatchException is an Exception that indicates that
+   * two versions of the same class do not have the same annotations on
+   * either the class, its field, or its methods.
+   */
+  public class AnnotationMismatchException extends RuntimeException {
+    private static final long serialVersionUID = 20060714L; // today's date
+
+    /**
+     * Constructs a new AnnotationMismatchException with the given error message.
+     *
+     * @param msg the error as to why the annotations do not match
+     */
+    public AnnotationMismatchException(String msg) {
+      super(msg);
+    }
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/AnnotationsTest.java b/scene-lib/test/annotations/tests/classfile/AnnotationsTest.java
new file mode 100644
index 0000000..6d16dcc
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/AnnotationsTest.java
@@ -0,0 +1,602 @@
+package annotations.tests.classfile;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import org.objectweb.asm.ClassReader;
+
+import annotations.Annotation;
+import annotations.AnnotationFactory;
+import annotations.el.AScene;
+import annotations.io.IndexFileParser;
+import annotations.io.IndexFileWriter;
+import annotations.io.classfile.ClassFileReader;
+import annotations.io.classfile.ClassFileWriter;
+import annotations.tests.classfile.foo.A;
+
+/**
+ * This class is the testing framework for the class file/index file
+ * annotations converter.  To add a new test,
+ * <ul>
+ *  <li>add the class name to array {@link #allTests}
+ *  <li>place two files in directory {@link #CLASS_FILE_BASE}:
+ *    a .class file (for the unannotated version of the class),
+ *    an _Expected.class file (for the annotated version of the class).
+ *  <li>place two files in directory {@link #INDEX_FILE_BASE}:
+ *    a .java source file (this is not used by the tests -- it is only for
+ *      documentation, and is helpful when creating the test files),
+ *    a .jaif index file.
+ *  <li>Add a <code>testc*()</code> method to test against class file and a
+ *    <code>testi*()</code> method to test against index file; this is just so
+ *     that JUnit has an accurate count of all tests.
+ * </ul>
+ *
+ * Two types of tests are performed:
+ * <ul>
+ *   <li>"c" tests that call testAgainstClass:
+ *      Read the annotations from <code>name.jaif</code>, insert them into
+ *      <code>name.class</code>, write the results to a temporary file
+ *      (name_Generated.class), and compare this generated class file with
+ *      <code>name_Expected.class</code>, asserting that they have the same
+ *      annotations.
+ *   <li>"i" tests that call testAgainstIndexFile:
+ *      Read the annotations from the generated class file, and check them
+ *      against the annotations from the index file.
+ * </ul>
+ */
+public class AnnotationsTest extends TestCase {
+
+  /**
+   * The directory in which to find the index files to test.
+   */
+  private static final String INDEX_FILE_BASE =
+    "test/annotations/tests/classfile/cases/";
+
+  /**
+   * The directory in which to find the class files (both .class and _Generated.class)
+   * to test.
+   */
+  private static final String CLASS_FILE_BASE =
+    "test/annotations-expected/tests/classfile/cases/";
+
+  /**
+   * An array of all the classes to test.  For each name in this array, there
+   * must be a corresponding .jaif file in {@link #INDEX_FILE_BASE} and
+   * .class and _Expected.class files in {@link #CLASS_FILE_BASE}
+   */
+  public static final String[] allTests = {
+    "TestClassEmpty",
+    "TestClassNonEmpty",
+    "TestFieldSimple",
+    "TestFieldGeneric",
+    "TestLocalVariable",
+    "TestLocalVariableA",
+    "TestLocalVariableGenericArray",
+    "TestTypecast",
+    "TestTypecastGenericArray",
+    "TestTypeTest",
+    "TestObjectCreation",
+    "TestObjectCreationGenericArray",
+    "TestMethodReceiver",
+    "TestMethodReturnTypeGenericArray"
+  };
+
+  /**
+   * Constructs a new <code>AnnotationsTest</code> with the given name.
+   *
+   * @param s the name of this test case
+   */
+  public AnnotationsTest(String s) {
+    super(s);
+  }
+
+  /**
+   * Runs all the tests in {@link #allTests} and displays the failure and error
+   * counts.
+   */
+  public static void main(String[] args) {
+    TestSuite suite = new TestSuite(AnnotationsTest.class);
+    TestResult result = new TestResult();
+    suite.run(result);
+    System.out.println(
+        "AnnotationsTests ran with " + result.failureCount() + " failures and "
+        + result.errorCount() + " errors. (" + result.runCount()
+        + " successes.)");
+  }
+
+  /**
+   * Prepends {@link #CLASS_FILE_BASE} to s.
+   */
+  private String nameClass(String s) {
+    return CLASS_FILE_BASE + s;
+  }
+
+  /**
+   * Prepends {@link #INDEX_FILE_BASE} to s.
+   */
+  private String nameIndex(String s) {
+    return INDEX_FILE_BASE + s;
+  }
+
+  /**
+   * Writes out scene to filename as an index file.
+   *
+   * @param filename the file to write to
+   * @param scene the scene to write out
+   */
+  private void writeScene(String filename, AScene scene) {
+    try {
+      IndexFileWriter.write(scene, filename);
+    } catch (Exception e) {
+      System.err.println("caught exception: ");
+      e.printStackTrace();
+      fail();
+    }
+  }
+
+  /**
+   * Reads in the annotations from filename, an index file, into scene.
+   *
+   * @param filename the index file to read from
+   * @param scene the scene to write out to
+   */
+  private void readScene(String filename, AScene scene) {
+    try {
+      IndexFileParser.parseFile(filename, scene);
+    } catch (Exception e) {
+      System.err.println("caught exception: ");
+      e.printStackTrace();
+      fail("caught exception: " + e.toString());
+    }
+  }
+
+  /**
+   * Reads in the class file from the given filename, inserts the annotations
+   * from scene, and writes out the result into the same file.
+   *
+   * @param filename the class file to insert annotations into
+   * @param scene the scene that contains annotations to be inserted
+   * @param overwrite whether to overwrite existing annotations
+   */
+  private void writeClass(String filename,
+      AScene scene, boolean overwrite) {
+    writeClass(filename, filename, scene, overwrite);
+  }
+
+  /**
+   * Like {@link #writeClass(String, AScene, boolean)}, except the class will be read from and written to
+   * different files.
+   *
+   * @param oldFileName the class file to read from
+   * @param newFileName the class file to write to
+   * @param scene the scene that contains annotations to be inserted
+   * @param overwrite whether to overwrite existing annotations
+   */
+  private void writeClass(
+      String oldFileName,
+      String newFileName,
+      AScene scene,
+      boolean overwrite) {
+    try {
+      ClassFileWriter.insert(
+          scene,
+          new FileInputStream(oldFileName),
+          new FileOutputStream(newFileName),
+          overwrite);
+    } catch (Throwable e) {
+      System.err.printf("caught exception in writeClass(oldFileName=%s, newFileName=%s, ...):%n",
+                        oldFileName, newFileName);
+      e.printStackTrace();
+      fail();
+    }
+  }
+
+  /**
+   * Reads in the annotations from the class file at filename and inserts them
+   * into scene.
+   *
+   * @param filename the class file to read from
+   * @param scene the scene to write to
+   */
+  private void readClass(String filename,
+      AScene scene) {
+    try {
+      ClassFileReader.read(scene, filename);
+    } catch (Exception e) {
+      System.err.printf("caught exception while reading %s:%n", new File(filename).getAbsolutePath());
+      e.printStackTrace();
+      fail();
+    }
+  }
+
+  /**
+   * Creates scene from the annotations in the given index file.
+   *
+   * @param indexFile the index file to create a scene from
+   * @return the scene created from the given index file
+   */
+  private AScene createScene(String indexFile) {
+    AScene scene =
+      new AScene();
+    readScene(indexFile, scene);
+    return scene;
+  }
+
+  /**
+   * Asserts that the annotations in two class files match.
+   * This method will cause this test to fail if there
+   * is a mismatch in annotations, or if there is a mismatch in either field
+   * or method information that means these classes cannot reasonably be
+   * compared.
+   *
+   * @param correctClass the file name of the correct version of the class
+   * @param generatedClass the file name of the version of the class being tested
+   */
+  private void assertClassAnnotations(String correctClass, String generatedClass) {
+
+    try {
+      InputStream correctIs = new FileInputStream(correctClass);
+
+      InputStream generatedIs = new FileInputStream(generatedClass);
+
+      ClassReader crCorrect = new ClassReader(correctIs);
+      ClassReader crGenerated = new ClassReader(generatedIs);
+
+      AnnotationVerifier av = new AnnotationVerifier();
+
+      crCorrect.accept(av.originalVisitor(), false);
+      crGenerated.accept(av.newVisitor(), false);
+
+      try {
+        av.verify();
+      } catch (AnnotationVerifier.AnnotationMismatchException e) {
+        String message = String.format("assertClassAnnotations (consider running javap on the two .class files):%n  correctClass %s%n  generatedClass %s%n%s", correctClass, generatedClass, e.toString());
+        System.out.println();
+        System.out.println(message);
+        av.verifyPrettyPrint();
+        System.out.println(message);
+        System.out.println();
+        fail(message);
+      }
+
+    } catch (IOException e) {
+      fail("IOException caught: " + e);
+    }
+  }
+
+  /**
+   * Runs a test that:
+   *  <li> reads annotations from indexFileName,
+   *  <li> inserts them into baseClassName.class,
+   *  <li> writes the result out to baseClassName_Generated.class, and
+   *  <li> asserts that the results written out match baseClassName_Expected.class
+   */
+  private void testAgainstClass(String indexFileName, String baseClassName) {
+    String base = baseClassName + ".class";
+    String expected = baseClassName + "_Expected.class";
+    String generated = baseClassName + "_Generated.class";
+
+    AScene scene = new AScene();
+
+    // read in annotations from index file to scene
+    readScene(indexFileName, scene);
+
+    // read in class from base, merge annotations from scene and
+    //  write out to generated
+    writeClass(base, generated, scene, true);
+
+    // assert that generated class has same annotations as expected class
+    assertClassAnnotations(expected, generated);
+  }
+
+  /**
+   * Runs a test that:
+   *  <li> reads annotations from indexFileName,
+   *  <li> inserts them into className
+   *  <li> writes results out to a temporary class file
+   *  <li> reads annotations from that class file, and
+   *  <li> asserts that results written out match the annotations in the index file.
+   */
+  private void testAgainstIndexFile(String indexFileName, String className) {
+    AScene correctScene = createScene(indexFileName);
+
+    String basename = className;
+    if (basename.endsWith(".class")) {
+      basename = basename.substring(0, basename.length() - 6);
+    }
+
+    File tempFile = new File(basename+"_temp.class");
+
+    writeClass(className, tempFile.toString(), correctScene, true);
+
+    AScene generatedScene = new AScene();
+
+    readClass(tempFile.toString(), generatedScene);
+
+    correctScene.prune();
+    generatedScene.prune();
+
+    if (!correctScene.equals(generatedScene)) {
+      String fname1 = className+"-from-indexfile.txt";
+      String fname2 = className+"-via-classfile-scene.txt";
+      writeScene(fname1, correctScene);
+      writeScene(fname2, generatedScene);
+      fail(String.format("For annotations read from %s :%n  After writing to class file and re-reading, result differed.%n  Scene read from index file is in %s .%n  Scene generated from class file is in %s .%n  Also consider running javap -v on %s .%n", indexFileName, fname1, fname2, tempFile));
+    }
+
+    tempFile.delete();
+
+  }
+
+  /**
+   * Runs both types of tests (against class file and index file), on all
+   * classes specified by {@link #allTests}
+   */
+  public void testAll() throws Exception {
+//    for (String s : allTests) {
+//      testAgainstIndexFile(nameIndex(s + ".jaif"), nameClass(s+".class"));
+//      testAgainstClass(nameIndex(s + ".jaif"), nameClass(s));
+//    }
+  }
+
+  /**
+   * Runs a test on class files for package-info.
+   */
+  public void testcPackage() {
+    testAgainstClass(nameIndex("package-info.jaif"),
+        nameClass("package-info"));
+  }
+
+  /**
+   * Runs a test on index files for package-info.
+   */
+  public void testiPackage() {
+    testAgainstIndexFile(nameIndex("package-info.jaif"),
+        nameClass("package-info.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestClassEmpty.
+   */
+  public void testcClassEmpty() {
+    testAgainstClass(nameIndex("TestClassEmpty.jaif"),
+        nameClass("TestClassEmpty"));
+  }
+
+  /**
+   * Runs a test on index files for TestClassEmpty.
+   */
+  public void testiClassEmpty() {
+    testAgainstIndexFile(nameIndex("TestClassEmpty.jaif"),
+        nameClass("TestClassEmpty.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestClassNonEmpty.
+   */
+  public void testcClassNonEmpty() {
+    testAgainstClass(nameIndex("TestClassNonEmpty.jaif"),
+        nameClass("TestClassNonEmpty"));
+  }
+
+  /**
+   * Runs a test on index files for TestClassNonEmpty.
+   */
+  public void testiClassNonEmpty() {
+    testAgainstIndexFile(nameIndex("TestClassNonEmpty.jaif"),
+        nameClass("TestClassNonEmpty.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestFieldSimple.
+   */
+  public void testcFieldSimple() {
+    testAgainstClass(nameIndex("TestFieldSimple.jaif"),
+        nameClass("TestFieldSimple"));
+  }
+
+  /**
+   * Runs a test on index files for TestFieldSimple.
+   */
+  public void testiFieldSimple() {
+    testAgainstIndexFile(nameIndex("TestFieldSimple.jaif"),
+        nameClass("TestFieldSimple.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestFieldGeneric.
+   */
+  public void testcFieldGeneric() {
+    testAgainstClass(nameIndex("TestFieldGeneric.jaif"),
+        nameClass("TestFieldGeneric"));
+  }
+
+  /**
+   * Runs a test on index files for TestFieldGeneric.
+   */
+  public void testiFieldGeneric() {
+    testAgainstIndexFile(nameIndex("TestFieldGeneric.jaif"),
+        nameClass("TestFieldGeneric.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestLocalVariable.
+   */
+  public void testcLocalVariable() {
+    testAgainstClass(nameIndex("TestLocalVariable.jaif"),
+        nameClass("TestLocalVariable"));
+  }
+
+  /**
+   * Runs a test on index files for TestLocalVariable.
+   */
+  public void testiLocalVariable() {
+    testAgainstIndexFile(nameIndex("TestLocalVariable.jaif"),
+        nameClass("TestLocalVariable.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestLocalVariableA.
+   */
+  public void testcLocalVariableA() {
+    testAgainstClass(nameIndex("TestLocalVariableA.jaif"),
+        nameClass("TestLocalVariableA"));
+  }
+
+  /**
+   * Runs a test on index files for TestLocalVariableA.
+   */
+  public void testiLocalVariableA() {
+    testAgainstIndexFile(nameIndex("TestLocalVariableA.jaif"),
+        nameClass("TestLocalVariableA.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestLocalVariableGenericArray.
+   */
+  public void testcLocalVariableGenericArray() {
+    testAgainstClass(nameIndex("TestLocalVariableGenericArray.jaif"),
+        nameClass("TestLocalVariableGenericArray"));
+  }
+
+  /**
+   * Runs a test on index files for TestLocalVariableGenericArray.
+   */
+  public void testiLocalVariableGenericArray() {
+    testAgainstIndexFile(nameIndex("TestLocalVariableGenericArray.jaif"),
+        nameClass("TestLocalVariableGenericArray.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestTypecast.
+   */
+  public void testcTypecast() {
+    testAgainstClass(nameIndex("TestTypecast.jaif"),
+        nameClass("TestTypecast"));
+  }
+
+  /**
+   * Runs a test on index files for TestTypecast.
+   */
+  public void testiTypecast() {
+    testAgainstIndexFile(nameIndex("TestTypecast.jaif"),
+        nameClass("TestTypecast.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestTypecastGenericArray.
+   */
+  public void testcTypecastGenericArray() {
+    testAgainstClass(nameIndex("TestTypecastGenericArray.jaif"),
+        nameClass("TestTypecastGenericArray"));
+  }
+
+  /**
+   * Runs a test on index files for TestTypecastGenericArray.
+   */
+  public void testiTypecastGenericArray() {
+    testAgainstIndexFile(nameIndex("TestTypecastGenericArray.jaif"),
+        nameClass("TestTypecastGenericArray.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestTypeTest.
+   */
+  public void testcTypeTest() {
+    testAgainstClass(nameIndex("TestTypeTest.jaif"),
+        nameClass("TestTypeTest"));
+  }
+
+  /**
+   * Runs a test on index files for TestTypeTest.
+   */
+  public void testiTypeTest() {
+    testAgainstIndexFile(nameIndex("TestTypeTest.jaif"),
+        nameClass("TestTypeTest.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestObjectCreation.
+   */
+  public void testcObjectCreation() {
+    testAgainstClass(nameIndex("TestObjectCreation.jaif"),
+        nameClass("TestObjectCreation"));
+  }
+
+  /**
+   * Runs a test on index files for TestObjectCreation.
+   */
+  public void testiObjectCreation() {
+    testAgainstIndexFile(nameIndex("TestObjectCreation.jaif"),
+        nameClass("TestObjectCreation.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestObjectCreationGenericArray.
+   */
+  public void testcObjectCreationGenericArray() {
+    testAgainstClass(nameIndex("TestObjectCreationGenericArray.jaif"),
+        nameClass("TestObjectCreationGenericArray"));
+  }
+
+  /**
+   * Runs a test on index files for TestObjectCreationGenericArray.
+   */
+  public void testiObjectCreationGenericArray() {
+    testAgainstIndexFile(nameIndex("TestObjectCreationGenericArray.jaif"),
+        nameClass("TestObjectCreationGenericArray.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestMethodReceiver.
+   */
+  public void testcMethodReceiver() {
+    testAgainstClass(nameIndex("TestMethodReceiver.jaif"),
+        nameClass("TestMethodReceiver"));
+  }
+
+  /**
+   * Runs a test on index files for TestMethodReceiver.
+   */
+  public void testiMethodReceiver() {
+    testAgainstIndexFile(nameIndex("TestMethodReceiver.jaif"),
+        nameClass("TestMethodReceiver.class"));
+  }
+
+  /**
+   * Runs a test on class files for TestMethodReturnTypeGenericArray.
+   */
+  public void testcMethodReturnTypeGenericArray() {
+    testAgainstClass(nameIndex("TestMethodReturnTypeGenericArray.jaif"),
+        nameClass("TestMethodReturnTypeGenericArray"));
+  }
+
+  /**
+   * Runs a test on index files for TestMethodReturnTypeGenericArray.
+   */
+  public void testiMethodReturnTypeGenericArray() {
+    testAgainstIndexFile(nameIndex("TestMethodReturnTypeGenericArray.jaif"),
+        nameClass("TestMethodReturnTypeGenericArray.class"));
+  }
+
+//   // Call javap programmatically.
+//   public static void javap(InputStream is, PrintStream ps) {
+//     JavapEnvironment env = new JavapEnvironment();
+//     PrintWriter pw = new PrintWriter(ps);
+//     JavapPrinter javapPrinter = new JavapPrinter(is, pw, env);
+//     javapPrinter.print();
+//     pw.flush();
+//   }
+
+}
diff --git a/scene-lib/test/annotations/tests/classfile/all-annotations.jaif b/scene-lib/test/annotations/tests/classfile/all-annotations.jaif
new file mode 100644
index 0000000..b85e148
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/all-annotations.jaif
@@ -0,0 +1,37 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(RUNTIME)
+
+package annotations.tests.classfile.foo:
+annotation @B: @Retention(RUNTIME)
+	String value
+	
+
+package annotations.tests.classfile.foo:
+annotation @C: @Retention(RUNTIME)
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.foo:
+annotation @D: @Retention(RUNTIME)
+	int fieldA
+	String fieldB
+	int[] fieldC
+
+package annotations.tests.classfile.foo:
+annotation @E: @Retention(RUNTIME)
+	int fieldA
+	String fieldB
+
+package annotations.tests.classfile.foo:
+annotation @F: @Retention(RUNTIME)
+	int fieldA
+	String fieldB
+
+package annotations.tests.classfile.foo:
+annotation @G: @Retention(RUNTIME)
+	int fieldA
+	String fieldB
+	boolean[] fieldC
+	int fieldD
+	int fieldE
+	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestClassEmpty.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestClassEmpty.jaif
new file mode 100644
index 0000000..f39a6fe
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestClassEmpty.jaif
@@ -0,0 +1,6 @@
+package annotations.tests.classfile.foo:
+annotation @A: @java.lang.annotation.Retention(RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests.classfile.cases:
+class TestClassEmpty: @annotations.tests.classfile.foo.A
+	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestClassEmpty.java b/scene-lib/test/annotations/tests/classfile/cases/TestClassEmpty.java
new file mode 100644
index 0000000..4665081
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestClassEmpty.java
@@ -0,0 +1,5 @@
+package annotations.tests.classfile.cases;
+
+public class TestClassEmpty {
+
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestClassNonEmpty.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestClassNonEmpty.jaif
new file mode 100644
index 0000000..c314e31
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestClassNonEmpty.jaif
@@ -0,0 +1,6 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests.classfile.cases:
+class TestClassNonEmpty: @annotations.tests.classfile.foo.A
+	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestClassNonEmpty.java b/scene-lib/test/annotations/tests/classfile/cases/TestClassNonEmpty.java
new file mode 100644
index 0000000..8154109
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestClassNonEmpty.java
@@ -0,0 +1,25 @@
+package annotations.tests.classfile.cases;
+
+public class TestClassNonEmpty {
+  public int i;
+  private String a;
+
+  private TestClassNonEmpty() {
+    i = 0;
+  }
+
+  protected TestClassNonEmpty(String s) {
+    a = s;
+  }
+
+  public int i() {
+    return i;
+  }
+
+  public String a() {
+    String s = new String(a);
+    s = s + s;
+    return s;
+  }
+
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestFieldGeneric.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestFieldGeneric.jaif
new file mode 100644
index 0000000..1814975
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestFieldGeneric.jaif
@@ -0,0 +1,64 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+
+annotation @D: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	int[] fieldC
+
+annotation @E: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+
+annotation @F: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+
+annotation @G: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	boolean[] fieldC
+	int fieldD
+	int fieldE
+	
+package annotations.tests.classfile.cases:
+class TestFieldGeneric: @annotations.tests.classfile.foo.A
+	field s: @annotations.tests.classfile.foo.F(fieldA=1, fieldB="fi")
+	field list: @annotations.tests.classfile.foo.G(fieldA=3, fieldB="three", fieldC={true, false}, fieldD=2, fieldE=4)
+	field set: @annotations.tests.classfile.foo.E(fieldA=2, fieldB = "rh")
+						 @annotations.tests.classfile.foo.F(fieldA = 1, fieldB = "if")
+  field testFieldGeneric: 
+	type:
+  				inner-type 3, 0 : @annotations.tests.classfile.foo.A
+  				
+  				
+  // Set<TestFieldGeneric<Set<TestFieldGeneric>>> nestedSet;
+    
+    field nestedSet: @annotations.tests.classfile.foo.B(value="nested")
+	type:
+  				inner-type 3, 0 : @annotations.tests.classfile.foo.F(fieldA=1, fieldB="n")
+  				inner-type 3, 0 : @annotations.tests.classfile.foo.A
+  				inner-type 3, 0, 3, 0 : @annotations.tests.classfile.foo.B(value="nil")
+  				inner-type 3, 0 : @annotations.tests.classfile.foo.C(fieldA=-2, fieldB="nl")
+  				inner-type 3, 0, 3, 0, 3, 0 : @annotations.tests.classfile.foo.D(fieldA=-1, fieldB="hello", fieldC={3,2,4})
+  				
+  // Map<Set<TestFieldGeneric>, TestFieldGeneric<T>> nestedMap;
+  
+  	field nestedMap: @annotations.tests.classfile.foo.C(fieldA=1, fieldB="nested")
+	    type:
+  				inner-type 3, 1 : @annotations.tests.classfile.foo.A
+  				inner-type 3, 1, 3, 0 : @annotations.tests.classfile.foo.B(value="inner most T")
+  				inner-type 3, 0, 3, 0 : @annotations.tests.classfile.foo.C(fieldA=256, fieldB="hello")
+  				inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="inner most F")
+  
+
+  				
+	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestFieldGeneric.java b/scene-lib/test/annotations/tests/classfile/cases/TestFieldGeneric.java
new file mode 100644
index 0000000..5403d04
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestFieldGeneric.java
@@ -0,0 +1,26 @@
+package annotations.tests.classfile.cases;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class TestFieldGeneric<T> {
+  String s;
+  List<String> list;
+  Set<TestFieldGeneric> set;
+  TestFieldGeneric<T> testFieldGeneric = new TestFieldGeneric<T>();
+
+  public TestFieldGeneric() {
+
+  }
+
+  Set<String> otherSet;
+
+  public String toString() {
+    return s;
+  }
+
+  Set<TestFieldGeneric<Set<TestFieldGeneric>>> nestedSet;
+
+  Map<Set<TestFieldGeneric>, TestFieldGeneric<T>> nestedMap;
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestFieldSimple.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestFieldSimple.jaif
new file mode 100644
index 0000000..b4cde74
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestFieldSimple.jaif
@@ -0,0 +1,12 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+package annotations.tests.classfile.cases:
+class TestFieldSimple: @annotations.tests.classfile.foo.A
+	field i: @annotations.tests.classfile.foo.A
+	field j: @annotations.tests.classfile.foo.B(value="Hello")
+	field o: @annotations.tests.classfile.foo.A @annotations.tests.classfile.foo.B(value="H")
+	field s: @annotations.tests.classfile.foo.B(value="E")
+	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestFieldSimple.java b/scene-lib/test/annotations/tests/classfile/cases/TestFieldSimple.java
new file mode 100644
index 0000000..4628b3c
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestFieldSimple.java
@@ -0,0 +1,9 @@
+package annotations.tests.classfile.cases;
+
+public class TestFieldSimple {
+  public int i;
+  private int j;
+  protected Object o;
+  String s = null;
+  TestFieldSimple f = null;
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariable.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariable.jaif
new file mode 100644
index 0000000..1f9462b
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariable.jaif
@@ -0,0 +1,36 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestLocalVariable: @annotations.tests.classfile.foo.A
+	field i:
+	
+	field s:
+	
+	method <init>()V:
+  	local 1 # 6 + 6 : @annotations.tests.classfile.foo.C(fieldA=166, fieldB="good")
+
+	method <init>(I)V:
+  	local 1 # 0 + 10 : @annotations.tests.classfile.foo.A
+	
+	method <init>(Ljava/lang/Integer;)V:
+		
+	method i()I:
+	
+	method j()I :
+		return : @annotations.tests.classfile.foo.A
+		local 1 # 2 + 5 : @annotations.tests.classfile.foo.B(value="hello")
+ 
+  method someMethod()V:
+       
+  method main([Ljava/lang/String;)V:
+
+  	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariable.java b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariable.java
new file mode 100644
index 0000000..52bd412
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariable.java
@@ -0,0 +1,54 @@
+package annotations.tests.classfile.cases;
+
+import java.util.Set;
+
+public class TestLocalVariable<T> extends Object {
+  public int i;
+
+  public Set<Set> s;
+
+  public TestLocalVariable() {
+    int t = 0;
+    i = 0;
+  }
+
+  public TestLocalVariable(int i) {
+    this.i = i;
+  }
+
+  public TestLocalVariable(Integer j) {
+    int k = 1;
+    k++;
+    this.i = j;
+    k--;
+    this.i = k;
+  }
+
+  public int i() {
+    return i;
+  }
+
+  public int j() {
+    int temp = 1;
+    return j();
+  }
+
+  public static void someMethod() {
+    TestLocalVariable t = new TestLocalVariable();
+    String s = new String();
+    Double d = Double.valueOf(2);
+  }
+
+  public static void main(String[] args) {
+    boolean b = true;
+    boolean b1 = Boolean.TRUE;
+    boolean b2 = (boolean) Boolean.FALSE;
+    b = b1 && b2;
+    if (b || b2) {
+      b1 = b;
+    }
+    if (b1) {
+      System.out.println("Message");
+    }
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableA.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableA.jaif
new file mode 100644
index 0000000..4cd6080
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableA.jaif
@@ -0,0 +1,25 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package annotations.tests.classfile.cases:
+class TestLocalVariableA: @annotations.tests.classfile.foo.A
+	field i:
+	
+	field s:
+	
+	method <init>()V:
+
+	method <init>(I)V:
+	
+	method <init>(Ljava/lang/Integer;)V:
+		
+	method i()I:
+	
+	method j()I :
+		return : @annotations.tests.classfile.foo.A
+ 
+  method someMethod()V:
+       
+  method main([Ljava/lang/String;)V:
+
+  	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableA.java b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableA.java
new file mode 100644
index 0000000..1c00caa
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableA.java
@@ -0,0 +1,54 @@
+package annotations.tests.classfile.cases;
+
+import java.util.Set;
+
+public class TestLocalVariableA<T> extends Object {
+  public int i;
+
+  public Set<Set> s;
+
+  public TestLocalVariableA() {
+    int t = 0;
+    i = 0;
+  }
+
+  public TestLocalVariableA(int i) {
+    this.i = i;
+  }
+
+  public TestLocalVariableA(Integer j) {
+    int k = 1;
+    k++;
+    this.i = j;
+    k--;
+    this.i = k;
+  }
+
+  public int i() {
+    return i;
+  }
+
+  public int j() {
+    int temp = 1;
+    return j();
+  }
+
+  public static void someMethod() {
+    TestLocalVariableA t = new TestLocalVariableA();
+    String s = new String();
+    Double d = Double.valueOf(2);
+  }
+
+  public static void main(String[] args) {
+    boolean b = true;
+    boolean b1 = Boolean.TRUE;
+    boolean b2 = (boolean) Boolean.FALSE;
+    b = b1 && b2;
+    if (b || b2) {
+      b1 = b;
+    }
+    if (b1) {
+      System.out.println("Message");
+    }
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableGenericArray.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableGenericArray.jaif
new file mode 100644
index 0000000..06bbde3
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableGenericArray.jaif
@@ -0,0 +1,62 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestLocalVariableGenericArray:
+	
+	method <init>()V:
+		local 2 # 37 + 55 : @annotations.tests.classfile.foo.B(value="good")
+		    type:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="first param")
+			inner-type 3, 1 : @annotations.tests.classfile.foo.B(value="second param")
+		
+	method someMethod()V:
+		local 1 # 8 + 26 : @annotations.tests.classfile.foo.B(value="bad")
+		    type:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.C(fieldA=0,fieldB="String")
+	
+	method someMethod2(I)I:
+		local 2 # 8 + 66 : @annotations.tests.classfile.foo.C(fieldA=0, fieldB="Boolean")
+		    type:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.A
+		local 3 # 16 + 58 : @annotations.tests.classfile.foo.A
+		    type:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="inner-type")
+		
+	method someMethod3()Z:
+		local 1 # 8 + 70 : @annotations.tests.classfile.foo.B(value="t")
+		    type:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="map key string")
+			inner-type 3, 1 : @annotations.tests.classfile.foo.B(value="map value set")
+			inner-type 3, 1, 3, 0 : @annotations.tests.classfile.foo.A
+		local 2 # 10 + 68 : 
+		    type:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.A
+			inner-type 3, 1 : @annotations.tests.classfile.foo.C(fieldA=1, fieldB="set of maps")
+			inner-type 3, 1, 3, 0 : @annotations.tests.classfile.foo.B(value="maps")
+			inner-type 3, 1, 3, 0, 3, 0 : @annotations.tests.classfile.foo.B(value="map key is integer")
+			inner-type 3, 1, 3, 0, 3, 1 : @annotations.tests.classfile.foo.B(value="map value is 2-d array")
+			inner-type 3, 1, 3, 0, 3, 1, 0, 0 : @annotations.tests.classfile.foo.B(value="first dimension")
+			inner-type 3, 1, 3, 0, 3, 1, 0, 0, 0, 0 : @annotations.tests.classfile.foo.B(value ="second dimension")
+			
+	method someMethod4()V:
+		local 1 # 8 + 10 :
+		    type:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.A
+    
+    
+    
+    
+    
+    
+    
+    
+    
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableGenericArray.java b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableGenericArray.java
new file mode 100644
index 0000000..ec3dadc
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestLocalVariableGenericArray.java
@@ -0,0 +1,66 @@
+package annotations.tests.classfile.cases;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class TestLocalVariableGenericArray {
+
+  Integer i;
+
+  Map<String, Set<String>> map1;
+
+  Map<String, ArrayList<Map<String, String>>> map2;
+
+  public TestLocalVariableGenericArray() {
+    int k = 1;
+    for (Map<String, String> e : map2.get("4gf")) {
+      if (k < 5) {
+        k = map2.get("").indexOf(new ArrayList<Map<String,String>>());
+      } else {
+        k = this.i.intValue() + 5;
+      }
+      k++;
+    }
+  }
+
+  public void someMethod() {
+    Set<String> s = new HashSet<String>();
+    s.add(new String());
+    s.add(s.toString());
+  }
+
+  public int someMethod2(int i) {
+    Set<Boolean> s = new HashSet<Boolean>();
+    Set<Integer> ints = new HashSet<Integer>();
+    boolean b = someMethod3();
+    if (s.iterator().next() & b) {
+      return b ? i : ints.iterator().next();
+    }
+    return i;
+  }
+
+  public boolean someMethod3() {
+    Map<String, Set<String>> t = new HashMap<String, Set<String>>();
+    Map<String, Set<Map<Integer, String[][]>>> s = null;
+
+    s.get("3").add(new HashMap<Integer, String[][]>());
+
+    s.get("4").iterator().next().get(3)[2][4] = "Hello";
+
+    return true;
+  }
+
+  protected void someMethod4() {
+    try {
+      Set<String> s = new HashSet<String>();
+      Map<Set<String>, String> m;
+      throw new RuntimeException("Hello");
+    } catch (Exception e) {
+      System.out.println(i);
+    }
+  }
+
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestMethodReceiver.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReceiver.jaif
new file mode 100644
index 0000000..7e0694f
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReceiver.jaif
@@ -0,0 +1,26 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestMethodReceiver:
+	
+  method test()V : 
+  	receiver : @annotations.tests.classfile.foo.A
+  						 @annotations.tests.classfile.foo.B(value="first method")
+  						 
+	method test2()V :
+	  receiver : @annotations.tests.classfile.foo.C(fieldA=2, fieldB="rec")
+	  
+	method test3()V :
+		receiver : @annotations.tests.classfile.foo.A
+		
+	method test4()V :
+		receiver : @annotations.tests.classfile.foo.B(value="last method")
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestMethodReceiver.java b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReceiver.java
new file mode 100644
index 0000000..38e324d
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReceiver.java
@@ -0,0 +1,26 @@
+package annotations.tests.classfile.cases;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class TestMethodReceiver {
+
+  public void test() {
+    System.out.println("test()");
+  }
+
+  private void test2() {
+    System.out.println("test2()");
+  }
+
+  protected void test3() {
+    System.out.println("test3()");
+  }
+
+  void test4() {
+    System.out.println("test4()");
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray.jaif
new file mode 100644
index 0000000..902ba7e
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray.jaif
@@ -0,0 +1,45 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @Target(TYPE_USE)
+
+annotation @B: @Retention(value=RUNTIME) @Target(TYPE_USE)
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @Target(TYPE_USE)
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestMethodReturnTypeGenericArray:
+	method test()Ljava/util/List; :
+		return: @annotations.tests.classfile.foo.A
+	
+	method test2()Ljava/util/List; :
+		return: @annotations.tests.classfile.foo.B(value="single-depth")
+			inner-type 3, 0 : @annotations.tests.classfile.foo.A
+
+	method test3()[Ljava/lang/String; :
+		return: @annotations.tests.classfile.foo.A
+			inner-type 0, 0 : @annotations.tests.classfile.foo.B(value="on array element")
+		
+	method test4()[[Ljava/lang/String; :
+		return:  @annotations.tests.classfile.foo.A
+			inner-type 0, 0 : @annotations.tests.classfile.foo.B(value="on")
+			inner-type 0, 0, 0, 0 : @annotations.tests.classfile.foo.B(value="in")
+		
+	method test5()Ljava/util/Set; :
+		return:
+			inner-type 3, 0 : @annotations.tests.classfile.foo.A
+			inner-type 3, 0, 0, 0 : @annotations.tests.classfile.foo.B(value="two-deep")
+		
+  method test6()Ljava/util/Map; :
+	return:
+		inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="map as key")
+		inner-type 3, 1, 3, 0 : @annotations.tests.classfile.foo.B(value="array of value")
+		inner-type 3, 1, 3, 0, 0, 0 : @annotations.tests.classfile.foo.B(value="inner-most value")
+		inner-type 3, 1 : @annotations.tests.classfile.foo.B(value="set as value")
+		inner-type 3, 0, 3, 0, 0, 0 : @annotations.tests.classfile.foo.B(value="innermost key or key")
+		inner-type 3, 0, 3, 0 : @annotations.tests.classfile.foo.A
+		inner-type 3, 0, 3, 1 : @annotations.tests.classfile.foo.C(fieldA=01, fieldB="value of key")
+		inner-type 3, 0, 3, 1, 3, 0: @annotations.tests.classfile.foo.A
+  
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray.java b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray.java
new file mode 100644
index 0000000..d8ea2d3
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestMethodReturnTypeGenericArray.java
@@ -0,0 +1,32 @@
+package annotations.tests.classfile.cases;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class TestMethodReturnTypeGenericArray {
+
+  public List test() {
+    return null;
+  }
+
+  public List<String> test2() {
+    return null;
+  }
+
+  public String[] test3() {
+    return null;
+  }
+
+  public String[][] test4() {
+    return null;
+  }
+
+  public Set<String[]> test5() {
+    return null;
+  }
+
+  public Map<Map<String[], Set<String>>, Set<String[]>> test6() {
+    return null;
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreation.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreation.jaif
new file mode 100644
index 0000000..4aa1a9a
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreation.jaif
@@ -0,0 +1,31 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestObjectCreation:
+	
+  method test()V : 
+  	new # 1 : @annotations.tests.classfile.foo.B(value="first new")
+  	new # 12 : @annotations.tests.classfile.foo.B(value="a string")
+  	new # 23 : @annotations.tests.classfile.foo.A
+  	  	
+  method test2()V :
+  	new # 7 : @annotations.tests.classfile.foo.A
+  	new # 14 : @annotations.tests.classfile.foo.A
+  	
+  method test3()V :
+  	new # 1 : @annotations.tests.classfile.foo.B(value="new")
+		new # 12 : @annotations.tests.classfile.foo.A
+  	
+  method test4()V :
+  	new # 1 : @annotations.tests.classfile.foo.A
+  	new # 13 : @annotations.tests.classfile.foo.B(value="self test")
+  	  	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreation.java b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreation.java
new file mode 100644
index 0000000..3b0cdd5
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreation.java
@@ -0,0 +1,30 @@
+package annotations.tests.classfile.cases;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class TestObjectCreation {
+  public Object o;
+
+  public void test() {
+    o = new Object();
+    o = new String();
+    o = new String("");
+  }
+
+  public void test2() {
+    o = "str";
+    o = new ArrayList();
+  }
+
+  public void test3() {
+    o = new HashSet();
+    o = new HashMap();
+  }
+
+  public void test4() {
+    o = new Integer(2);
+    o = new TestObjectCreation();
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreationGenericArray.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreationGenericArray.jaif
new file mode 100644
index 0000000..cf8ca89
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreationGenericArray.jaif
@@ -0,0 +1,41 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestObjectCreationGenericArray:
+	
+  method test()V : 
+  	new # 3 : @annotations.tests.classfile.foo.B(value="first new")
+  		inner-type 0, 0 : @annotations.tests.classfile.foo.A
+  	  	
+  method test2()V :
+  	new # 23 : @annotations.tests.classfile.foo.A
+  		inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="str")
+  	
+  method test3()V :
+  	new # 1 : @annotations.tests.classfile.foo.B(value="new")
+			inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="map")
+			inner-type 3, 0, 3, 0 : @annotations.tests.classfile.foo.A
+			inner-type 3, 0, 3, 1 : @annotations.tests.classfile.foo.B(value="map key string")
+		new # 12 : @annotations.tests.classfile.foo.A
+  		inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="first level")
+  		inner-type 3, 1 : @annotations.tests.classfile.foo.B(value="value")
+  		inner-type 3, 1, 3, 0 : @annotations.tests.classfile.foo.B(value="on the array")
+  		inner-type 3, 1, 3, 0, 0, 0: @annotations.tests.classfile.foo.B(value="on array elements")
+  		
+  method test4()V :
+  	new # 1 : @annotations.tests.classfile.foo.A
+  		inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="key")
+  		inner-type 3, 1 : @annotations.tests.classfile.foo.B(value="value")
+  		inner-type 3, 0, 0, 0 :@annotations.tests.classfile.foo.B(value="key element")
+  		inner-type 3, 1, 3, 0 : @annotations.tests.classfile.foo.B(value="value array")
+  		inner-type 3, 1, 3, 0, 0, 0 : @annotations.tests.classfile.foo.B(value="value array element")
+  		  	  	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreationGenericArray.java b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreationGenericArray.java
new file mode 100644
index 0000000..05552fb
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestObjectCreationGenericArray.java
@@ -0,0 +1,29 @@
+package annotations.tests.classfile.cases;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class TestObjectCreationGenericArray {
+  public Object o;
+
+  public void test() {
+    o = new int[10];
+  }
+
+  public void test2() {
+    o = "str";
+    o = new ArrayList<String>();
+  }
+
+  public void test3() {
+    o = new HashSet<Map<String, String>>();
+    o = new HashMap<String, Set<String[]>>();
+  }
+
+  public void test4() {
+    o = new HashMap<String[], Set<String[]>>();
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestTypeTest.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestTypeTest.jaif
new file mode 100644
index 0000000..45c864a
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestTypeTest.jaif
@@ -0,0 +1,33 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestTypeTest:
+	
+  method test()V :
+  	instanceof # 4 : @annotations.tests.classfile.foo.B(value="ismap")
+  	instanceof # 14 : @annotations.tests.classfile.foo.A
+  	instanceof # 24 : @annotations.tests.classfile.foo.B(value="islist")
+  	
+  method test2()V :
+  	instanceof # 4 : @annotations.tests.classfile.foo.A
+  	instanceof # 14 : @annotations.tests.classfile.foo.A
+  	
+  method test3()V :
+  	instanceof # 4 : @annotations.tests.classfile.foo.B(value="instanceof object")
+  	
+  method test4()V :
+  	instanceof # 12 : @annotations.tests.classfile.foo.A
+  	instanceof # 28 : @annotations.tests.classfile.foo.B(value="second")
+  	instanceof # 44 : @annotations.tests.classfile.foo.A
+  	instanceof # 60 : @annotations.tests.classfile.foo.B(value="fourth")
+  	instanceof # 76 : @annotations.tests.classfile.foo.A
+  	
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestTypeTest.java b/scene-lib/test/annotations/tests/classfile/cases/TestTypeTest.java
new file mode 100644
index 0000000..f009fa2
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestTypeTest.java
@@ -0,0 +1,52 @@
+package annotations.tests.classfile.cases;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class TestTypeTest {
+  public Object o;
+
+  public void test() {
+    if (o instanceof Map) {
+      if (o instanceof Set) {
+        if (o instanceof List) {
+          o = new Object();
+        }
+      }
+    }
+  }
+
+  public void test2() {
+    if (o instanceof List) {
+      if (o instanceof ArrayList) {
+        o = new Object();
+      }
+    }
+  }
+
+  public void test3() {
+    if (!(o instanceof Object)) {
+      o = new Object();
+    }
+  }
+
+  public void test4() {
+    Class c = o.getClass();
+    if (o instanceof Boolean) {
+      c = Boolean.class;
+    } else if (o instanceof Integer) {
+      c = Integer.class;
+    } else if (o instanceof Character) {
+      c = Character.class;
+    } else if (o instanceof String) {
+      c = String.class;
+    } else if (o instanceof List) {
+      c = List.class;
+    } else {
+      c = int.class;
+    }
+    System.out.println(c);
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestTypecast.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestTypecast.jaif
new file mode 100644
index 0000000..3aa2b23
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestTypecast.jaif
@@ -0,0 +1,28 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestTypecast:
+	
+  method test()V :
+  	typecast # 21 : @annotations.tests.classfile.foo.A
+  	typecast # 32 : @annotations.tests.classfile.foo.B(value="second cast")
+  	typecast # 59 : @annotations.tests.classfile.foo.C(fieldA=3, fieldB="cast")
+  	typecast # 70 : @annotations.tests.classfile.foo.C(fieldA=4, fieldB="cast")
+  	typecast # 99, 0 : @annotations.tests.classfile.foo.A
+  	typecast # 99, 1 : @annotations.tests.classfile.foo.B(value="99")
+  	    inner-type 3, 0: @annotations.tests.classfile.foo.C(fieldA=0, fieldB="99")
+  	typecast # 110, 0 : @annotations.tests.classfile.foo.A
+  	typecast # 110, 1 : @annotations.tests.classfile.foo.B(value="110")
+  	    inner-type 3, 0 : @annotations.tests.classfile.foo.C(fieldA=0, fieldB="110")
+  	    inner-type 3, 1 : @annotations.tests.classfile.foo.C(fieldA=1, fieldB="110")
+  	    inner-type 3, 1, 2, 0: @annotations.tests.classfile.foo.C(fieldA=10, fieldB="110")
+  	typecast # 110, 2 : @annotations.tests.classfile.foo.B(value="59")
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestTypecast.java b/scene-lib/test/annotations/tests/classfile/cases/TestTypecast.java
new file mode 100644
index 0000000..14ac2d9
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestTypecast.java
@@ -0,0 +1,31 @@
+package annotations.tests.classfile.cases;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class TestTypecast {
+  public Object o;
+  public String s;
+  public Integer i;
+  public Boolean b;
+  public Set set;
+  public HashSet hset;
+  public Map map;
+
+  public void test() {
+    o = (Object) o;
+    o = (Object) s;
+    s = (String) o;
+    i = (Integer) o;
+    b = (Boolean) b;
+    set = (HashSet) hset;
+    hset = (HashSet) set;
+    map = (Map) hset;
+    int pi = 0;
+    i = pi;
+    o = pi;
+    o = (String & Comparable<String>) o;
+    o = (String & Map<String, ? extends Set<String>> & CharSequence) o;
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestTypecastGenericArray.jaif b/scene-lib/test/annotations/tests/classfile/cases/TestTypecastGenericArray.jaif
new file mode 100644
index 0000000..12b01c2
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestTypecastGenericArray.jaif
@@ -0,0 +1,50 @@
+package annotations.tests.classfile.foo:
+annotation @A: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+annotation @B: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	String value
+
+// annotation @C: @Retention(value=CLASS) @java.lang.annotation.Target(value={TYPE_USE})
+annotation @C: @Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+	int fieldA
+	String fieldB
+	
+package annotations.tests.classfile.cases:
+class TestTypecastGenericArray:
+	
+  method test()V :
+  	typecast # 21 : @annotations.tests.classfile.foo.A
+  	typecast # 32 : @annotations.tests.classfile.foo.B(value="second")
+  	
+  method test2()V :
+  	typecast # 5 : 
+  		inner-type 3, 0 : @annotations.tests.classfile.foo.A
+    typecast # 16 : @annotations.tests.classfile.foo.B(value="B")
+    	inner-type 3, 0 : @annotations.tests.classfile.foo.C(fieldA=2,fieldB="")
+    	
+  method test3()V :
+  	typecast # 20 : 
+  		inner-type 3, 0 : @annotations.tests.classfile.foo.A
+    typecast # 23 :
+    	inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="v")
+    	
+  method test4()V :
+    typecast # 5 :
+    	inner-type 3, 0 : @annotations.tests.classfile.foo.A
+    	inner-type 3, 1 : @annotations.tests.classfile.foo.B(value="second")
+    typecast # 15 : @annotations.tests.classfile.foo.A	
+    	inner-type 3, 0 : @annotations.tests.classfile.foo.A
+    typecast # 30 : @ annotations.tests.classfile.foo.B(value="set")
+    	inner-type 3, 0 :
+    typecast # 43 : 
+    	inner-type 3, 1 : @annotations.tests.classfile.foo.A
+    typecast # 53 : @annotations.tests.classfile.foo.B(value="on the set")
+    	inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="on value")
+    	
+  method test5()V :
+  	typecast # 4 :
+  		inner-type 3, 0 : @annotations.tests.classfile.foo.B(value="string is key")
+  		inner-type 3, 1 : @annotations.tests.classfile.foo.B(value="2d-array is value")
+  		inner-type 3, 1, 0, 0 : @annotations.tests.classfile.foo.B(value="first dimension")
+  		inner-type 3, 1, 0, 0, 0, 0 : @annotations.tests.classfile.foo.B(value="second dimension")
+  		
diff --git a/scene-lib/test/annotations/tests/classfile/cases/TestTypecastGenericArray.java b/scene-lib/test/annotations/tests/classfile/cases/TestTypecastGenericArray.java
new file mode 100644
index 0000000..9d4b6f4
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/TestTypecastGenericArray.java
@@ -0,0 +1,50 @@
+package annotations.tests.classfile.cases;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class TestTypecastGenericArray {
+  public Object o;
+  public String s;
+  public Integer i;
+  public Boolean b;
+  public Set<String> set;
+  public HashSet<Set<String>> hset;
+  public Map<Set<String>,Set<Map<String,Set<String>>>> map;
+
+  public void test() {
+    o = (Object) o;
+    o = (Object) s;
+    s = (String) o;
+    i = (Integer) o;
+    b = (Boolean) b;
+  }
+
+  @SuppressWarnings({"unchecked"})
+  public void test2() {
+    set = (HashSet<String>) o;
+    set = (Set<String>) o;
+  }
+
+  @SuppressWarnings({"unchecked"})
+  public void test3() {
+    set = (HashSet<String>) map.keySet().iterator().next();
+    hset = (HashSet<Set<String>>) o;
+  }
+
+  @SuppressWarnings("unchecked")
+  public void test4() {
+    map = (Map<Set<String>, Set<Map<String, Set<String>>>>) o;
+    Set<Map<String, Set<String>>> t = (Set<Map<String, Set<String>>>) o;
+    set = map.get(null).iterator().next().get("");
+  }
+
+  @SuppressWarnings("unchecked")
+  public void test5() {
+    Map<String, String[][]> m;
+    m = (Map<String, String[][]>) o;
+    System.out.println(m);
+  }
+}
diff --git a/scene-lib/test/annotations/tests/classfile/cases/package-info.jaif b/scene-lib/test/annotations/tests/classfile/cases/package-info.jaif
new file mode 100644
index 0000000..a2d4c76
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/package-info.jaif
@@ -0,0 +1,5 @@
+package java.lang:
+annotation @Deprecated: @java.lang.annotation.Target(value={CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE}) @java.lang.annotation.Documented @java.lang.annotation.Retention(value=RUNTIME)
+
+package annotations.tests.classfile.cases: @java.lang.Deprecated
+
diff --git a/scene-lib/test/annotations/tests/classfile/cases/package-info.java b/scene-lib/test/annotations/tests/classfile/cases/package-info.java
new file mode 100644
index 0000000..6e0250e
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/cases/package-info.java
@@ -0,0 +1,3 @@
+@Deprecated
+package annotations.tests.classfile.cases;
+
diff --git a/scene-lib/test/annotations/tests/classfile/foo/A.java b/scene-lib/test/annotations/tests/classfile/foo/A.java
new file mode 100644
index 0000000..f81a84c
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/foo/A.java
@@ -0,0 +1,8 @@
+package annotations.tests.classfile.foo;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+public @interface A {
+}
diff --git a/scene-lib/test/annotations/tests/classfile/foo/B.java b/scene-lib/test/annotations/tests/classfile/foo/B.java
new file mode 100644
index 0000000..97bdb3d
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/foo/B.java
@@ -0,0 +1,9 @@
+package annotations.tests.classfile.foo;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+public @interface B {
+  String value();
+}
diff --git a/scene-lib/test/annotations/tests/classfile/foo/C.java b/scene-lib/test/annotations/tests/classfile/foo/C.java
new file mode 100644
index 0000000..31e6a41
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/foo/C.java
@@ -0,0 +1,11 @@
+package annotations.tests.classfile.foo;
+
+import java.lang.annotation.*;
+
+// @Retention(RetentionPolicy.CLASS)
+@Retention(RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+public @interface C {
+  int fieldA();
+  String fieldB();
+}
diff --git a/scene-lib/test/annotations/tests/classfile/foo/D.java b/scene-lib/test/annotations/tests/classfile/foo/D.java
new file mode 100644
index 0000000..d7217a3
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/foo/D.java
@@ -0,0 +1,11 @@
+package annotations.tests.classfile.foo;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+public @interface D {
+  int fieldA();
+  String fieldB();
+  int[] fieldC();
+}
diff --git a/scene-lib/test/annotations/tests/classfile/foo/E.java b/scene-lib/test/annotations/tests/classfile/foo/E.java
new file mode 100644
index 0000000..0901776
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/foo/E.java
@@ -0,0 +1,10 @@
+package annotations.tests.classfile.foo;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+public @interface E {
+  int fieldA();
+  /*@A*/ String fieldB();
+}
diff --git a/scene-lib/test/annotations/tests/classfile/foo/F.java b/scene-lib/test/annotations/tests/classfile/foo/F.java
new file mode 100644
index 0000000..c787470
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/foo/F.java
@@ -0,0 +1,10 @@
+package annotations.tests.classfile.foo;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+public @interface F {
+  int fieldA();
+  /*@B(value="value from annotation @F")*/ String fieldB();
+}
diff --git a/scene-lib/test/annotations/tests/classfile/foo/G.java b/scene-lib/test/annotations/tests/classfile/foo/G.java
new file mode 100644
index 0000000..20fa822
--- /dev/null
+++ b/scene-lib/test/annotations/tests/classfile/foo/G.java
@@ -0,0 +1,13 @@
+package annotations.tests.classfile.foo;
+
+import java.lang.annotation.*;
+
+@Retention(RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE)
+public @interface G {
+  int fieldA();
+  String fieldB();
+  boolean[] fieldC();
+  /*@D(fieldA=1, fieldB="value from annotation @G", fieldC={3,2})*/ int fieldD();
+  /*@D(fieldA=2,fieldB="value from annotation @G",fieldC={3,2})*/ int fieldE();
+}
diff --git a/scene-lib/test/annotations/tests/executable/ClassToIndexDemo.java b/scene-lib/test/annotations/tests/executable/ClassToIndexDemo.java
new file mode 100644
index 0000000..c7bfdbd
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/ClassToIndexDemo.java
@@ -0,0 +1,22 @@
+package annotations.tests.executable;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.*;
+
+import annotations.el.*;
+import annotations.io.*;
+import annotations.io.classfile.*;
+
+/**
+ * A(n) <code>ClassToIndexDemo</code> is/represents ...
+ */
+public class ClassToIndexDemo {
+    public static void main(String[] args) throws IOException, DefException {
+        AScene s = new AScene();
+        ClassFileReader.read(s, args[0]);
+        IndexFileWriter.write(s, new OutputStreamWriter(System.out));
+    }
+}
diff --git a/scene-lib/test/annotations/tests/executable/Example.java b/scene-lib/test/annotations/tests/executable/Example.java
new file mode 100644
index 0000000..e7a3f86
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/Example.java
@@ -0,0 +1,88 @@
+package annotations.tests.executable;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.tainting.qual.Tainted;
+*/
+
+import java.io.*;
+import java.util.*;
+
+import annotations.*;
+import annotations.el.*;
+import annotations.io.*;
+
+/**
+ * Prints information about Tainted and NonNull annotations on a given class.
+ * Invoke as:
+ * <pre>
+ * java Example <i>input.jaif</i> <i>ClassToProcess</i> <i>output.jaif</i>
+ * </pre>
+ */
+public class Example {
+  public static void main(String [] args) {
+    AScene scene;
+
+    if (! new File(args[0]).exists()) {
+      try {
+        throw new Error(String.format("Cannot find file %s in directory %s",
+                                    args[0], new File(".").getCanonicalPath()));
+      } catch (IOException e) {
+        throw new Error("This can't happen: ", e);
+      }
+    }
+
+    // System.out.println("Reading in " + args[0]);
+    try {
+      scene = new AScene();
+      IndexFileParser.parseFile(args[0], scene);
+    } catch (IOException e) {
+      e.printStackTrace(System.err);
+      return;
+    }
+
+    System.out.println("Processing class " + args[1]);
+    // Get a handle on the class
+    AClass clazz1 = scene.classes.get(args[1]);
+    if (clazz1 == null) {
+      System.out.println("Class " + args[1] + " is not mentioned in annotation file " + args[0]);
+      return;
+    }
+    AClass clazz = (AClass) clazz1;
+
+    for (Map.Entry<String, AMethod> me : clazz.methods.entrySet()) {
+      AMethod method = me.getValue();
+
+      Annotation rro = method.receiver.type.lookup("Tainted");
+      if (rro == null) {
+        System.out.println("Method " + me.getKey()
+            + " might modify the receiver");
+      } else {
+        System.out.println("Method " + me.getKey()
+            + " must not modify the receiver");
+      }
+
+      ATypeElement paramType1 = method.parameters.vivify(0).type;
+      Annotation p1nn = paramType1.lookup("NonNull");
+      if (p1nn == null) {
+        System.out.println("Annotating type of first parameter of "
+            + me.getKey() + " nonnull");
+
+        paramType1.tlAnnotationsHere.add(Annotations.aNonNull);
+      }
+    }
+
+    // System.out.println("Writing out " + args[2]);
+    try {
+      IndexFileWriter.write(scene, new FileWriter(args[2]));
+    } catch (IOException e) {
+      e.printStackTrace(System.err);
+      return;
+    } catch (DefException e) {
+      e.printStackTrace(System.err);
+      return;
+    }
+
+    System.out.println("Success.");
+  }
+}
diff --git a/scene-lib/test/annotations/tests/executable/JavapDemo.java b/scene-lib/test/annotations/tests/executable/JavapDemo.java
new file mode 100644
index 0000000..053768b
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/JavapDemo.java
@@ -0,0 +1,28 @@
+package annotations.tests.executable;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.*;
+
+import plume.FileIOException;
+
+import annotations.el.*;
+import annotations.io.*;
+
+public class JavapDemo {
+    public static void main(String[] args) throws IOException, FileIOException, DefException {
+        /*@NonNull*/ AScene scene = new AScene();
+
+        String filename = args[0];
+        LineNumberReader lnr = new LineNumberReader(new FileReader(filename));
+        try {
+            JavapParser.parse(new FileReader(filename), scene);
+        } catch (ParseException e) {
+            throw new FileIOException(lnr, filename, e);
+        }
+
+        IndexFileWriter.write(scene, new OutputStreamWriter(System.out));
+    }
+}
diff --git a/scene-lib/test/annotations/tests/executable/RegurgitateDemo.java b/scene-lib/test/annotations/tests/executable/RegurgitateDemo.java
new file mode 100644
index 0000000..d9349fb
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/RegurgitateDemo.java
@@ -0,0 +1,34 @@
+package annotations.tests.executable;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.NonNull;
+*/
+
+import java.io.*;
+
+import annotations.el.*;
+import annotations.io.*;
+
+public class RegurgitateDemo {
+    public static void main(/*@NonNull*/ String /*@NonNull*/ [] args) {
+        // String sampleIndexFile = "package pkg: annotation @A: int value class
+        // foo: @pkg.A(value=dinglewompus)";
+        /*@NonNull*/ AScene scene = new AScene();
+        try {
+            LineNumberReader in = new LineNumberReader(new FileReader("test2-2.jaif"));
+            IndexFileParser.parse(in, scene);
+
+            System.out.println("regurgitating:");
+            IndexFileWriter.write(scene, new FileWriter("test2-3.jaif"));
+        } catch (ParseException p) {
+            p.printStackTrace(System.err);
+        } catch (DefException p) {
+            p.printStackTrace(System.err);
+        } catch (IOException e) {
+            // won't happen for a StringReader
+            assert false;
+        }
+        // set a breakpoint here to inspect the scene
+        System.out.println("finished");
+    }
+}
diff --git a/scene-lib/test/annotations/tests/executable/TestSceneLib.java b/scene-lib/test/annotations/tests/executable/TestSceneLib.java
new file mode 100644
index 0000000..049652a
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/TestSceneLib.java
@@ -0,0 +1,640 @@
+package annotations.tests.executable;
+
+/*>>>
+import org.checkerframework.checker.nullness.qual.*;
+*/
+
+import java.io.*;
+import java.util.*;
+import java.lang.annotation.RetentionPolicy;
+
+import com.sun.tools.classfile.TypeAnnotation.Position.TypePathEntryKind;
+import com.sun.tools.javac.code.TypeAnnotationPosition;
+
+import junit.framework.*;
+import annotations.*;
+import annotations.el.*;
+import annotations.field.*;
+import annotations.io.*;
+
+import plume.FileIOException;
+
+public class TestSceneLib extends TestCase {
+    LineNumberReader openPackagedIndexFile(String name) {
+        return new LineNumberReader(new InputStreamReader(
+                (InputStream) TestSceneLib.class.getResourceAsStream(name)));
+    }
+
+    static final String fooIndexContents =
+            "package:\n" +
+            "annotation @Ready: @Retention(RUNTIME)\n" +
+            "annotation @Author: @Retention(CLASS)\n" +
+            "String value\n" +
+            "class Foo:\n" +
+            "field x: @Ready\n" +
+            "method y()Z:\n" +
+            "parameter #5:\n" +
+            "type:\n" +
+            "inner-type 0, 0, 3, 2:\n" +
+            "@Author(value=\"Matt M.\")\n";
+
+    public static AnnotationDef adAuthor
+      = Annotations.createValueAnnotationDef("Author",
+                                             Annotations.asRetentionClass,
+                                             BasicAFT.forType(String.class));
+
+    static final AnnotationDef ready =
+                    new AnnotationDef(
+                            "Ready",
+                            Annotations.asRetentionRuntime,
+                            Annotations.noFieldTypes);
+    static final AnnotationDef readyClassRetention =
+                    new AnnotationDef(
+                            "Ready",
+                            Annotations.asRetentionClass,
+                            Annotations.noFieldTypes);
+
+
+    /**
+     * Parse indexFileContents as an annotation file, merging the results
+     * into s; the final state of s should equal expectScene.
+     */
+    void doParseTest(String indexFileContents,
+                     AScene s,
+                     AScene expectScene) {
+        try {
+            IndexFileParser.parseString(indexFileContents, s);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        if (! expectScene.equals(s)) {
+          System.err.println("expectScene does not equal s");
+          String esu = expectScene.unparse();
+          String su = s.unparse();
+          if (esu.equals(su)) {
+            System.err.println("(but their printed representations are the same)");
+          }
+          System.err.println(esu);
+          System.err.println(su);
+        }
+        assertEquals(expectScene, s);
+    }
+
+    // lazy typist!
+    AScene newScene() {
+        return new AScene();
+    }
+
+    void doParseTest(String index,
+                     /*@NonNull*/ AScene expectScene) {
+        AScene s = newScene();
+        doParseTest(index, s, expectScene);
+    }
+
+    private Annotation createEmptyAnnotation(AnnotationDef def) {
+        return new Annotation(def, Collections.<String, Object> emptyMap());
+    }
+
+    public void testEquals() {
+        AScene s1 = newScene(), s2 = newScene();
+
+        s1.classes.vivify("Foo");
+        s1.classes.vivify("Foo").fields.vivify("x");
+        s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
+                .add(createEmptyAnnotation(ready));
+
+        s2.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
+                .add(Annotations.aNonNull);
+        s2.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
+                .add(createEmptyAnnotation(ready));
+
+        assertEquals(false, s1.equals(s2));  // FIXME: why does assertion fail?
+
+        s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
+                .add(Annotations.aNonNull);
+
+        assertEquals(true, s1.equals(s2));
+    }
+
+    public void testStoreParse1() {
+        AScene s1 = newScene();
+
+        s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
+                .add(createEmptyAnnotation(ready));
+        Annotation myAuthor = Annotations.createValueAnnotation(adAuthor, "Matt M.");
+        s1.classes.vivify("Foo");
+        s1.classes.vivify("Foo").methods.vivify("y()Z");
+        s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5);
+        @SuppressWarnings("unused")
+        Object dummy =
+          s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type;
+        @SuppressWarnings("unused")
+        Object dummy2 =
+          s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type.innerTypes;
+        s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type.innerTypes
+                .vivify(new InnerTypeLocation(
+                        TypeAnnotationPosition.getTypePathFromBinary(
+                                Arrays.asList(new Integer[] { 0, 0, 3, 2 })))).tlAnnotationsHere
+                .add(myAuthor);
+
+        doParseTest(fooIndexContents, s1);
+    }
+
+    private void checkConstructor(AMethod constructor) {
+        Annotation ann = ((Annotation) constructor.receiver.type.lookup("p2.D"));
+        assertEquals(Collections.singletonMap("value", "spam"), ann.fieldValues);
+        ATypeElement l = (ATypeElement) constructor.body.locals
+                        .get(new LocalLocation(1, 3, 5)).type;
+        AElement i = (AElement) l.innerTypes.get(new InnerTypeLocation(
+                TypeAnnotationPosition.getTypePathFromBinary(
+                                Arrays.asList(new Integer[] { 0, 0 }))));
+        assertNotNull(i.lookup("p2.C"));
+        AField l2 =
+                constructor.body.locals.get(new LocalLocation(1, 3, 6));
+        assertNull(l2);
+    }
+
+    public void testParseRetrieve1() throws Exception {
+        LineNumberReader fr = openPackagedIndexFile("test1.jaif");
+        AScene s1 = newScene();
+        IndexFileParser.parse(fr, s1);
+
+        AClass foo1 = s1.classes.get("p1.Foo");
+        assertNotNull("Didn't find foo1", foo1);
+        AClass foo = (AClass) foo1;
+        boolean sawConstructor = false;
+        for (Map.Entry<String, AMethod> me : foo.methods.entrySet()) {
+            if (me.getKey().equals("<init>(Ljava/util/Set;)V")) {
+                assertFalse(sawConstructor);
+                AMethod constructor = me.getValue();
+                assertNotNull(constructor);
+                checkConstructor((AMethod) constructor);
+                sawConstructor = true;
+            }
+        }
+        assertTrue(sawConstructor);
+    }
+
+    class TestDefCollector extends DefCollector {
+        AnnotationDef a, b, c, d, e;
+
+        AnnotationDef f;
+
+        public TestDefCollector(AScene s) throws DefException {
+            super(s);
+        }
+
+        @Override
+        protected void visitAnnotationDef(AnnotationDef tldef) {
+            if (tldef.name.equals("p2.A")) {
+                assertNull(a);
+                a = tldef;
+            } else if (tldef.name.equals("p2.B")) {
+                assertNull(b);
+                b = tldef;
+            } else if (tldef.name.equals("p2.C")) {
+                assertNull(c);
+                c = tldef;
+            } else if (tldef.name.equals("p2.D")) {
+                assertNull(d);
+                d = tldef;
+            } else if (tldef.name.equals("p2.E")) {
+                assertNotNull(f); // should give us fields first
+                assertNull(e);
+                e = tldef;
+            } else if (tldef.name.equals("p2.F")) {
+                f = tldef;
+            } else {
+                fail();
+            }
+        }
+    }
+
+    public void testParseRetrieveTypes() throws Exception {
+        LineNumberReader fr = openPackagedIndexFile("test1.jaif");
+        AScene s1 = newScene();
+        IndexFileParser.parse(fr, s1);
+
+        TestDefCollector tdc = new TestDefCollector(s1);
+        tdc.visit();
+        assertNotNull(tdc.a);
+        assertNotNull(tdc.b);
+        assertNotNull(tdc.c);
+        assertNotNull(tdc.d);
+        assertNotNull(tdc.e);
+        assertNotNull(tdc.f);
+
+        // now look at p2.E because it has some rather complex types
+        AnnotationDef tle = (AnnotationDef) tdc.e;
+        assertEquals(RetentionPolicy.CLASS, tle.retention());
+        AnnotationDef e = tle;
+        assertEquals(new ArrayAFT(new AnnotationAFT(tdc.a)), e.fieldTypes
+                .get("first"));
+        assertEquals(new AnnotationAFT(tdc.f), e.fieldTypes.get("second"));
+        assertEquals(new EnumAFT("Foo"), e.fieldTypes.get("third"));
+        assertEquals(
+                ClassTokenAFT.ctaft,
+                e.fieldTypes.get("fourth"));
+        assertEquals(ClassTokenAFT.ctaft, e.fieldTypes.get("fifth"));
+
+        AnnotationDef tla = (AnnotationDef) tdc.a;
+        assertEquals(RetentionPolicy.RUNTIME, tla.retention());
+        AnnotationDef a = tla;
+        assertEquals(BasicAFT.forType(int.class), a.fieldTypes.get("value"));
+        AnnotationDef d = tdc.d;
+        assertEquals(BasicAFT.forType(String.class), d.fieldTypes.get("value"));
+    }
+
+    public void testParseRetrieveValues() throws Exception {
+        LineNumberReader fr = openPackagedIndexFile("test1.jaif");
+        AScene s1 = newScene();
+        IndexFileParser.parse(fr, s1);
+
+        // now look at Bar because it has some rather complex values
+        Annotation a = s1.classes.get("p1.Bar").lookup("p2.E");
+
+        assertEquals("fooconstant", a.fieldValues.get("third"));
+        assertEquals("interface java.util.Map", a.fieldValues.get("fourth").toString());
+        assertEquals("class [[I", a.fieldValues.get("fifth").toString());
+
+        List<?> first =
+                (List<?>) a.fieldValues.get("first");
+        assertEquals(2, first.size(), 2);
+        Annotation aa = (Annotation) first.get(0);
+        assertEquals("p2.A", aa.def().name);
+        assertEquals(-1, aa.fieldValues.get("value"));
+
+        Annotation a2 =
+                s1.classes.get("p1.Baz").lookup("p2.E");
+        assertEquals("FOO_FOO", a2.fieldValues.get("third"));
+        assertEquals("class java.util.LinkedHashMap", a2.fieldValues.get("fourth").toString());
+        assertEquals("void", a2.fieldValues.get("fifth").toString());
+    }
+
+    void doRewriteTest(LineNumberReader r) throws Exception {
+        AScene s1 = newScene(), s2 = newScene();
+        IndexFileParser.parse(r, s1);
+        StringWriter sbw = new StringWriter();
+        IndexFileWriter.write(s1, sbw);
+        IndexFileParser.parseString(sbw.toString(), s2);
+        assertEquals(s1, s2);
+    }
+
+    public void testRewriteOne() throws Exception {
+        LineNumberReader fr = openPackagedIndexFile("test1.jaif");
+        doRewriteTest(fr);
+    }
+
+    public void testRewriteTwo() throws Exception {
+        LineNumberReader fr = openPackagedIndexFile("test2.jaif");
+        doRewriteTest(fr);
+    }
+
+    public void testConflictedDefinition() throws Exception {
+        AScene s1 = newScene();
+        s1.classes.vivify("Foo").tlAnnotationsHere
+                .add(createEmptyAnnotation(ready));
+        s1.classes.vivify("Bar").tlAnnotationsHere
+                .add(createEmptyAnnotation(readyClassRetention));
+        StringWriter sbw = new StringWriter();
+        try {
+            IndexFileWriter.write(s1, sbw);
+            fail("an exception should have been thrown");
+        } catch (DefException de) {
+            assertEquals("Ready", de.annotationType);
+            // success
+        }
+
+    }
+
+    public void testParseErrorMissingColon() throws Exception {
+        AScene s1 = newScene();
+        String fileContents = "package p1:\n" + "annotation @A:\n"
+                              + "class Foo @A";
+        try {
+            IndexFileParser.parseString(fileContents, s1);
+            fail(); // an exception should have been thrown
+        } catch (FileIOException e) {
+            // TODO:  check line number
+            // assertEquals(3, e.line);
+            // success
+        }
+    }
+
+    public void testParseErrorMissingDefinition() throws Exception {
+        AScene s1 = newScene();
+        String fileContents
+          = "package p1:\n" + "annotation @AIsDefined:\n"
+          + "class Foo:\n" + "@AIsDefined\n" + "@BIsNotDefined\n";
+        try {
+            IndexFileParser.parseString(fileContents, s1);
+            fail(); // an exception should have been thrown
+        } catch (FileIOException e) {
+            // TODO: check line number
+            // assertEquals(5, e.line);
+            // success
+        }
+    }
+
+    private static Annotation getAnnotation(Set<Annotation> annos, String name) {
+        for (Annotation anno : annos) {
+            if (anno.def.name.equals(name)) {
+                return anno;
+            }
+        }
+        return null;
+    }
+
+    public void testEmptyArrayHack() throws Exception {
+        AScene scene = newScene();
+        AClass clazz =
+            scene.classes.vivify("bar.Test");
+
+        // One annotation with an empty array of unknown type...
+        AnnotationBuilder ab1 =
+          AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass);
+        ab1.addEmptyArrayField("array");
+        Annotation a1 = ab1.finish();
+        Annotation tla1 = a1;
+
+        // ... and another with an empty array of known type
+        AnnotationBuilder ab2 =
+          AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass);
+        ArrayBuilder ab2ab = ab2.beginArrayField("array",
+                new ArrayAFT(BasicAFT.forType(int.class)));
+        ab2ab.finish();
+        Annotation a2 = ab2.finish();
+        Annotation tla2 = a2;
+
+        // And they're both fields of another annotation to make sure that
+        // unification works recursively.
+        AnnotationBuilder ab3 =
+          AnnotationFactory.saf.beginAnnotation("foo.CombinedAnno", Annotations.asRetentionRuntime);
+        ab3.addScalarField("fieldOne", new AnnotationAFT(a1.def()), a1);
+        ab3.addScalarField("fieldTwo", new AnnotationAFT(a2.def()), a2);
+        Annotation a3 = ab3.finish();
+        Annotation tla3 = a3;
+
+        clazz.tlAnnotationsHere.add(tla3);
+
+        StringWriter sw = new StringWriter();
+        IndexFileWriter.write(scene, sw);
+
+        AScene sceneRead = newScene();
+        IndexFileParser.parseString(sw.toString(), sceneRead);
+
+        // the anomaly: see second "consequence" on IndexFileWriter#write
+        assertFalse(scene.equals(sceneRead));
+
+        AClass clazz2 = sceneRead.classes.get("bar.Test");
+        assertNotNull(clazz2);
+        Annotation a3_2 = getAnnotation(clazz2.tlAnnotationsHere, "foo.CombinedAnno");
+        Annotation a1_2 = (Annotation) a3_2.getFieldValue("fieldOne");
+        Annotation a2_2 = (Annotation) a3_2.getFieldValue("fieldTwo");
+        // now that the defs were merged, the annotations should be equal
+        assertEquals(a1_2, a2_2);
+
+        // Yet another annotation with an array of a different known type
+        AnnotationBuilder ab4 =
+          AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass);
+        ArrayBuilder ab4ab = ab4.beginArrayField("array",
+                new ArrayAFT(BasicAFT.forType(double.class)));
+        ab4ab.appendElement(5.0);
+        ab4ab.finish();
+        Annotation a4 = ab4.finish();
+        Annotation tla4 = a4;
+
+        // try combining unifiable _top-level_ annotations
+        AScene secondScene = newScene();
+        AClass secondSceneClazz =
+            secondScene.classes.vivify("bar.Test");
+        secondSceneClazz.tlAnnotationsHere.add(tla1);
+        // Oops--the keyed set gives us an exception if we try to put two
+        // different foo.ArrayAnnos on the same class!
+        AClass secondSceneClazz2 =
+            secondScene.classes.vivify("bar.Test2");
+        secondSceneClazz2.tlAnnotationsHere.add(tla4);
+
+        // it should be legal to write this
+        StringWriter secondSW = new StringWriter();
+        IndexFileWriter.write(secondScene, secondSW);
+
+        // add an incompatible annotation
+        AClass secondSceneClazz3 =
+            secondScene.classes.vivify("bar.Test3");
+        secondSceneClazz3.tlAnnotationsHere.add(tla2);
+
+        // now we should get a DefException
+        StringWriter secondSceneSW2 = new StringWriter();
+        try {
+            IndexFileWriter.write(secondScene, secondSceneSW2);
+            // we should have gotten an exception
+            fail();
+        } catch (DefException de) {
+            assertEquals("Conflicting definition of annotation type foo.ArrayAnno",
+                    de.getMessage());
+            // success
+        }
+    }
+
+    public void testEmptyArrayIO() throws Exception {
+        // should succeed
+        String index1 = "package: annotation @Foo: @Retention(CLASS)\n  unknown[] arr\n" +
+            "class Bar: @Foo(arr={})";
+        AScene scene1 = newScene();
+        IndexFileParser.parseString(index1, scene1);
+
+        // should reject nonempty array
+        String index2 = "package: annotation @Foo:  @Retention(CLASS)\n unknown[] arr\n" +
+            "class Bar: @Foo(arr={1})";
+        AScene scene2 = newScene();
+        try {
+            IndexFileParser.parseString(index2, scene2);
+            // should have gotten an exception
+            fail();
+        } catch (FileIOException e) {
+            // success
+        }
+
+        // construct a scene programmatically
+        AScene scene3 = newScene();
+        AClass clazz3 =
+            scene3.classes.vivify("Bar");
+        AnnotationBuilder ab =
+          AnnotationFactory.saf.beginAnnotation("Foo", Annotations.asRetentionClass);
+        ab.addEmptyArrayField("arr");
+        Annotation a = ab.finish();
+        Annotation tla = a;
+        clazz3.tlAnnotationsHere.add(tla);
+
+        assertEquals(scene1, scene3);
+
+        // when we write the scene out, the index file should contain the
+        // special unknown[] field type
+        StringWriter sw3 = new StringWriter();
+        IndexFileWriter.write(scene3, sw3);
+        String index3 = sw3.toString();
+        assertTrue(index3.indexOf("unknown[]") >= 0);
+
+        // can we read it back in and get the same thing?
+        AScene scene4 = newScene();
+        IndexFileParser.parseString(index3, scene4);
+        assertEquals(scene3, scene4);
+    }
+
+    public void testPrune() {
+        AScene s1 = newScene(), s2 = newScene();
+        assertTrue(s1.equals(s2));
+
+        s1.classes.vivify("Foo");
+        assertFalse(s1.equals(s2));
+
+        assertTrue(s1.prune());
+        assertTrue(s1.equals(s2));
+
+        Annotation sa = AnnotationFactory.saf.beginAnnotation("Anno", Annotations.asRetentionClass).finish();
+        Annotation tla = sa;
+
+        AClass clazz2 = s2.classes.vivify("Bar");
+        clazz2.tlAnnotationsHere.add(tla);
+
+        assertFalse(s1.equals(s2));
+        assertFalse(s2.prune());
+        assertFalse(s1.equals(s2));
+    }
+
+    static class MyTAST {
+        final int id;
+        MyTAST(int id) {
+            this.id = id;
+        }
+
+        MyTAST element = null;
+        MyTAST[] typeArgs = null;
+
+        static MyTAST arrayOf(int id, MyTAST element) {
+            MyTAST t = new MyTAST(id);
+            t.element = element;
+            return t;
+        }
+
+        static MyTAST parameterization(int id, MyTAST... args) {
+            MyTAST t = new MyTAST(id);
+            t.typeArgs = args;
+            return t;
+        }
+    }
+
+    /*
+    private static final AnnotationDef idAnnoDef =
+        new AnnotationDef("IdAnno", null, Collections.singletonMap(
+                "id", BasicAFT.forType(int.class)));
+    */
+    private static final AnnotationDef idAnnoTLDef =
+      new AnnotationDef("IdAnno", Annotations.asRetentionClass,
+                          Collections.singletonMap(
+                "id", BasicAFT.forType(int.class)));
+
+    static Annotation makeTLIdAnno(int id) {
+        return new Annotation(idAnnoTLDef,
+                              Collections.singletonMap("id", Integer.valueOf(id)));
+    }
+
+    static class MyTASTMapper extends TypeASTMapper<MyTAST> {
+        boolean[] saw = new boolean[11];
+
+        @Override
+        protected MyTAST getElementType(MyTAST n) {
+            return n.element;
+        }
+
+        @Override
+        protected MyTAST getTypeArgument(MyTAST n, int index) {
+            return n.typeArgs[index];
+        }
+
+        @Override
+        protected int numTypeArguments(MyTAST n) {
+            return n.typeArgs == null ? 0 : n.typeArgs.length;
+        }
+
+        @Override
+        protected void map(MyTAST n, ATypeElement e) {
+            int nodeID = n.id;
+            if (nodeID == 10) {
+                assertTrue(e.lookup("IdAnno") == null);
+                e.tlAnnotationsHere.add(makeTLIdAnno(10));
+            } else {
+                int annoID = (Integer) e.lookup("IdAnno").getFieldValue("id");
+                assertEquals(nodeID, annoID);
+            }
+            assertFalse(saw[nodeID]);
+            saw[nodeID] = true;
+        }
+    }
+
+    private void assignId(ATypeElement myField, int id,
+            Integer... ls) {
+        AElement el = myField.innerTypes.vivify(
+                new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Arrays.asList(ls))));
+        el.tlAnnotationsHere.add(makeTLIdAnno(id));
+    }
+
+    public void testTypeASTMapper() {
+        // Construct a TAST for the type structure:
+        // 0< 3<4>[1][2], 5<6, 8[7], 9, 10> >
+        MyTAST tast = MyTAST.parameterization(0,
+                MyTAST.arrayOf(1,
+                        MyTAST.arrayOf(2,
+                                MyTAST.parameterization(3,
+                                        new MyTAST(4)
+                                )
+                        )
+                ),
+                MyTAST.parameterization(5,
+                        new MyTAST(6),
+                        MyTAST.arrayOf(7,
+                                new MyTAST(8)),
+                        new MyTAST(9),
+                        new MyTAST(10)
+                )
+        );
+
+        // Pretend myField represents a field of the type represented by tast.
+        // We have to do this because clients are no longer allowed to create
+        // AElements directly; instead, they must vivify.
+        AElement myAField =
+            new AScene().classes.vivify("someclass").fields.vivify("somefield");
+        ATypeElement myAFieldType = myAField.type;
+        // load it with annotations we can check against IDs
+        myAFieldType.tlAnnotationsHere.add(makeTLIdAnno(0));
+
+        final int ARRAY = TypePathEntryKind.ARRAY.tag;
+        final int TYPE_ARGUMENT = TypePathEntryKind.TYPE_ARGUMENT.tag;
+
+        assignId(myAFieldType, 1, TYPE_ARGUMENT, 0);
+        assignId(myAFieldType, 2, TYPE_ARGUMENT, 0, ARRAY, 0);
+        assignId(myAFieldType, 3, TYPE_ARGUMENT, 0, ARRAY, 0, ARRAY, 0);
+        assignId(myAFieldType, 4, TYPE_ARGUMENT, 0, ARRAY, 0, ARRAY, 0, TYPE_ARGUMENT, 0);
+        assignId(myAFieldType, 5, TYPE_ARGUMENT, 1);
+        assignId(myAFieldType, 6, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 0);
+        assignId(myAFieldType, 7, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 1);
+        assignId(myAFieldType, 8, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 1, ARRAY, 0);
+        assignId(myAFieldType, 9, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 2);
+        // to test vivification, we don't assign 10
+
+        // now visit and make sure the ID numbers match up
+        MyTASTMapper mapper = new MyTASTMapper();
+        mapper.traverse(tast, myAFieldType);
+
+        for (int i = 0; i < 11; i++) {
+            assertTrue(mapper.saw[i]);
+        }
+        // make sure it vivified #10 and our annotation stuck
+        AElement e10 = myAFieldType.innerTypes.get(
+                new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Arrays.asList(TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 3))));
+        assertNotNull(e10);
+        int e10aid = (Integer) e10.lookup("IdAnno").getFieldValue("id");
+        assertEquals(e10aid, 10);
+    }
+}
diff --git a/scene-lib/test/annotations/tests/executable/example-input.jaif b/scene-lib/test/annotations/tests/executable/example-input.jaif
new file mode 100644
index 0000000..2a824fe
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/example-input.jaif
@@ -0,0 +1,11 @@
+package:
+annotation @Tainted: @Retention(RUNTIME)
+annotation @NonNull: @Retention(RUNTIME)
+
+package foo:
+class Bar:
+  method getSomething(Ljava/lang/StringBuffer;)V:
+    receiver: @Tainted
+  method setSomething(Ljava/lang/String;)V:
+    parameter #0: @NonNull
+  method <init>(Ljava/lang/String;)V:
diff --git a/scene-lib/test/annotations/tests/executable/example-output.jaif.goal b/scene-lib/test/annotations/tests/executable/example-output.jaif.goal
new file mode 100644
index 0000000..7b3c6bb
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/example-output.jaif.goal
@@ -0,0 +1,23 @@
+package :
+annotation @Tainted: @java.lang.annotation.Retention(value=RUNTIME)
+
+package org.checkerframework.checker.nullness.qual:
+annotation @NonNull: @java.lang.annotation.Retention(value=RUNTIME) @java.lang.annotation.Target(value={TYPE_USE})
+
+package :
+annotation @NonNull: @java.lang.annotation.Retention(value=RUNTIME)
+
+package foo:
+class Bar:
+
+    method getSomething(Ljava/lang/StringBuffer;)V:
+        return:
+        receiver: @Tainted
+        parameter #0:
+            type: @org.checkerframework.checker.nullness.qual.NonNull
+
+    method setSomething(Ljava/lang/String;)V:
+        return:
+        parameter #0: @NonNull
+            type: @org.checkerframework.checker.nullness.qual.NonNull
+
diff --git a/scene-lib/test/annotations/tests/executable/example-stdout.jaif.goal b/scene-lib/test/annotations/tests/executable/example-stdout.jaif.goal
new file mode 100644
index 0000000..768d48e
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/example-stdout.jaif.goal
@@ -0,0 +1,6 @@
+Processing class foo.Bar
+Method getSomething(Ljava/lang/StringBuffer;)V must not modify the receiver
+Annotating type of first parameter of getSomething(Ljava/lang/StringBuffer;)V nonnull
+Method setSomething(Ljava/lang/String;)V might modify the receiver
+Annotating type of first parameter of setSomething(Ljava/lang/String;)V nonnull
+Success.
diff --git a/scene-lib/test/annotations/tests/executable/test1.jaif b/scene-lib/test/annotations/tests/executable/test1.jaif
new file mode 100644
index 0000000..1b33e64
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/test1.jaif
@@ -0,0 +1,79 @@
+package p2:
+annotation @A: @Retention(RUNTIME)
+    int value
+annotation @B: @Retention(RUNTIME)
+annotation @C: @Retention(RUNTIME)
+annotation @D: @Retention(CLASS)
+    String value
+annotation @F:
+    String value
+annotation @E: @Retention(CLASS)
+    annotation-field p2.A[] first
+    annotation-field p2.F second
+    enum Foo third
+    Class fourth // <? extends java.util.Map<?,?>>
+    Class fifth
+
+package p1:
+class Foo: @p2.A(value=12)
+    bound 0 &1: @p2.B
+    bound 1 &0: @p2.C
+        inner-type 1, 0, 2, 0, 3, 1: @p2.B
+
+    field bar:
+
+    field baz:
+        type: @p2.B
+            inner-type 0, 0: @p2.C
+
+    method <init>(Ljava/util/Set;)V: @p2.B
+        bound 2 &0: @p2.C
+            inner-type 0, 0: @p2.B
+        parameter #0: @p2.B
+            type:
+                inner-type 0, 0: @p2.C
+        receiver: @p2.D(value="spam")
+        local 1 #3+5: @p2.B
+            type:
+                inner-type 0, 0: @p2.C
+        typecast #7: @p2.B
+            inner-type 0, 0: @p2.C
+        instanceof #199: @p2.C
+            inner-type 1, 0: @p2.B
+        new #0:
+            inner-type 0, 0: @p2.C
+
+    method mapAllElements(Ljava/util/Map;)Ljava/util/Set;:
+
+class
+Bar
+:
+@
+p2.E
+(
+first
+=
+{
+@
+p2.A
+(
+value
+=
+-1
+)
+,
+@
+p2.A
+(
+value=-6
+),
+}
+,second=@p2.F(value="thestring"),
+third=fooconstant,
+fourth=java.util.Map.class,
+fifth=[[I.class
+)
+
+// Test comment
+// Test all the forms of class tokens
+class Baz: @p2.E(first={}, second=@p2.F(value="hello"), third=FOO_FOO, fourth=java.util.LinkedHashMap.class, fifth=void.class)
diff --git a/scene-lib/test/annotations/tests/executable/test2.jaif b/scene-lib/test/annotations/tests/executable/test2.jaif
new file mode 100644
index 0000000..c0808e8
--- /dev/null
+++ b/scene-lib/test/annotations/tests/executable/test2.jaif
@@ -0,0 +1,27 @@
+package p2:
+annotation @A: @Retention(RUNTIME)
+    int value
+
+package p2:
+annotation @B: @Retention(CLASS)
+
+package p2:
+annotation @C: @Retention(RUNTIME)
+
+package p2:
+annotation @D:
+    String value
+
+package p1:
+class Foo: @p2.A(value=12)
+
+package p1:
+class Foo:
+    field baz: @p2.B
+
+package p1:
+class Foo:
+    field baz:
+        type:
+            inner-type 0, 0: @p2.C
+
diff --git a/user.build.properties b/user.build.properties
new file mode 100644
index 0000000..bb853eb
--- /dev/null
+++ b/user.build.properties
@@ -0,0 +1,14 @@
+# Annotation File Utilities project
+# Global properties file that contains absolute paths to all required
+# third-party libraries.
+
+# This file must contain absolute paths for each library.
+# Environment variables can be used, but must be enclosed in brackets
+# and prefixed with 'env.' as such: ${env.PATH}
+
+# The following are required external libraries.
+jre1.6.0 : ${env.JAVA_HOME}/jre/lib/rt.jar
+
+# The JUnit jar file can be downloaded from the JUnit website at:
+# http://www.junit.org/index.htm
+junit : ${scene-lib}/junit.jar